TrainApp Navigation 实现顶部和、底部导航栏以及导航抽屉

2,788 阅读6分钟

导航体系结构组件包括NavigationUI类,此类包含用于管理顶部应用程序栏,导航抽屉和底部导航的静态方法。

顶部程序应用栏(Top app bar)

顶部程序应用栏能够为我们的应用在顶部提供统一的位置用来展示当前屏幕上的信息和操作,为用户提供便捷的操作入口。

常见的顶部应用栏大概也就是这个样子:

NavigationUI使用导航图中的目标标签来使顶部应用栏的标题保持最新。

NavigationUI支持的应用栏

  • Toolbar

  • CollapsingToolbarLayout

  • ActionBar

创建工具栏

首先我们需要在我们的MainActivity中定义一个工具栏:

 <com.google.android.material.appbar.MaterialToolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toTopOf="parent" />

接下来我们需要设置NavController的工具栏:

        val navController = findNavController(R.id.nav_host)
        val appBarConfiguration = AppBarConfiguration(navController.graph)
        findViewById<MaterialToolbar>(R.id.toolbar)
            .setupWithNavController(navController, appBarConfiguration)

此时运行app: setupWithNavController方法用来设置NavController的工具栏,调用该方法,当导航的目的地发生变化时,工具栏上的标题会自动进行更新。

AppBarConfiguration对象来管理应用程序显示区域左上角的“导航”按钮的行为。默认情况下,当用户位于导航图的顶级目标位置时,“导航”按钮处于隐藏状态,而在其他任何目标位置均显示为“向上”按钮。单击导航图标时,此方法将调用NavController.navigateUp。使用工具栏时,“导航”会自动处理“导航”按钮的单击事件,因此我们无需覆盖onSupportNavigateUp方法,此时在Home Fragment中点击返回键,那么将回退到LaunchFragment。

此时,系统默认提供的工具栏还在,我们在AndroidManifest.xml定义下app的主题样式,取消默认的工具栏。

   android:theme="@style/Theme.AppCompat.Light.NoActionBar">

ok,看起来好多了。

CollapsingToolbarLayout

CollapsingToolbarLayout 主要用于实现一个可折叠的标题栏,一般作为 AppBarLayout 的子 View 来使用。

在MainActivity中使用CollapsingToolbarLayout:

 <com.google.android.material.appbar.AppBarLayout
        android:id="@+id/appbar_layout"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:background="@color/colorPrimary"
        app:layout_constraintTop_toTopOf="parent">

        <com.google.android.material.appbar.CollapsingToolbarLayout
            android:id="@+id/collapsing_toolbar_layout"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:contentScrim="?attr/colorPrimary"
            app:expandedTitleGravity="top"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_scrollFlags="scroll|exitUntilCollapsed|snap">

            <com.google.android.material.appbar.MaterialToolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                app:layout_collapseMode="pin" />
        </com.google.android.material.appbar.CollapsingToolbarLayout>
    </com.google.android.material.appbar.AppBarLayout>

我们简单了解下其基本属性:

//是否显示标题
app:titleEnabled="true"
//标题内容
app:title="CollapsingToolbarLayout"
//扩展后Title的位置
app:expandedTitleGravity="left|bottom"
//收缩后Title的位置
app:collapsedTitleGravity="left"
//CollapsingToolbarLayout收缩后Toolbar的背景颜色
app:contentScrim ="@color/colorPrimary"
//CollapsingToolbarLayout收缩时颜色变化的持续时间
app:scrimAnimationDuration="1200"
//颜色从可见高度的什么位置开始变化
app:scrimVisibleHeightTrigger="150dp"
//状态颜色变化(Android 5.0)
app:statusBarScrim="@color/colorAccent"
//设置滑动组件与手势之间的关系
app:layout_scrollFlags="scroll|exitUntilCollapsed|snap"

同理我们需要使用setupWithNavController方法来设置NavController的工具栏的CollapsingToolbarLayout和toolbar:

        val navController = findNavController(R.id.nav_host)
        val appBarConfiguration = AppBarConfiguration(navController.graph)
        val toolbar = findViewById<MaterialToolbar>(R.id.toolbar)
        val toolbarLayout = findViewById<CollapsingToolbarLayout>(R.id.collapsing_toolbar_layout)
        toolbarLayout.setupWithNavController(toolbar, navController, appBarConfiguration)
androidx.navigation.ui CollapsingToolbarLayoutKt.class public fun CollapsingToolbarLayout.setupWithNavController(
    toolbar: Toolbar,
    navController: NavController,
    configuration: AppBarConfiguration
): Unit

app效果:

导航抽屉(navigation drawer)

导航抽屉一般用来显示app的主导航菜单,当我们从屏幕左边缘开始滑动或者点击导航图标的时候,导航抽屉就会出现。

要想使用导航抽屉,我们需要将DrawerLayout作为我们的根布局:

