之前介绍过jetpack Navigation的基本使用,本篇文章是Navigation系列的第二篇,主要介绍一下如何将Navigation与BottomNavigationView相结合实现多个fragment切换的过程,以wanAndroid的界面为依据,分三步实现这个过程:
1. 创建底部导航TAB
创建底部导航tab的第一步就是先创建导航菜单,菜单文件的位置位于res-menu目录下,这个菜单定义了每个tab的标题、图标、颜色等属性。
main_bottom_navigation.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/nav_home"
android:icon="@mipmap/icon_home" //图标
android:title="首页" //标题文字
app:showAsAction="ifRoom" />
<item
android:id="@+id/nav_quare"
android:icon="@mipmap/icon_square"
android:title="广场"
app:showAsAction="ifRoom" />
<item
android:id="@+id/nav_pub"
android:icon="@mipmap/icon_pub"
android:title="公众号"
app:showAsAction="ifRoom" />
<item
android:id="@+id/nav_ask"
android:icon="@mipmap/icon_ask"
android:title="问答"
app:showAsAction="ifRoom" />
</menu>
导航菜单创建完成之后还无法在界面上显示,要想在界面上看到显示效果需要引入BottomNavigationView。BottomNavigationView是谷歌官方的用于显示底部导航的控件,需要注意的是,BottomNavigationView目前最大设置5个tab,这对大部分的APP应该是够用了。
引入BottomNavigationView之后需要通过其menu属性与刚刚创建的导航菜单关联。
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/nav_main"
android:layout_width="0dp"
android:layout_height="50dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:menu="@menu/main_bottom_navigation"
android:background="@color/white"
app:flow_wrapMode="none"
app:labelVisibilityMode="labeled"
app:itemBackground="@null"
/>
当item的数量超过三个时默认只有选中项会同时显示文字和图标,其余项只显示图标,labelVisibilityMode=labeled可以打破这个限制。
BottomNavigationView默认每个item点击时都是有水波纹动画的,使用itemBackground=@null可以去掉默认的点击动画。
看一下导航栏创建完成之后的效果:
2. 创建Navigation
导航栏创建完成之后就该创建Navigation了,关于Navigation的详细使用可以参考我的另一篇文章《JetPack系列—Navigation(一)》,这里只做一下简单的介绍。
首先第一步就是创建导航图,导航图的位置在res-navigation目录下,也是一个xml文件。
nav_graph.xml
<?xml version="1.0" encoding="utf-8"?>
<navigation 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:id="@+id/nav_graph">
</navigation>
这是一个空的导航图,接下来创建BottomNavigationView中每个item对应的fragment文件,然后把每个fragment都放到导航图中,并且为导航图指定startDestination起始位置。 nav_graph.xml
<?xml version="1.0" encoding="utf-8"?>
<navigation 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:id="@+id/nav_graph"
app:startDestination="@id/nav_home">
<fragment
android:id="@+id/nav_home"
android:name="com.example.mvvm_wanandroid.home.HomeFragment"
android:label="fragment_home"
tools:layout="@layout/fragment_home" />
<fragment
android:id="@+id/nav_quare"
android:name="com.example.mvvm_wanandroid.square.SquareFragment"
android:label="fragment_square"
tools:layout="@layout/fragment_square" />
<fragment
android:id="@+id/nav_pub"
android:name="com.example.mvvm_wanandroid.pub.PublicFragment"
android:label="fragment_public"
tools:layout="@layout/fragment_public" />
<fragment
android:id="@+id/nav_ask"
android:name="com.example.mvvm_wanandroid.ask.AskFragment"
android:label="fragment_ask"
tools:layout="@layout/fragment_ask" />
</navigation>
完成这几步之后还需要将导航图关联到activity中,一般指的fragment所在的activity。这时候就会用到另外一个控件FragmentContainerView了, FragmentContainerView 位于androidx.fragment.app包下面,是专门为Fragment设计的自定义布局,可以看作是fragment的容器。
在activity中引入fragmentcontainerview:
<androidx.fragment.app.FragmentContainerView
android:id="@+id/nav_host_fragment"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:name="androidx.navigation.fragment.NavHostFragment"
app:defaultNavHost="true"
app:navGraph="@navigation/nav_graph"
app:layout_constraintTop_toBottomOf="@+id/main_toolbar"/>
name属性一定要定义成androidx.navigation.fragment.NavHostFragment,同时需要关联nav_graph导航图。
3. 将Navigation与BNV关联
介绍完了如何创建底部导航栏和navigation之后要想实现最终的效果就要将这两个组件关联到一起。
private var binding:ActivityMainBinding?=null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this,R.layout.activity_main)
val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
val navController = navHostFragment.findNavController()
NavigationUI.setupWithNavController(binding!!.navMain,navController)
这里使用了NavigationUI.setupWithNavController将Navigation与BottomNavigationView关联到一起,当点击导航的每个item时就可以切换fragment了。有一点要注意就是如果我们同时实现了BottomNavigationView的 onNavigationItemSelected方法会导致上面的方式失效,这个时候可以把关联的操作放到onNavigationItemSelected回调方法中执行。
private lateinit var navController:NavController
override fun onNavigationItemSelected(item: MenuItem): Boolean {
NavigationUI.onNavDestinationSelected(item, navController)
return true
}
看一下最后的效果: