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:
- Overview of navigation components
- Navigate to the dialog box
- Use SafeArgs when navigating in the app
- Use deep link navigation
- Build your first app bundle | MAD Skills
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.
△ 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.
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。