<androidx.drawerlayout.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <fragment
        android:id="@+id/nav_host"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:defaultNavHost="true"
        app:navGraph="@navigation/navigation" />

    <com.google.android.material.navigation.NavigationView
        android:id="@+id/nav_view"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_gravity="start" />
</androidx.drawerlayout.widget.DrawerLayout>

这里我们需要注意,一般我们会将app主要内容视图作为第一个view,第二个view设置其layout_gravity属性,android会根据这个属性来区别抽屉容器和主要内容视图

此时运行程序:

可以看到我们的抽屉还没有任何内容,但是已经可以正常的显示了。

通过下面的属性指定抽屉对应的header和menu:

这里我们做个简单的演示,效果如下:

使用Navigation另外一个便捷之处就是如果我们为app指定menu项,那么如果菜单项的id和导航文件中定义的组件的id相同,那么通过setupWithNavController方法将view和导航关联后,点击对应的菜单项会自动导航至指定的目的地。

看个简单的例子,首先我们定义菜单:

<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <item
        android:id="@id/launchFragment"
        android:icon="@drawable/ic_home_black"
        android:title="主页"
        app:showAsAction="ifRoom" />

    <item
        android:id="@id/homeFragment"
        android:icon="@drawable/ic_settings_black"
        android:title="引导页面"
        app:showAsAction="ifRoom" />
</menu>

其id和导航文件中对应的fragment相同:

在fragment中使用:

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setHasOptionsMenu(true)

    }

    override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
        inflater.inflate(R.menu.menu_app, menu)
        super.onCreateOptionsMenu(menu, inflater)
    }

接下来我们还有最关键的一个步骤,那就是重写onOptionsItemSelected方法,在其内部调用onNavDestinationSelected方法。

    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        NavigationUI.onNavDestinationSelected(item!!, findNavController())
        return super.onOptionsItemSelected(item)
    }

该方法会尝试将菜单项导航至指定的目的地,默认情况下后堆栈将弹出回到导航图的起始目标位置。如果需要避免这种情况,那么可以使用android:menuCategory属性进行设置。

同样的我们可以使用BottomNavigationView来创建一个底部导航:

    <com.google.android.material.bottomnavigation.BottomNavigationView
        android:id="@+id/bottom_nav"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/color_bottom_view"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintTop_toBottomOf="@id/nav_host"
        app:menu="@menu/menu_bottom" />
 bottom_nav.setupWithNavController(navController)

创建menu_bottom菜单:

<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <item
        android:id="@id/launchFragment"
        android:icon="@drawable/ic_home_black"
        android:title="@string/home"
        app:showAsAction="ifRoom" />

    <item
        android:id="@id/homeFragment"
        android:icon="@drawable/ic_group_work_black"
        android:title="@string/work"
        app:showAsAction="ifRoom" />
    <item
        android:id="@id/homeFragment"
        android:icon="@drawable/ic_supervisor_account_black"
        android:title="@string/address_book"
        app:showAsAction="ifRoom" />

    <item
        android:id="@id/homeFragment"
        android:icon="@drawable/ic_person_black"
        android:title="@string/my"
        app:showAsAction="ifRoom" />
</menu>

同时我们将应用栏和底部导航栏都设置背景色:

    <color name="color_header_bar">#AA87B8</color>
    <color name="color_bottom_view">#AA87B8</color>
    <color name="color_header_drawer">#AA87B8</color>

运行程序:

底部导航栏的菜单项导航至具体的页面可参考上面的,只要指定id即可。

不过呢大家可以看到底部导航栏的图标和文字并没有全部显示出来,只有选中的时候才会有文字,这个和我们经常看到的app有点不一样,使用labelVisibilityMode属性显示图标和文字:

app:labelVisibilityMode="labeled"

将我们app的主题设置为:

Theme.MaterialComponents.Light.NoActionBar

此时app效果如下:

可以看到状态栏的颜色和toolbar的颜色不一致,这里为了偷懒,更多的方式网上有好多的教程。设置最低版本号为21:

然后修改我们的主题样式如下:

<resources xmlns:android="http://schemas.android.com/apk/res/android">

    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <item name="android:statusBarColor">@color/color_header_bar</item>
    </style>
</resources>

将状态栏的颜色和我们的toolbar设置为一个颜色,看看效果:

顺眼多了。。。更多教程请大家查阅沉浸式状态栏的教程。。。

最后我们修改下溢出菜单的弹出位置,这个需要指定toolbar的popupTheme:

 app:popupTheme="@style/popTheme"
 
 
     <style name="popTheme" parent="android:Widget.Material.Light.PopupMenu.Overflow">
        <!--设置不覆盖锚点-->
        <item name="overlapAnchor">false</item>
    </style>

ok,其他的就不多说了。。。不太会搞啊。。有资源的给个链接学习下。