Android Dev Summit, October 23-24: two days of technical content, directly from the Android team. Sign-up for livestream updates.

Update UI components with NavigationUI

The Navigation Architecture Component includes a NavigationUI class. This class contains static methods that manage navigation with the top app bar, the navigation drawer, and bottom navigation.

Listen for navigation events

Interacting with the NavController is the primary method for navigating between destinations. The NavController is responsible for replacing the contents of the NavHost with the new destination. In many cases, UI elements—such as a top app bar or other persistent navigation controls like a BottomNavigationBar—live outside of the NavHost and need to be updated as you navigate between destinations.

NavController offers an OnDestinationChangedListener interface that is called when the NavController's current destination or its arguments change. A new listener can be registered via the addOnDestinationChangedListener() method. Note that when calling addOnDestinationChangedListener(), if the current destination exists, it is immediately sent to your listener.

NavigationUI uses OnDestinationChangedListener to make these common UI components navigation-aware. Note, however, that you can also use OnDestinationChangedListener on its own to make any custom UI or business logic aware of navigation events.

As an example, you might have common UI elements that you intend to show in some areas of your app while hiding them in others. Using your own OnDestinationChangedListener, you can selectively show or hide these UI elements based on the target destination, as shown in the following example:

Kotlin

navController.addOnDestinationChangedListener { _, destination, _ ->
   if(destination.id == R.id.full_screen_destination) {
       toolbar.visibility = View.GONE
       bottomNavigationView.visibility = View.GONE
   } else {
       toolbar.visibility = View.VISIBLE
       bottomNavigationView.visibility = View.VISIBLE
   }
}

Java

navController.addOnDestinationChangedListener(new NavController.OnDestinationChangedListener() {
   @Override
   public void onDestinationChanged(@NonNull NavController controller,
           @NonNull NavDestination destination, @Nullable Bundle arguments) {
       if(destination.getId() == R.id.full_screen_destination) {
           toolbar.setVisibility(View.GONE);
           bottomNavigationView.setVisibility(View.GONE);
       } else {
           toolbar.setVisibility(View.VISIBLE);
           bottomNavigationView.setVisibility(View.VISIBLE);
       }
   }
});

Top app bar

The top app bar provides a consistent place along the top of your app for displaying information and actions from the current screen.

NavigationUI contains methods that automatically update content in your top app bar as users navigate through your app. For example, NavigationUI uses the destination labels from your navigation graph to keep the title of the top app bar up-to-date.

When using NavigationUI with the top app bar methods discussed below, the label you attach to destinations can be automatically populated from the arguments provided to the destination by using the format of {argName} in your label.

NavigationUI provides support the following top app bar types:

AppBarConfiguration

NavigationUI uses an AppBarConfiguration object to manage the behavior of the Navigation button in the upper-left corner of your app's display area. By default, the Navigation button is hidden when a user is at a top-level destination of a navigation graph and appears as an Up button in any other destination.

To use the start destination of your navigation graph as the only top-level destination, you can create an AppBarConfiguration object and pass in the corresponding navigation graph, as shown below:

Kotlin

val appBarConfiguration = AppBarConfiguration(navController.graph)

Java

AppBarConfiguration appBarConfiguration =
        new AppBarConfiguration.Builder(navController.getGraph()).build();

If you want to customize which destinations are considered top-level destinations, you can instead pass a set of destination IDs to the constructor, as shown below:

Kotlin

val appBarConfiguration = AppBarConfiguration(setOf(R.id.main, R.id.android))

Java

AppBarConfiguration appBarConfiguration =
        new AppBarConfiguration.Builder(R.id.main, R.id.android).build();

Create a Toolbar

To create a Toolbar with NavigationUI, first define the bar in your main activity, as shown below:

<LinearLayout>
    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar" />
    <fragment
        android:id="@+id/nav_host_fragment"
        ... />
    ...
</LinearLayout>

Next, call setupWithNavController() from your main activity's onCreate() method, as shown below:

Kotlin

override fun onCreate(savedInstanceState: Bundle?) {
    setContentView(R.layout.activity_main)

    ...

    val navController = findNavController(R.id.nav_host_fragment)
    val appBarConfiguration = AppBarConfiguration(navController.graph)
    findViewById<Toolbar>(R.id.toolbar)
        .setupWithNavController(navController, appBarConfiguration)
}

