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.

Leave a rating/review
Download materials
Save for later
Share
You are currently viewing page 2 of 3 of this article. Click here to view the first page.

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:

  1. You postpone the Fragment’s enter transition.
  2. 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.
  3. 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?

User selecting doggos with correct tranisitions

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!

User selecting doggos but the up button doesn't work

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!

User selects doggos and uses up button to return to list

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.

Boxed fragment and item IDs showing they match

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:

  1. Inflate the menu item.
  2. Call onNavDestinationSelected, which is a NavigationUI helper method. This method takes in the NavController 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:

User selects a dog, selects button and is taken to start screen

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.

App correctly works

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.
Note: Icons are always mandatory but text labels are optional. If you want text, keep it short. Otherwise, you’ll face the Material Design Police for truncating or wrapping text in a bottom nav bar.