Android BottomNavigationView 全部配置
直接给出标准配置
布局中
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/navView"
style="@style/BottomNavigationItemViewStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:menu="@menu/menu_main_nav" />
styles.xml
已经包含全部属性配置
<style name="BottomNavigationItemViewStyle">
<!-- 按下时水波纹效果 , 去除水波纹 @null -->
<item name="itemRippleColor">@null</item>
<!-- 在“选定”标签可见性模式下,项目是否水平平移 -->
<item name="itemHorizontalTranslationEnabled">false</item>
<!-- 显示标签模式,四种: auto selected labeled unlabeled-->
<item name="labelVisibilityMode">labeled</item>
<!-- 设置背景: itemBackground 会覆盖 android:background , 任选其一 -->
<!-- <item name="android:background">?android:attr/windowBackground</item>-->
<!-- <item name="itemBackground">?android:attr/windowBackground</item>-->
<item name="itemBackground">@color/white</item>
<!-- 图标和文字的颜色样式 + 图标大小,用选择器写控制 state_checked 属性即可 -->
<item name="itemIconSize">@dimen/dp_20</item>
<item name="itemIconTint">@drawable/sel_main_nav</item>
<item name="itemTextColor">@drawable/sel_main_nav</item>
<item name="itemIconPadding">@dimen/dp_20</item>
<!-- 控制选中和未选中时的字体大小, 系统默认一个12sp一个14sp产生有字体缩放的效果,给一个大小就好了 -->
<!-- 代码方式需要用到反射,不推荐 http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2017/0428/7888.html -->
<item name="itemTextAppearanceInactive">@style/BottomNavigationItemViewText</item>
<item name="itemTextAppearanceActive">@style/BottomNavigationItemViewText</item>
<!-- 阴影效果: https://developer.android.com/training/material/shadows-clipping -->
<item name="elevation">@dimen/dp_5</item>
<!-- 如果定义了不同图片进行切换,而不只是简单的改变颜色, 需要代码中设置: BottomNavigationItemView.itemIconTintList = null -->
</style>
<style name="BottomNavigationItemViewText">
<item name="android:textSize">@dimen/font_12</item>
</style>
sel_main_nav.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@color/color_main_theme" android:state_checked="true" />
<item android:color="@color/color_font_black" android:state_checked="false" />
</selector>
效果
源码中所有属性声明
<declare-styleable name="BottomNavigationView">
<!-- Background tint for the BottomNavigationView. -->
<attr name="backgroundTint"/>
<!-- The menu resource to inflate and populate items from. Attribute type definition is in
navigation package. -->
<attr name="menu"/>
<!-- Whether navigation items display with a label, without a label, or with a label during
selected state. Can also be "auto", which uses the item count to determine whether to show
or hide the label. -->
<attr name="labelVisibilityMode">
<!-- Label behaves as "labeled" when there are 3 items or less, or "selected" when there are
4 items or more. -->
<enum name="auto" value="-1"/>
<!-- Label is shown on the selected navigation item. -->
<enum name="selected" value="0"/>
<!-- Label is shown on all navigation items. -->
<enum name="labeled" value="1"/>
<!-- Label is not shown on any navigation items. -->
<enum name="unlabeled" value="2"/>
</attr>
<!-- The background for the navigation items. Attribute type definition is in navigation
package. -->
<attr name="itemBackground"/>
<!-- The ColorStateList to use for a ripple background. This only exists because creating
ripples in drawable xml based on theme colors is not supported pre-23. This will be ignored
if itemBackground is set.-->
<attr format="color" name="itemRippleColor"/>
<!-- The size to provide for the navigation item icons. -->
<attr name="itemIconSize"/>
<!-- The tint to apply to the navigation item icons. Attribute type definition is in navigation
package. -->
<attr name="itemIconTint"/>
<!-- The text appearance to apply to the inactive navigation item labels. Setting
android:textColor in itemTextAppearanceInactive will take precedence over android:textColor
in itemTextAppearanceActive. Instead, set itemTextColor with a ColorStateList to make
the text color stateful. -->
<attr format="reference" name="itemTextAppearanceInactive"/>
<!-- The text appearance to apply to the active navigation item label. You should not set
android:textColor in itemTextAppearanceActive. Instead, set itemTextColor to a
ColorStateList to make the text color stateful. -->
<attr format="reference" name="itemTextAppearanceActive"/>
<!-- The color to apply to the navigation items' text. Setting itemTextColor will take
precedence over android:textColor in itemTextAppearanceInactive or
itemTextAppearanceActive. Attribute type definition is in navigation package. -->
<attr name="itemTextColor"/>
<!-- Whether the items translate horizontally when in "selected" label visibility mode. -->
<attr format="boolean" name="itemHorizontalTranslationEnabled"/>
<attr name="elevation"/>
</declare-styleable>
取消长按吐司
(BottomNavigationView.getChildAt(0) as? ViewGroup)?
.children?.forEach { it.setOnLongClickListener { true } }
即把每一个BottomNavigationItemView
的长按监听吃掉。但是vivo手机会有长按震动的问题,这个无法屏蔽掉...-_-||
处理双击事件
需要从 BottomNavigationView.setOnNavigationItemSelectedListener
入手
//快速点击事件
val fastClick=object :NoShakeClickListener2(){
override fun onFastClick(item: Any?) {
super.onFastClick(item)
Log.e("123", "onFastClick Click")
//处理双击刷新逻辑...
if(XXXFragment.isNotRefreshing){
balabal...
}
}
}
BottomNavigationView.setOnNavigationItemSelectedListener {
switchPage(it.itemId)
fastClick.proceedClick()
true
}
附NoShakeClickListener2
源码
/**
* 事件防抖
* 注: 不仅适用于 View , 其他控件如: MenuItem 同样适用
*
* 1.既适用于单个`View`事件防抖, 也适用于`Adapter`中`ItemView`事件防抖
* 2.如果事件为跳转到新的`Activity`, 该`Activity`启动模型应为`android:launchMode="singleTop"`
*/
open class NoShakeClickListener2 @JvmOverloads constructor(interval: Long = 500L) {
private var mTimeInterval = 500L
private var mLastClickTime: Long = 0 //最近一次点击的时间
private var mLastClick: Any? = null //最近一次点击的控件 View or MenuItem ...
init {
mTimeInterval = interval
}
fun proceedClick() {
if (isFastClick(null, mTimeInterval)) onFastClick(null) else onSingleClick(null)
}
fun <T> proceedClick(item: T?) {
if (isFastClick(item, mTimeInterval)) onFastClick(item) else onSingleClick(item)
}
/**
* 是否是快速点击
*
* @param item 点击的控件 View or MenuItem ...
* @param interval 时间间期(毫秒)
* @return true:是,false:不是
*/
private fun <T> isFastClick(item: T?, interval: Long): Boolean {
val nowTime = System.currentTimeMillis()
val timeInterval = abs(nowTime - mLastClickTime)
return if (timeInterval < interval && item == mLastClick) {
// 快速点击事件
true
} else {
// 单次点击事件
mLastClickTime = nowTime
mLastClick = item
false
}
}
protected open fun onFastClick(item: Any?) {}
protected open fun onSingleClick(item: Any?) {}
}
此处参考我的另外一篇日志 : Android View 事件防抖
辅助工具类BottomNavController
BottomNavController
既适用于BottomNavigationView
的底部导航方案, 也适用于RadioGroup
+RadioButton
的方案。
class BottomNavController(private val containerId: Int, size: Int = 5) {
private val fragmentArray = SparseArray<Fragment>(size)
private lateinit var navView: BottomNavigationView
private lateinit var fragmentManager: FragmentManager
fun attach(navView: BottomNavigationView?, fragmentManager: FragmentManager?) {
if (navView == null) {
throw RuntimeException("BottomNavigationView can`t be null")
}
if (fragmentManager == null) {
throw RuntimeException("FragmentManager can`t be null")
}
this.navView = navView
this.fragmentManager = fragmentManager
}
fun putFragments(vararg arrayOfPairs: Pair<Int, Fragment>?) {
if (arrayOfPairs.isNullOrEmpty()) return
arrayOfPairs.forEach { p ->
p?.apply {
fragmentArray.put(first, second)
}
}
}
fun switchDefaultPage(pageId: Int) {
fragmentManager
.beginTransaction()
.apply {
fragmentArray.forEach { key, value ->
add(containerId, value, key.toString())
}
}
.commitAllowingStateLoss()
switchPage(pageId)
}
fun switchPage(id: Int) {
fragmentManager
.beginTransaction()
.apply {
fragmentArray.forEach { key, value ->
if (key == id) show(value) else hide(value)
}
}.commitAllowingStateLoss()
}
//Copy from androidx.core.util.SparseArrayKt.forEach
private inline fun <T> SparseArray<T>.forEach(action: (key: Int, value: T) -> Unit) {
for (index in 0 until size()) {
action(keyAt(index), valueAt(index))
}
}
}
🌴 MainActivity
中使用 :
@AndroidEntryPoint
class MainActivity : BaseMvvmActivity<ActivityMainBinding>() {
private lateinit var navView: BottomNavigationView
private lateinit var navViewController: BottomNavController
override fun initView(savedInstanceState: Bundle?) {
prepareLogin()
navView = findViewById(R.id.bottomNavView)
navViewController = BottomNavController(containerId = R.id.main_container, size = 3)
navViewController.attach(navView, supportFragmentManager)
navViewController.putFragments(
R.id.nav_home to HomeFragment(),
R.id.nav_repo to RepoFragment(),
R.id.nav_mine to MineFragment()
)
//屏蔽长按吐司
(navView.getChildAt(0) as? ViewGroup)?.children?.forEach { it.setOnLongClickListener { true } }
//快速点击事件
val fastClick = object : NoShakeClickListener2() {
override fun onFastClick(item: Any?) {
super.onFastClick(item)
(item as? MenuItem?)?.apply {
//val fg = fragmentArray.get(itemId)
//if (fg.isAdded) fg.refreshData()
}
}
}
navView.setOnNavigationItemSelectedListener {
navViewController.switchPage(it.itemId)
fastClick.proceedClick(it)
true
}
}
private fun prepareLogin() {
repo.loginByToken()?.observe(this) { r ->
if (r?.isSuccessful == true) {
r.body?.apply {
...
isLogin = true
//登录成功后选中 HomeFragment 页面
navViewController.switchDefaultPage(R.id.nav_home)
}
} else {
AppRouter.toLogin(this)//未登录 跳转至 注册/登录页
}
}
}
}
activit_main.xml
:
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:orientation="vertical">
<FrameLayout
android:id="@id/main_container"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottomNavView"
style="@style/BottomNavigationItemViewStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:menu="@menu/menu_nav_main" />
</LinearLayout>
本文作者:风之旅人
本文链接:https://www.cnblogs.com/jooy/p/17302052.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。