持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第1天,点击查看活动详情
概述
BottomNavigationView可以用来设置页面的底部菜单栏,一般在底部菜单栏中有3到5个元素的时候推荐使用这个View。使用这个View配合Fragment实现页面的切换效果。同时也可以配合CoordinatorLayout中的部分behavior实现页面滚动的时候隐藏菜单栏,从而能够让屏幕显示更多的内容。
使用
基本使用
要想使用BottomNavigationView十分简单,和普通的View是一样的,我们只需要将其定义在页面的底部,唯一不同的是,我们需要向其指定一个menu标签,menu标签中指定了需要显示的导航item。
- 如下我们首先定义了一个
menu:
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/menu1"
android:title="menu1"
android:icon="@drawable/abc_vector_test" />
<item
android:id="@+id/menu2"
android:title="menu2"
android:icon="@drawable/frank" />
<item
android:id="@+id/menu3"
android:title="menu3"
android:icon="@drawable/ic_icon1" />
</menu>
- 接着我们在页面中使用
BottomNavigationView:
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottom_navigation_view"
android:layout_width="@dimen/dp_0"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:background="@color/color_f54949"
app:menu="@menu/menu_bottom_navigation_view_test1" />
</androidx.constraintlayout.widget.ConstraintLayout>
经过上面简单的两步操作,我们就成功定义了一个BottomNavigationView,下面是这样定义之后的效果:
通过上面的动图可以看到: 我们定义了三个用于导航的item,这个三个导航item可以在点击的时候切换到不同的Fragment(尚未实现),而且我们定义的BottomNavigationView本身就拥有点击和切换的效果,这是组件默认实现的效果。
监听切换事件
使用BottomNavigationView主要就是为了配合Fragment进行页面的导航和切换,因此BottomNavigationView向我们提供了setOnNavigationItemSelectedListener和setOnNavigationItemReselectedListener这两个回调来通知我们当前选中的item,我们可以根据这两个回调来进行不同页面的切换。下面的代码简单演示了如何使用这两个回调:
mBinding.bottomNavigationView.setOnNavigationItemSelectedListener {
Logs.e(TAG,"item select listener:$it")
true
}
mBinding.bottomNavigationView.setOnNavigationItemReselectedListener {
Logs.e(TAG,"item reselect listener:$it")
}
需要注意的是: OnNavigationItemSelectedListener这个接口需要我们返回一个布尔值,这个布尔值表示当前的item是否能够起作用,在上面的代码中我们默认返回true表示任何item都可以起作用,当我们点击BottomNavigationView的某一个item的时候,我们将会看到如下的打印信息:
myStudyProject_system_view.BottomNavigationViewActivity: item reselect listener:menu1
myStudyProject_system_view.BottomNavigationViewActivity: item select listener:menu2
myStudyProject_system_view.BottomNavigationViewActivity: item reselect listener:menu2
myStudyProject_system_view.BottomNavigationViewActivity: item select listener:menu3
myStudyProject_system_view.BottomNavigationViewActivity: item reselect listener:menu3
myStudyProject_system_view.BottomNavigationViewActivity: item select listener:menu1
myStudyProject_system_view.BottomNavigationViewActivity: item reselect listener:menu1
从打印和信息和接口的名称也可以看出这两个回调的区别:
- 如果当前点击的item和上一次点击的item(即已经选中的item)不一样,那么将会回调
OnNavigationItemSelectedListener这个接口 - 如果当前点击和item和上一次点击的item(即已经选中的item)一样,则会回调
OnNavigationItemReselectedListener接口
现在很多应用在点击底部导航栏同一个item的时候会刷新页面的数据,这个功能我们通过OnNavigationItemReselectedListener也可以实现。
下面的代码演示了使用BottomNavigationView配合Fragment做页面切换的操作,点击同一个item会刷新页面数据,点击不同的item会切换Fragment:
- 首先简单定义三个Fragment:
class BottomNavigationViewMenuFragment: Fragment() {
private var mBinding: FragmentBottomNavigationViewMenuBinding? = null
private var mContent: String? = null
private val mHandler = Handler()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
mBinding = FragmentBottomNavigationViewMenuBinding.inflate(inflater,container,false)
mBinding?.let {
if(!TextUtils.isEmpty(mContent)){
it.tvContent.text = mContent
}
}
return mBinding!!.root
}
/**
* 设置内容
* @param content 设置要显示的内容
*/
fun setContent(content: String){
this.mContent = content
mBinding?.let {
it.tvContent.text = mContent
}
}
/**
* 刷新页面数据
*/
fun refreshContent(){
mBinding?.let {
mHandler.removeCallbacksAndMessages(null)
it.tvContent.text = "正在刷新"
mHandler.postDelayed({
it.tvContent.text = mContent
},1000)
}
}
}
- 这个三个
Fragment只是三个不同的实例,我们在Activity中初始化三个Fragment,如下所示:
private val mFragment1 by lazy {
BottomNavigationViewMenuFragment().apply {
this.setContent("menu1")
}
}
private val mFragment2 by lazy {
BottomNavigationViewMenuFragment().apply {
this.setContent("menu2")
}
}
private val mFragment3 by lazy {
BottomNavigationViewMenuFragment().apply {
this.setContent("menu3")
}
}
- 当我们重复点击某一个导航item的时候,
Fragment中会执行刷新的操作,否则执行切换Fragment的操作:
mBinding.bottomNavigationView.setOnNavigationItemSelectedListener {
Logs.e(TAG,"item select listener:$it")
when(it.itemId){
R.id.menu1 ->
switchFragment(mFragment1)
R.id.menu2 ->
switchFragment(mFragment2)
R.id.menu3 ->
switchFragment(mFragment3)
}
true
}
mBinding.bottomNavigationView.setOnNavigationItemReselectedListener {
Logs.e(TAG,"item reselect listener:$it")
if(mCurrentFragment is BottomNavigationViewMenuFragment){
(mCurrentFragment as BottomNavigationViewMenuFragment).refreshContent()
}
}
运行上面的程序可以得到如下的效果:
其它属性
系统为我们默认提供的是基于主题色的属性,很多时候这些属性的设置并不能满足我们的需求,BottomNavigationView也为我们提供了部分字段来实现自定义的需求,如下表所示:
| 属性名 | 属性简介 | 备注 |
|---|---|---|
itemIconSize | 这个属性用于设置icon的大小 | 需要注意的是,并不能通过修改这个属性的值来修改BottomNavigationView的高度,目前来看BottomNavigationView中menu的高度是固定的,并不会随着icon的高度变化而变化,icon设置的较大可能还会导致文本看的不清楚 |
itemRippleColor | 点击item时水波纹的颜色 | 这个属性只有在没有设置itemBackground的时候才有用 |
itemIconTint | 这个属性用于设置图标的颜色 | 这里我们可以通过设置一个ColorStateList来指定在不同状态下图标的颜色,测试发现通过设置的不同状态下的图片在切换的时候可能没有效果 |
itemBackground | 设置导航item的背景颜色 | 这只此项可能会导致点击时的水波纹效果消失 |
itemTextColor | 设置导航栏item文本颜色 | 这里仍然可以通过设置一个ColorStateList来指定不同状态下文本的颜色 |
itemHorizontalTranslationEnabled | 设置当某一个item被选中的时候别的item的情况 | 如果设置为true表示当某一个item被选中缩放的时候其它的item会被横向挤压,如果设置为false表示当某一个item被选中缩放的时候,别的item不会被挤压 |
itemTextAppearanceInactive | 这个属性不推荐使用 | 应该使用itemTextColor并传递ColorStateList设置文本的颜色 |
itemTextAppearanceActive | 这个属性不推荐使用 | 应该使用itemTextColor并传递ColorStateList设置文本的颜色 |
labelVisibilityMode | 设置标签的显示模式 | auto表示根据当前的子项的数量自由决定是否显示标签,selected表示导航项被选中的时候显示标签,unlabeled表示标签任何时候都不会显示,labeled表示标签在任何时候都会显示 |
设置角标
BottomNavigationView允许我们给某一个导航项设置角标,我们可以指定角标的数量和位置,背景颜色等。使用角标我们可以很容易地提醒用户未读的消息数量等信息,这对于那些拥有消息通知功能的应用来说是非常方便的,下面的代码演示如何在某一个导航项上添加角标:
val badge = mBinding.bottomNavigationView.getOrCreateBadge(R.id.menu3)
badge.number = 100
badge.backgroundColor = ContextCompat.getColor(this,R.color.colorPrimaryDark)
badge.badgeTextColor = ContextCompat.getColor(this,R.color.design_default_color_error)
badge.badgeGravity = BadgeDrawable.TOP_END
badge.verticalOffset = 5
badge.horizontalOffset = 5
在上面的代码中,我们向id为menu3的导航项上添加了一个角标,我们设置了角标上显示的数量为100,同时设置了角标的背景颜色和文本颜色,最后设置了角标位于这个导航项的右上角,同时基于右上角我们设置和横向和纵向的偏移量。
运行上面的程序会得到如下的结果: