0%

BottomNavigatorView与VIewPager2联动

早在2019年11月20日,ViewPager2就发布了正式版本。今天来使用ViewPager2BottomNavigationView完成一个首页Demo.

准备依赖

1
2
implementation 'com.google.android.material:material:1.1.0'
implementation "androidx.viewpager2:viewpager2:1.0.0"

开始干活

布局

先来完成主页面的布局,上端是一个ViewPager2,底部是一个BottomNavigationView。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">

<androidx.viewpager2.widget.ViewPager2
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toTopOf="@+id/bottomNavigationView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottomNavigationView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#D1D1D1"
app:itemBackground="@null"
app:itemTextColor="@drawable/bottom_navigation_selector"
app:labelVisibilityMode="labeled"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:menu="@menu/bottom_menu" />

</androidx.constraintlayout.widget.ConstraintLayout>

BottomNavigationView

1
2
3
4
5
6
7
8
9
10
11
12
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottomNavigationView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#D1D1D1"
app:itemBackground="@null"
app:itemTextColor="@drawable/bottom_navigation_selector"
app:labelVisibilityMode="labeled"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:menu="@menu/bottom_menu" />
  • app:itemBackground="@null" 设置为@null是为了禁止点击时的水波纹效果。
  • app:labelVisibilityMode="labeled" 设置为labeled是为了让底部的图标和文字始终都可以显示出来。
  • app:itemTextColor="@drawable/bottom_navigation_selector" 设置底部按钮被选中时的文字颜色,这里使用选择器来设置
  • app:menu="@menu/bottom_menu" 使用菜单配置底部按钮

首先设置底部按钮被选中时的文字颜色,bottom_navigation_selector选择器如下。

1
2
3
4
5
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_checked="true" android:color="@color/tab_checked"/>
<item android:state_checked="false" android:color="@color/tab_unchecked"/>
</selector>

接下来新建一个menu.xml文件,完成menu。由于底部按钮也需要根据点击而变换,所以在设置icon的时候,也使用了选择器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/menu1"
android:icon="@drawable/bottom_navigation_icon_selector1"
android:title="菜单1" />
<item
android:id="@+id/menu2"
android:icon="@drawable/bottom_navigation_icon_selector2"
android:title="菜单2" />
<item
android:id="@+id/menu3"
android:icon="@drawable/bottom_navigation_icon_selector3"
android:title="菜单3" />
<item
android:id="@+id/menu4"
android:icon="@drawable/bottom_navigation_icon_selector4"
android:title="菜单4" />
</menu>
1
2
3
4
5
6
7
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 选中时图片-->
<item android:drawable="@drawable/ic_auto_checked" android:state_checked="true" />
<!-- 未选中时 图片 -->
<item android:drawable="@drawable/ic_auto" android:state_checkable="false" />
</selector>

只是这样仍不能在点击的时候切换图片,还需要在代码中进行设置。

1
2
// 取消图标默认颜色切换
bottomNavigationView.itemIconTintList = null

现在BottomNavigationView就基本完成了,但是仔细看的话,会发现在选中某一个按钮时,对应的字体会从12sp变大到14sp。 为了让字体大小不发生变化,可以重写values中的属性,新建一个values.xml, 重写属性。

1
2
3
4
5
6
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- 重写material属性 禁止字体放大动画-->
<dimen name="design_bottom_navigation_active_text_size">12sp</dimen>
<dimen name="design_bottom_navigation_text_size">12sp</dimen>
</resources>

ViewPager2

ViewPager2的使用方法就比BottomNavigationView简单的多了。当ViewPager2中存放的是Fragment的时候,适配器需要继承FragmentStateAdapter

1
2
3
4
5
6
7
8
9
10
11
class MyFragmentPagerAdapter(fragmentActivity: FragmentActivity, private val mFragments: List<Fragment>
) : FragmentStateAdapter(fragmentActivity) {
override fun createFragment(position: Int): Fragment {
return mFragments[position]
}

override fun getItemCount(): Int {
return mFragments.size
}

}

然后给ViewPager2设置适配器。

1
2
3
4
5
6
val mFragments = ArrayList<Fragment>()
mFragments.add(BlankFragment1())
mFragments.add(BlankFragment2())
mFragments.add(BlankFragment3())
mFragments.add(BlankFragment4())
viewpager.adapter = MyFragmentPagerAdapter(this, mFragments)

BottomNavigationView和ViewPager2联动

目前BottomNavigationViewViewPager2都可以单独使用,但是正常的项目中,需要在滑动ViewPager的时候触发BottomNavigationView的菜单切换,在点击切换菜单的时候ViewPager2能够自动切换Fragment。为了实现这个功能,需要分别设置监听操作。

点击BottomNavigationView的时候,自动切换Fragment。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
bottomNavigationView.setOnNavigationItemSelectedListener {
when (it.itemId) {
R.id.menu1 -> {
viewpager.currentItem = 0
}
R.id.menu2 -> {
viewpager.currentItem = 1
}
R.id.menu3 -> {
viewpager.currentItem = 2
}
R.id.menu4 -> {
viewpager.currentItem = 3
}
}
return@setOnNavigationItemSelectedListener false
}

滑动切换Fragment的时候,自动切换底部菜单

ViewPager2修改的监听Selected的方法,改为了registerOnPageChangeCallbackunregisterOnPageChangeCallback。那么就需要实现一个MyPageChangeCallback去继承抽象类ViewPager2.OnPageChangeCallback

1
2
3
4
5
6
7
8
class MyPageChangeCallback(private val bottomNavigationView: BottomNavigationView) :
ViewPager2.OnPageChangeCallback() {
override fun onPageSelected(position: Int) {
super.onPageSelected(position)
Log.d(TAG, "$position")
bottomNavigationView.menu.getItem(position).isChecked = true
}
}

然后在对应的生命周期中去注册和取消监听。

1
2
3
4
5
6
7
8
9
10
11
override fun onCreate(savedInstanceState: Bundle?) {
···
myPageChangeCallback = MyPageChangeCallback(bottomNavigationView)
viewpager.registerOnPageChangeCallback(myPageChangeCallback)
···
}

override fun onDestroy() {
super.onDestroy()
viewpager.unregisterOnPageChangeCallback(myPageChangeCallback)
}