Java

@Override
protected void onCreate(Bundle savedInstanceState) {
    setContentView(R.layout.activity_main);

    ...

    NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
    AppBarConfiguration appBarConfiguration =
            new AppBarConfiguration.Builder(navController.getGraph()).build();
    Toolbar toolbar = findViewById(R.id.toolbar);
    NavigationUI.setupWithNavController(toolbar, navController);
}

Include CollapsingToolbarLayout

To include a CollapsingToolbarLayout with your Toolbar, first define the Toolbar and surrounding layout in your main activity, as shown below:

<LinearLayout>
    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="@dimen/tall_toolbar_height">

        <android.support.design.widget.CollapsingToolbarLayout
            android:id="@+id/collapsing_toolbar_layout"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:contentScrim="?attr/colorPrimary"
            app:expandedTitleGravity="top"
            app:layout_scrollFlags="scroll|exitUntilCollapsed|snap">

            <android.support.v7.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                app:layout_collapseMode="pin"/>
        </android.support.design.widget.CollapsingToolbarLayout>
    </android.support.design.widget.AppBarLayout>

    <fragment
        android:id="@+id/nav_host_fragment"
        ... />
    ...
</LinearLayout>

Next, call setupWithNavController() from your main activity's onCreate method, as shown below:

Kotlin

override fun onCreate(savedInstanceState: Bundle?) {
    setContentView(R.layout.activity_main)

    ...

    val layout = findViewById<CollapsingToolbarLayout>(R.id.collapsing_toolbar_layout)
    val toolbar = findViewById<Toolbar>(R.id.toolbar)
    val navController = findNavController(R.id.nav_host_fragment)
    val appBarConfiguration = AppBarConfiguration(navController.graph)
    layout.setupWithNavController(toolbar, navController, appBarConfiguration)
}

Java

@Override
protected void onCreate(Bundle savedInstanceState) {
    setContentView(R.layout.activity_main);

    ...

    CollapsingToolbarLayout layout = findViewById(R.id.collapsing_toolbar_layout);
    Toolbar toolbar = findViewById(R.id.toolbar);
    NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
    AppBarConfiguration appBarConfiguration =
            new AppBarConfiguration.Builder(navController.getGraph()).build();
    NavigationUI.setupWithNavController(layout, toolbar, navController, appBarConfiguration);
}

Action bar

To add navigation support to the default action bar, call setupActionBarWithNavController() from your main activity's onCreate() method, as shown below. Note that you need to declare your AppBarConfiguration outside of onCreate(), since you also use it when overriding onSupportNavigateUp():

Kotlin

private lateinit var appBarConfiguration: AppBarConfiguration

...

override fun onCreate(savedInstanceState: Bundle?) {
    ...

    val navController = findNavController(R.id.nav_host_fragment)
    appBarConfiguration = AppBarConfiguration(navController.graph)
    setupActionBarWithNavController(navController, appBarConfiguration)
}

Java

AppBarConfiguration appBarConfiguration;

...

@Override
protected void onCreate(Bundle savedInstanceState) {
    ...

    NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
    appBarConfiguration = new AppBarConfiguration.Builder(navController.getGraph()).build();
    NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration);
}

Next, override onSupportNavigateUp() to handle Up navigation:

Kotlin

override fun onSupportNavigateUp(): Boolean {
    val navController = findNavController(R.id.nav_host_fragment)
    return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
}

Java

@Override
public boolean onSupportNavigateUp() {
    NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
    return NavigationUI.navigateUp(navController, appBarConfiguration)
            || super.onSupportNavigateUp();
}

Tie destinations to menu items

NavigationUI also provides helpers for tying destinations to menu-driven UI components. NavigationUI contains a helper method, onNavDestinationSelected(), which takes a MenuItem along with the NavController that hosts the associated destination. If the id of the MenuItem matches the id of the destination, the NavController can then navigate to that destination.

As an example, the XML snippets below define a menu item and a destination with a common id, details_page_fragment:

<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:android="http://schemas.android.com/apk/res/android"
    ... >

    ...

    <fragment android:id="@+id/details_page_fragment"
         android:label="@string/details"
         android:name="com.example.android.myapp.DetailsFragment" />
