头图

This is the second MAD Skills series about navigation. If you want to review the content published in the past, please refer to the link below to view:

Today I will release the first article in this series for everyone. In this article, we will explain another use case for you, that is, how UI components such as the Action Bar, bottom tab bar, or navigation drawer can implement navigation functions in an application. If you prefer to watch videos instead of reading articles, please check the video content.

Overview

In the previous navigation series articles , Chet developed an tracking donuts. Do you know what is the best partner for donuts? (Is it another donut?) Of course it is coffee! So I am going to add a function to track coffee. I need to add some pages to the application, so it is necessary to use the navigation drawer or the bottom tab bar to assist the user in navigation. But how do we use these UI components to integrate navigation functions? Do you manually trigger navigation actions by clicking on the listener?

unnecessary! No need for any listeners. The NavigationUI class implements the navigation function between different pages by matching the target page id and the menu id. Let's explore its internal mechanism in depth.

add coffee tracker

△ 工程结构

△ Engineering structure

First, I copied a copy of the class files related to donuts to the new package and renamed them. Such an operation may not be the best practice for real applications, but here can quickly help us add coffee tracking functions to existing applications. If you want to synchronize operations with the content of the article, you can get the code , which contains all the modifications to the Donut Tracker application, and you can learn about NavigationUI based on this code.

Based on the changes made above, I updated the navigation map, adding new pages and operations related to coffeeFragment to coffeeDialogFragment and selectionFragment to donutFragment. I will use the id of these destination pages later ;)

△ 带有新的目的页面的导航图

△ Navigation map with new destination page

After updating the navigation graph, we can start to bind the elements and realize the navigation to the SelectionFragment.

Option menu

The app’s options menu is not yet functional. To enable it, we need to onOptionsItemSelected() function, call for the selected menu item onNavDestinationSelected() function, passing navController . As long as the id MenuItem the id of 0611a453a60210, the function will navigate to the destination page MenuItem

override fun onOptionsItemSelected(item: MenuItem): Boolean {
   return item.onNavDestinationSelected(
       findNavController(R.id.nav_host_fragment)
   ) || super.onOptionsItemSelected(item)
}

Now that the navigation controller can "dominate" the menu items, I MenuItem of id with 0611a453a60249 of the destination page created id . In this way, the navigation component can MenuItem with the destination page.

<menu 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"
   tools:context="com.android.samples.donuttracker.MainActivity">
   <item
       android:id="@+id/selectionFragment"
       android:orderInCategory="100"
       android:title="@string/action_settings"
       app:showAsAction="never" />
</menu>

Toolbar

Now the app can navigate to selectionFragment , but the title remains the same. When it is at selectionFragment , we hope that the title can be updated and the back button is displayed.

First I need to add a AppBarConfiguration object, NavigationUI will use this object to manage the behavior of the navigation button in the upper left corner of the application.

appBarConfiguration = AppBarConfiguration(navController.graph)

The button will change its behavior according to the level of your destination page. For example, when you are on the top-level destination page, the back button will not be displayed because there is no higher-level page.

By default, the initial page of your application is the only top-level destination page, but you can also define multiple top-level destination pages. For example, in our application, I can donutList and coffeeList 1611a453a60310 as the top destination page.

Next, in the MainActivity class, obtain navController and toolbar , and verify setSupportActionBar() is called. Here I also updated the reference of the toolbar passed into the function.

val navHostFragment = supportFragmentManager.findFragmentById(
   R.id.nav_host_fragment
) as NavHostFragment
navController = navHostFragment.navController
val toolbar = binding.toolbar

To add navigation function to the default Action Bar, I used the setupActionBarWithNavController() function here. This function requires two parameters: navController and appBarConfiguration .

setSupportActionBar(toolbar)
setupActionBarWithNavController(navController, appBarConfiguration)

Next, according to the purpose of the current page, I override the onSupportNavigationUp() function, then nav_host_fragment call on navigateUp() and passing appBarConfiguration to support the rollback navigation or display the menu icons.

override fun onSupportNavigateUp(): Boolean {
   return findNavController(R.id.nav_host_fragment).navigateUp(
       appBarConfiguration
   )
}

Now I can navigate to the selectionFragment, and you can see that the title has been updated, and the back button is also displayed, and the user can return to the previous page.

△ 标题更新了并且也显示了返回按钮

△ The title has been updated and the back button is also displayed

Bottom tab bar

So far, it’s going well, but the app can’t navigate to the coffeeList Fragment. Next we will solve this problem.

We start by adding the bottom tab bar. First add the bottom_nav_menu.xml file and declare two menu elements. NavigationUI relies on MenuItem of id and uses it to match the id of the destination page in the navigation map. I also set an icon and title for each destination page.

<menu xmlns:android="http://schemas.android.com/apk/res/android">
   <item
       android:id="@id/donutList"
       android:icon="@drawable/donut_with_sprinkles"
       android:title="@string/donut_name" />
   <item
       android:id="@id/coffeeList"
       android:icon="@drawable/coffee_cup"
       android:title="@string/coffee_name" />
