Navigation Component for Android Part 3: Transition and Navigation
In this tutorial, you’ll learn how to use shared element transitions, action bar and bottom navigation to make an app that shows a list of random dogs images. By Ricardo Costeira.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Navigation Component for Android Part 3: Transition and Navigation
25 mins
- Getting Started
- Fetching Dependencies
- Sharing Elements Between Screens
- Adapting the Adapter
- Adding Shared Elements to NavController
- Walking the Doggo to Another View
- Teaching the RecyclerView to Stay
- Controlling the Action Bar
- Adding a Menu Item
- Implementing Bottom Navigation
- Updating Styles and Layouts
- Wiring the NavController to Bottom Navigation
- Fixing The Action Bar Navigation Button
- The Multiple Back Stacks Problem
- Where to Go From Here?
Teaching the RecyclerView to Stay
No, a doggo didn’t run away with your transition. They’re well-trained!
You’re facing a problem you had before, but with a different component. The dog pictures displayed by the RecyclerView also need to be loaded. This load takes more time than the RecyclerView needs to set up everything else.
The fix? Same as before. Call postponeEnterTransition()
followed by startPostponedEnterTransition()
. The difference is, this time you’ll do it with the RecyclerView.
First, go back to DoggoListFragment.kt. At the end of the setupRecyclerView()
, right below addOnScrollListener
, add the following code:
//1
postponeEnterTransition()
//2
viewTreeObserver.addOnPreDrawListener {
//3
startPostponedEnterTransition()
true
}
There are three new elements here:
- You postpone the Fragment’s enter transition.
- Then you set a listener
onPreDraw
to the RecyclerView. This callback is only invoked when all the views in the RecyclerView view tree are measured. - You call
startPostponedEnterTransition()
as soon as all the images finish loading.
Build and run the app again. Don’t you love it when things work correctly?
Notice that the other images appear and disappear without any animation at all.
You can solve this by going to res ▸ navigation ▸ doggo_list.xml and replacing the action tag of doggoListFragment
that navigates to DoggoFragment
with below:
<action
android:id="@+id/to_doggoFragment"
app:destination="@id/doggoFragment"
app:enterAnim="@anim/fragment_fade_enter"
app:exitAnim="@anim/fragment_fade_exit"
app:popEnterAnim="@anim/fragment_fade_enter"
app:popExitAnim="@anim/fragment_fade_exit" />
Build and run the app. Poof! No more animation glitches!
This was the most complex part of the tutorial. Congrats on making it this far!
Controlling the Action Bar
The Action Bar is one of the most important design elements in Android. It not only provides consistency between apps but also allows users to quickly interact with a familiar set of elements.
Navigation Component provides default support for Action Bars through the NavigationUI
class. The Action Bar in this app is the theme’s default.
Navigation Component also guarantees the principles of navigation for the Action Bar are followed:
- Up and Back are identical within your app task.
- The Up button never exits your app.
In the presentation package, open MainActivity.kt. At the top of the class, right above onCreate
, add the following properties:
private val navController by lazy { findNavController(R.id.nav_host_fragment) }
private val appBarConfiguration by lazy { AppBarConfiguration(navController.graph) }
Now resolve the import about findNavController
choosing findNavController(Activity.Int)(androidx...)
.
These should already be familiar to you from the part 2 of this tutorial. This NavController
is the same one used to navigate in DoggoListFragment
.
Now you need to connect the Action Bar to NavController
. Inside the setupActionBar()
, add the following one liner:
setupActionBarWithNavController(navController, appBarConfiguration)
Resolve the reference error, then build and run the app.
You’ll see the Action Bar now updates the title and shows the Up button. Success!
OK, the Up button doesn’t work. Deep inside, you knew it was too good to be true. :]
There’s one last step. Now you need to override onSupportNavigateUp
so NavController
will handle clicks on the Navigation button. Add the following method above the setupActionBar()
method declaration:
override fun onSupportNavigateUp(): Boolean {
return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
}
If you were using your own Toolbar instead of the default Action Bar, you wouldn’t need to override onSupportNavigateUp()
. With Toolbar, Navigation automatically handles click events for the Navigation button.
Build and run the app. You now have a working Up button!
Adding a Menu Item
Sometimes, you want certain screens to show menu items in the Action Bar. With Navigation Component, it only takes a few lines of code.
There’s already an About Fragment
in the app’s nav graph, so you’ll add a menu item that navigates to that Fragment.
First, you need to create the menu item.
Right-click your res package and select New ▸ Android Resource File. Then, on the Resource type dropdown, choose menu. Call it menu_about
. Delete everything in it and paste the following:
<?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/aboutFragment"
android:title="@string/about"
android:icon="@drawable/ic_settings_24dp"
app:showAsAction="always"/>
</menu>
Just your typical menu item. However, take a look at the item’s ID.
If you open the doggo_list.xml nav graph, you’ll notice that AboutFragment
has the same ID. This isn’t a coincidence. These IDs must match for the navigation to work.
Now, open DoggoFragment.kt. You’ll add the menu item here. In onCreateView()
, right before the return, add this line:
setHasOptionsMenu(true)
This tells the system that the Action Bar in this Fragment should display a menu item.
Next, tell the system which menu item to display and what to do with it. Paste this code after onCreateView()
:
// 1
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.menu_about, menu)
}
// 2
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return item.onNavDestinationSelected(findNavController()) ||
super.onOptionsItemSelected(item)
}
Resolve the import errors. In these method overrides, you:
- Inflate the menu item.
- Call
onNavDestinationSelected
, which is aNavigationUI
helper method. This method takes in theNavController
and, if the IDs of the destination and the menu item match, uses it to navigate to that destination.
Build and run the app. Try your new button:
Notice anything strange? As soon as you press the Up button, you’re back to the start destination. The back stack is effectively popping back to the nav graph start destination.
You can fix this by adding android:menuCategory="secondary"
to your menu item inside the menu_about.xml. Add it below android:id
:
<?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/aboutFragment"
android:menuCategory="secondary"
android:title="@string/about"
android:icon="@drawable/ic_settings_24dp"
app:showAsAction="always"/>
</menu>
This way, onNavDestinationSelected()
knows it shouldn’t pop the whole back stack.
Build your app, and try it out.
Look at you go! All that’s missing now is a Bottom Navigation setup inside the app.
Implementing Bottom Navigation
Each button of a bottom navigation bar represents a top-level destination. You should only use bottom navigation when you have three to five top-level destinations of equal importance. This app only has two, but, after all, it’s a demo.
The way you represent destinations varies with their number:
- Three destinations: Display icons and text labels for all.
- Four destinations: Active destinations display an icon and text label. Inactive destinations display icons and text labels are recommended.
- Five destinations: Active destinations display an icon and text label. Inactive destinations use icons and use text labels if space permits.