</navigation>
<menu xmlns:android="http://schemas.android.com/apk/res/android">

    ...

    <item
        android:id="@id/details_page_fragment"
        android:icon="@drawable/ic_details"
        android:title="@string/details" />
</menu>

If your menu was added via the Activity's onCreateOptionsMenu(), for example, you can associate the menu items with destinations by overriding the Activity's onOptionsItemSelected() to call onNavDestinationSelected(), as shown below:

Kotlin

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

Java

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
    return NavigationUI.onNavDestinationSelected(item, navController)
            || super.onOptionsItemSelected(item);
}

Now, when a user clicks the details_page_fragment menu item, the app automatically navigates to the corresponding destination with the same id.

Add a navigation drawer

The navigation drawer is a UI panel that shows your app's main navigation menu. The drawer appears when the user touches the drawer icon in the app bar or when the user swipes a finger from the left edge of the screen.

The drawer icon is displayed on all top-level destinations that use a DrawerLayout. Top-level destinations are the root-level destinations of your app. They do not display an Up button in the app bar.

To add a navigation drawer, first declare a DrawerLayout as the root view. Inside the DrawerLayout, add a layout for the main UI content and another view that contains the contents of the navigation drawer.

For example, the following layout uses a DrawerLayout with two child views: a NavHostFragment to contain the main content and a NavigationView for the contents of the navigation drawer.

<?xml version="1.0" encoding="utf-8"?>
<!-- Use DrawerLayout as root container for activity -->
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true">

    <!-- Layout to contain contents of main body of screen (drawer will slide over this) -->
    <fragment
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:id="@+id/nav_host_fragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:defaultNavHost="true"
        app:navGraph="@navigation/nav_graph" />

    <!-- Container for contents of drawer - use NavigationView to make configuration easier -->
    <android.support.design.widget.NavigationView
        android:id="@+id/nav_view"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:fitsSystemWindows="true" />

</android.support.v4.widget.DrawerLayout>

Next, connect the DrawerLayout to your navigation graph by passing it to AppBarConfiguration, as shown below:

Kotlin

val appBarConfiguration = AppBarConfiguration(navController.graph, drawerLayout)

Java

AppBarConfiguration appBarConfiguration =
        new AppBarConfiguration.Builder(navController.getGraph())
            .setDrawerLayout(drawerLayout)
            .build();

Next, in your main activity class, call setupWithNavController() from your main activity's onCreate() method, as shown below:

Kotlin

override fun onCreate(savedInstanceState: Bundle?) {
    setContentView(R.layout.activity_main)

    ...

    val navController = findNavController(R.id.nav_host_fragment)
    findViewById<NavigationView>(R.id.nav_view)
        .setupWithNavController(navController)
}

Java

@Override
protected void onCreate(Bundle savedInstanceState) {
    setContentView(R.layout.activity_main);

    ...

    NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
    NavigationView navView = findViewById(R.id.nav_view);
    NavigationUI.setupWithNavController(navView, navController);
}

Bottom navigation

NavigationUI can also handle bottom navigation. When a user selects a menu item, the NavController calls onNavDestinationSelected() and automatically updates the selected item in the bottom navigation bar.

To create a bottom navigation bar in your app, first define the bar in your main activity, as shown below:

<LinearLayout>
    ...
    <fragment
        android:id="@+id/nav_host_fragment"
        ... />
    <android.support.design.widget.BottomNavigationView
        android:id="@+id/bottom_nav"
        app:menu="@menu/menu_bottom_nav" />
</LinearLayout>

Next, in your main activity class, call setupWithNavController() from your main activity's onCreate() method, as shown below:

Kotlin

override fun onCreate(savedInstanceState: Bundle?) {
    setContentView(R.layout.activity_main)

    ...

    val navController = findNavController(R.id.nav_host_fragment)
    findViewById<BottomNavigationView>(R.id.bottom_nav)
        .setupWithNavController(navController)
}

Java

@Override
protected void onCreate(Bundle savedInstanceState) {
    setContentView(R.layout.activity_main);

    ...

    NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
    BottomNavigationView bottomNav = findViewById(R.id.bottom_nav);
    NavigationUI.setupWithNavController(bottomNav, navController);
}

For a comprehensive example that includes bottom navigation, see the Android Architecture Components Advanced Navigation Sample on GitHub.

Additional resources

To learn more about navigation, consult the following additional resources.

Samples

Codelabs

Blog posts

Videos