</menu>

Now MenuItem ready, I mainActivity add a layout BottomNavigationView , and bottom_nav_menu set BottomNavigationView of menu property.

<com.google.android.material.bottomnavigation.BottomNavigationView
       android:id="@+id/bottom_nav_view"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       app:menu="@menu/bottom_nav_menu" />

To make the bottom tab bar to play a role, calling here setupWithNavController() function navController incoming BottomNavigationView .

private fun setupBottomNavMenu(navController: NavController) {
   val bottomNav = findViewById<BottomNavigationView>(
       R.id.bottom_nav_view
   )
   bottomNav?.setupWithNavController(navController)
}

Please note that I did not call any navigation operations from the navigation graph. In fact, there is not even a path coffeeList And before ActionBar operation as we have done, BottomNavigationView by matching MenuItem the id and navigation purposes page id automatically responds navigation operation.

Navigation drawer

Although it looks good, if the screen size of your device is large, the bottom tab bar may not provide the best user experience. To solve this problem, I will use another layout file with the w960dp qualifier, indicating that it is suitable for devices with larger and wider screens.

This layout file is similar to the default activity_main layout, which already contains Toolbar and FragmentContainerView . I need to add NavigationView , and nav_drawer_menu set NavigationView of menu property. Next, I will add a separator between NavigationView and FragmentContainerView

<RelativeLayout
   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="com.android.samples.donuttracker.MainActivity">
   <com.google.android.material.navigation.NavigationView
       android:id="@+id/nav_view"
       android:layout_width="wrap_content"
       android:layout_height="match_parent"
       android:layout_alignParentStart="true"
       app:elevation="0dp"
       app:menu="@menu/nav_drawer_menu" />
   <View
       android:layout_width="1dp"
       android:layout_height="match_parent"
       android:layout_toEndOf="@id/nav_view"
       android:background="?android:attr/listDivider" />
   <androidx.appcompat.widget.Toolbar
       android:id="@+id/toolbar"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:layout_alignParentTop="true"
       android:background="@color/colorPrimary"
       android:layout_toEndOf="@id/nav_view"
       android:theme="@style/ThemeOverlay.MaterialComponents.Dark.ActionBar" />
   <androidx.fragment.app.FragmentContainerView
       android:id="@+id/nav_host_fragment"
       android:name="androidx.navigation.fragment.NavHostFragment"
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       android:layout_below="@id/toolbar"
       app:defaultNavHost="true"
       android:layout_toEndOf="@id/nav_view"
       app:navGraph="@navigation/nav_graph" />
</RelativeLayout>

As a result, on wide-screen devices, NavigationView will be displayed on the screen BottomNavigationView Now that the layout file is ready, I create another nav_drawer_menu.xml , and add donutList and coffeeList as the main group as the destination page. For MenuItem , I added selectionFragment as its destination page.

<menu xmlns:android="http://schemas.android.com/apk/res/android">
   <group android:id="@+id/primary">
       <item
           android:id="@id/donutList"
           android:icon="@drawable/donut_with_sprinkles"
           android:title="@string/donut_name" />
       <item
           android:id="@id/coffeeList"
           android:icon="@drawable/coffee_cup"
           android:title="@string/coffee_name" />
   </group>
   <item
       android:id="@+id/selectionFragment"
       android:title="@string/action_settings" />
</menu>

Now that all the layouts are ready, we go back to MainActivity and set up the navigation drawer so that it can work with NavigationController . Before and for BottomNavigationView similar done, create a new approach here, and call setupWithNavController() function navController incoming NavigationView . In order to keep the code tidy and clearer between elements, we will implement related operations in a new method and call this method onCreate()

private fun setupNavigationMenu(navController: NavController){
   val sideNavView = findViewById<NavigationView>(R.id.nav_view)
   sideNavView?.setupWithNavController(navController)
}

Now when I run the application on a device with a wider screen, I can see that the MenuItem has been set in the navigation drawer, and in the navigation diagram, the MenuItem matches the id of the destination page.

△ 在屏幕较宽的设备上运行 Donut Tracker

△ Run Donut Tracker on a device with a wider screen

Please note that when I switch pages, the back button will automatically appear in the upper left corner. If you want to do this, you can also modify AppBarConfiguration to add CoffeeList as the topmost destination page.

Summary

This is the content shared this time. The Donut Tracker application does not need the bottom tab bar or the navigation drawer, but after adding new functions and purpose pages, NavigationUI can help us to a large extent in handling the navigation functions in the application.

We don't need to perform extra operations, just add UI components, and match MenuItem the id of the destination page. You can complete code of , and compare starter branch to observe the code changes.


Android开发者
404 声望2k 粉丝

Android 最新开发技术更新,包括 Kotlin、Android Studio、Jetpack 和 Android 最新系统技术特性分享。更多内容,请关注 官方 Android 开发者文档。