Navigation Component: An Overview
In which I introduce some fundamental concepts of the Navigation component tool and APIs
This article is the first in hopefully many articles in an ongoing series we are calling MAD Skills. We will be posting video episodes in the MAD Skills playlist on the Android Developers YouTube channel. When there is not an existing article for any given video episode, we’ll post one with similar content (like this one!), for those who prefer to read about this stuff (also because it tends to be easier to copy/paste code from text than from a video).
If you prefer your content in video form, here’s the thing to watch:
MAD Skills: Navigation Overview
This article provides a quick, high-level overview of Navigation component, including how to create a new application with navigation capability, details on the containment hierarchy of a navigation-enabled UI, and an explanation of some of the major APIs and pieces involved in making Navigation component work.
Note that there is already good introductory material on Navigation component, such as the Get started guide and Ian Lake’s recent video. The reason for this overview article is more to sync with the video content, and also to lay down some of the groundwork that I will rely on in upcoming episodes.
So if you want to learn more, there are plenty of opportunities to, er, navigate your way through the material.
Introduction
Navigation component is the API and the design tool in Android Studio that makes it much easier to create and edit navigation flows throughout your application. Before Navigation component, navigation tasks in an application were created very manually. You’d add a listener in your code for whatever UI element triggered a navigation action and then write the code to, say, launch an intent to show a new activity. Or transition to a Fragment.
You also needed to correctly handle the Back and Up actions, when the user clicked on the device’s Back button or the Up button in the ActionBar. The way these two related-but-different actions were handled created… inconsistencies between applications.
With the Navigation component, we now have standard APIs, plus a visual tool in the IDE, to help make the entire process clearer, easier, and more consistent. You use the design tool to create navigation destinations and define the navigation paths, or actions, that take the user between destinations in your graph. Then you add the code which connects user interaction in your app with those actions to navigate appropriately.
Let’s see what it actually looks like in the tool and in the code. Let’s build an application.
Navigation Templates
One of the useful new features in recent Android Studio releases, starting with version 3.6, is the integration of templates for creating new applications that come with navigation built in. This facility isn’t necessary for using the Navigation component library, but it does make it a lot easier when you are building a new application, because it pulls in all of the necessary pieces to start with.
We’re going to use one of these templates by creating a new application with the Basic Activity template. A few of the templates come with navigation built-in, but we’ll start with this one for now.
This template creates a new application for us with the basic structure of a navigation-enabled application. We get two destinations as well as a navigation graph that defines the navigation paths between them.
Once the application is loaded and ready in the IDE, go to the navigation resource file, nav_graph.xml
View this file with the Design option (vs Code or Split), so you see what the current navigation graph of the application looks like.
You can see two destinations: FirstFragment
, which has been set up as the initial, or home, destination, and SecondFragment
, which is another destination we can navigation to and from.
Clicking on these destinations, you can see the information about them in the property sheet on the right, which shows information like the Fragment
class that is used for that destination.
You can also see, in the navigation view shown in the previous figure, arrows between the two destinations. These are the actions, which define the navigations that are possible in this graph. There is a navigation from FirstFragment
to SecondFragment
, and from SecondFragment
back to FirstFragment
.
The actions define the navigations that are possible, but they do not specify when those navigations occur; that logic lives in your code. So when the user clicks on an item that should trigger a navigation, that’s where you call the navigation APIs that navigate to a destination in the graph using one of these actions.
Actions are also useful for defining things like values to pass to the destination as well as transition animations to use when entering and exiting the source and destination. We’ll see more about some of these properties in later episodes, or you can read about them in the navigation docs.
We can use the navigation tool to define new destinations, which can be placeholders, if we do not yet have the Fragment
class written for a destination, or they can use existing Fragment
classes for each destination. By defining destinations and the actions that take us between destinations, you can design the entire flow between screens of your application, in a much more visual and intuitive way than before.
But Where’s the Code?
So far, we’ve been doing all of this in the visual tool. But like all resource files in Android Studio, it is backed by XML code, which you can view and edit directly. If you switch to Code view in the tool, you’ll see the underlying XML:
<?xml version="1.0" encoding="utf-8"?>
<navigation
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:id="@+id/nav_graph"
app:startDestination="@id/FirstFragment"> <fragment
android:id="@+id/FirstFragment"
android:name="com.android.samples.navoverviewarticle.FirstFragment"
android:label="@string/first_fragment_label"
tools:layout="@layout/fragment_first"> <action
android:id="@+id/action_FirstFragment_to_SecondFragment"
app:destination="@id/SecondFragment" />
</fragment> <fragment
android:id="@+id/SecondFragment"
android:name="com.android.samples.navoverviewarticle.SecondFragment"
android:label="@string/second_fragment_label"
tools:layout="@layout/fragment_second"> <action
android:id="@+id/action_SecondFragment_to_FirstFragment"
app:destination="@id/FirstFragment" />
</fragment></navigation>
Here you can see that the structure of the navigation graph is pretty simple. There is the navigation
element itself, which contains the rest of the structure and defines which destination is the start, or home destination. Each of the destinations in the graph are fragment
s, and each destination holds zero or more action
s, which define navigation to other destinations in the graph.
The Basic Activity template also creates placeholder code to navigate between the two destinations. For example, FirstFragment
has this code, which is triggered when the user clicks the button in the UI:
override fun onViewCreated(view: View, savedInstanceState: Bundle?){
super.onViewCreated(view, savedInstanceState)
view.findViewById<Button>(R.id.button_first)
.setOnClickListener {
findNavController()
.navigate(R.id.action_FirstFragment_to_SecondFragment)
}
}
The call to navigate()
, using the action_FirstFragment_to_SecondFragment
defined in the navigation graph file, causes the application to navigate to the second destination.
You can run the application and click the buttons (or the Back button, which is automatically plugged in to navigate back) to see the result:
Navigation Hierarchy
To understand how the pieces of Navigation component work together, I find it helpful to look at how they relate to each other in the containment hierarchy in the UI. In order to see that, let’s start another project, this time using the Navigation Drawer Activity template.
Once the application is loaded into Android Studio, go ahead and run it. You should see something like this:
In this application, unlike the one we created with the Basic Activity template, there is no button to click to navigate to to the next destination. Instead, navigation happens via the menu items in the DrawerLayout
:
When the user clicks on the items in the DrawerLayout
, that causes the application to navigate to the destinations associated with those items. This is because Navigation component automatically hooks up the menu items with destinations so you don’t have to write the code to make those connections yourself.
Now let’s look at the containment hierarchy that is involved in making all of this work. To do this, we’re going to walk through the UI of the Layout Inspector tool in Android Studio, which you can launch from the Tools menu:
Layout Inspector allows us to visualize the view hierarchy of the entire application, along with the properties of each of the containers and views we see. You should see something like this:
Here, the blue rectangle in the activity display indicates the bounds of whatever view is currently selected (the top level LinearLayout
inside the DecorView
, in this case).
We could walk through the entire hierarchy (and I encourage you to do so; it helps to visualize what’s going on in a standard view hierarchy). But instead, I want to call out just a couple of specific items. First, let’s select the ConstraintLayout item:
The ConstraintLayout
container is defined in the main_activity.xml layout file. It contains the actual content of the application (and not all of the stuff that’s created on your behalf like the ActionBar
elements). Inside of that, we see the NavHostFragment
item:
This is where a lot of the magic happens with Navigation component. NavHostFragment
is the container in which fragment destinations are swapped in and out as the user navigates between them.
The other element I wanted to point out is the NavigationView
:
This view is currently offscreen, to the left. This is the NavigationDrawer
with the menu items that are used to navigate between destinations. It is not currently visible because we need to tap the ActionBar menu to bring it on screen.
Navigation Pieces
Now that we’ve seen how some of the UI pieces relate to each other in the hierarchy, I wanted to talk about some of the important pieces that Navigation component uses to do the work of navigating between destinations.
I found some of this confusing when I first started using Navigation component because many of the pieces have similar uses of the terms Navigation and Nav, and some of those elements even existed before the Navigation component library itself. So I thought it might be useful to see what these major pieces are and how they relate to each other.
App Container
To illustrate how these pieces integrate, I’ll use a simplified sketch of an overall app container:
Here we see the Toolbar up at the top, where the ActionBar menu resides. Then the app content itself lives below that, containing the NavHostFragment
, which contains the UI for the current destination.
NavHostFragment
The NavHostFragment is, as I said earlier, where a lot of the work happens with navigation. This is the container used by Navigation component to swap destination fragments in and out. When you navigate in your application to a given fragment destination, it will replace whatever is currently in the NavHostFragment with the new fragment.
NavController
NavController is an internal piece used by Navigation component which is crucial behind the scenes. This is the logic inside the Navigation library which handles swapping the destinations into and out of the NavHostFragment
as the user navigates through the application.
NavigationView
Next is the NavigationView
, the drawer which slides in from the left. It provides a menu of possible destinations in the navigation graph. One of the cool things about NavigationView
is that you can use the menu IDs in the drawer to automatically navigate to the associated destinations, avoiding boilerplate code to manually navigate based on menu selections.
It’s important to note that NavigationView
exists outside of the NavHostFragment
container; it is not a destination itself, but rather a mechanism for specifying the destinations that are navigated to. It’s also interesting to note that this API existed before component and had a life of its own before Navigation component came along and integrated it into its overall system for navigation.
NavigationUI
This is the piece of Navigation component that is responsible for updating UI outside of the NavHostFragment
. Most of the visual updates related to navigation happen inside of the NavHostFragment
, but there are other pieces of the system which are not in that container that need updating as well, including the navigation drawer that we saw above, and elements like a tab bar (which can be used to show current destination info).
Summary
This was a really quick overview of Navigation component, to give you a taste of how to create a navigation-enabled application as well giving glimpse into the overall structure of a such an application. In future articles+videos, I will explore more of the technical details of how to interact with specific parts of the the navigation API, like navigating to dialog destinations, using SafeArgs, and handling deep links.
For More Information
For more details on Navigation component, check out the guide Get started with the Navigation component on developer.android.com.