JetPack系列—将Navigation与BottomNavigationView结合使用

3,480 阅读2分钟

之前介绍过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可以去掉默认的点击动画。

看一下导航栏创建完成之后的效果:

图片.png

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
}

看一下最后的效果:

图片.png