Save the date! Android Dev Summit is coming to Mountain View, CA on November 7-8, 2018.

Create a navigation drawer

The navigation drawer is a UI panel that shows your app's main navigation menu. It is hidden when not in use, but appears when the user swipes a finger from the left edge of the screen or, when at the top level of the app, the user touches the drawer icon in the app bar.

This page shows you how to implement a navigation drawer using the DrawerLayout APIs available in the Support Library.

Navigation Drawer Design

Before you decide to use a navigation drawer in your app, you should understand the use cases and design principles defined in the Navigation Drawer design guide.

Figure 1. A navigation drawer

Add dependencies

This page uses APIs from on the Android Support Library, so you need to add the following dependencies to your apps module's build.gradle file:

dependencies {
  implementation 'com.android.support:appcompat-v7:27.1.1'
  implementation 'com.android.support:design:27.1.1'
}

Add a drawer to your layout

To add a navigation drawer, declare your layout with a DrawerLayout as the root view. Inside the DrawerLayout, add a layout for the main content for the UI (your primary layout when the drawer is hidden) and another view that contains the contents of the navigation drawer.

For example, the following layout uses a DrawerLayout with two child views: a FrameLayout to contain the main content (which could, for example, by populated by a Fragment at runtime), 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) -->
    <FrameLayout
        android:id="@+id/content_frame"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <!-- 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>

This example demonstrates some important layout characteristics:

  • The FrameLayout is set to match the parent view's width and height, because it represents the entire UI when the navigation drawer is closed.
  • The NavigationView (the drawer) must specify its horizontal gravity with the android:layout_gravity attribute. To support right-to-left (RTL) languages, specify the value with "start" instead of "left" (so the drawer appears on the right when the layout is RTL).
  • The NavigationView sets android:fitsSystemWindows to "true" to ensure the contents of the drawer don’t overlay the status bar and other system windows. The DrawerLayout also uses fitsSystemWindows as a sign that it needs to inset its children (such as the main content view), but still draw the status bar background in that space as per the material design specs (which defaults to your theme’s colorPrimaryDark).

To configure the menu items listed in the drawer, specify a menu resource with the app:menu attribute, as shown in the example code below:

<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"
    app:menu="@menu/drawer_view" />

Create the menu resource with the corresponding file name. For example, at res/menu/drawer_view.xml:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
    <group android:checkableBehavior="single">
        <item
            android:id="@+id/nav_camera"
            android:icon="@drawable/ic_menu_camera"
            android:title="@string/import" />
        <item
            android:id="@+id/nav_gallery"
            android:icon="@drawable/ic_menu_gallery"
            android:title="@string/gallery" />
        <item
            android:id="@+id/nav_slideshow"
            android:icon="@drawable/ic_menu_slideshow"
            android:title="@string/slideshow" />
        <item
            android:id="@+id/nav_manage"
            android:icon="@drawable/ic_menu_manage"
            android:title="@string/tools" />
    </group>
</menu>

You can make a group of items single-selectable by applying android:checkableBehavior="single" to a group. This allows you to show which list item is currently selected.

To learn more about creating a menu XML file, see the Menu resource documentation.

Add a header to the nav drawer

Optionally, you can add a header at the top of the drawer, by specifying a layout with the app:headerLayout attribute as shown here:

<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"
    app:menu="@menu/drawer_view"
    app:headerLayout="@layout/nav_header" />

The following is an example header layout that provides a title:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="192dp"
    android:background="?attr/colorPrimaryDark"
    android:padding="16dp"
    android:theme="@style/ThemeOverlay.AppCompat.Dark"
    android:orientation="vertical"
    android:gravity="bottom">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="My header title"
        android:textAppearance="@style/TextAppearance.AppCompat.Body1"/>

</LinearLayout>

For design recommendations for the navigation drawer header sizing, spacing, and typography, see Patterns - Navigation drawer.

Handle navigation click events

To receive callbacks when the user taps a list item in the drawer, implement the OnNavigationItemSelectedListener interface and attach it to your NavigationView by calling setNavigationItemSelectedListener(). For example:

Kotlin

class MainActivity : AppCompatActivity() {

    private lateinit var mDrawerLayout: DrawerLayout

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

        mDrawerLayout = findViewById(R.id.drawer_layout)

        val navigationView: NavigationView = findViewById(R.id.nav_view)
        navigationView.setNavigationItemSelectedListener { menuItem ->
            // set item as selected to persist highlight
            menuItem.isChecked = true
            // close drawer when item is tapped
            mDrawerLayout.closeDrawers()

            // Add code here to update the UI based on the item selected
            // For example, swap UI fragments here

            true
        }
    }
}

Java

public class MainActivity extends AppCompatActivity {

    private DrawerLayout mDrawerLayout;

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

        mDrawerLayout = findViewById(R.id.drawer_layout);

        NavigationView navigationView = findViewById(R.id.nav_view);
        navigationView.setNavigationItemSelectedListener(
                new NavigationView.OnNavigationItemSelectedListener() {
                    @Override
                    public boolean onNavigationItemSelected(MenuItem menuItem) {
                        // set item as selected to persist highlight
                        menuItem.setChecked(true);
                        // close drawer when item is tapped
                        mDrawerLayout.closeDrawers();

                        // Add code here to update the UI based on the item selected
                        // For example, swap UI fragments here

                        return true;
                    }
                });
    }
}

When an item is tapped, this code sets the selected item as checked, changing the list item's style to be highlighted because the list items are part of a checkable group (as shown above in the menu file). It also closes the drawer by calling closeDrawers().

If your app switches out content based on which navigation menu item the user selects, you should consider using fragments in the main content area. Swapping fragments when you navigate from the navigation drawer allows for a seamless drawer animation, because the same base layout stays in place. To learn how to build your layout with fragments, see the Fragments documentation.

Add the nav drawer button to the app bar

At this point, you already have a working navigation drawer—the DrawerLayout provides built-in support for users to open and close the navigation drawer with a swipe on the side of the screen. But if your UI design includes an app bar, you should also allow users to open and close the drawer by touching the drawer icon on the top left of the app bar (as shown in figure 2). That's what you'll do in this section.

Figure 2. An app bar with the nav drawer button on the left

Add the toolbar to your layout

To make your UI meet the material design guidelines, the navigation drawer should appear in front of the app bar (as shown in figure 1), not below it. To make that happen, you need to use Toolbar as your app bar, instead of using a theme with a built-in app bar. You can add Toolbar to your layout as follows:

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

    <FrameLayout
        android:id="@+id/content_frame"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            android:theme="@style/ThemeOverlay.AppCompat.ActionBar" />

    </FrameLayout>
...

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

Then open your manifest file and set the app theme to one without the action bar, such as Theme.AppCompat.Light.NoActionBar:

<manifest ...>
  <application
      ...
      android:theme="@style/Theme.AppCompat.Light.NoActionBar" >
  ...
</manifest>

If you want to override some styles in this theme, see Styles and Themes.

Set the toolbar as the action bar

Now the toolbar appears in the layout but it's not functioning as the app bar. To apply the toolbar as the app bar, first make sure your activity extends from AppCompatActivity. Then call setSupportActionBar() and pass the Toolbar object from your layout:

Kotlin

class MainActivity : AppCompatActivity() {

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

        val toolbar: Toolbar = findViewById(R.id.toolbar)
        setSupportActionBar(toolbar)

        ...
    }
}

Java

public class MainActivity extends AppCompatActivity {

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

        Toolbar toolbar = findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        ...
    }
}

Add the nav drawer button

Now add the button that opens the navigation drawer. You first need to add the menu button's icon to your project. You can download a material menu icon from the Material Design Icons page, or import the icon from Android Studio:

  1. In the Project window, right-click the res folder and select New > Vector Asset.
  2. Select Material icon as the asset type and then click the Icon button to open the Select Icon window.
  3. Search for "menu" and select the menu icon (the icon is 3 horizontal lines).
  4. Click OK, and then rename the file to "ic_menu" and click Next to import it.

Enable the app bar's "home" button by calling setDisplayHomeAsUpEnabled(true), and then change it to use the menu icon by calling setHomeAsUpIndicator(int), as shown here:

Kotlin

class MainActivity : AppCompatActivity() {

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

        val toolbar: Toolbar = findViewById(R.id.toolbar)
        setSupportActionBar(toolbar)
        val actionbar: ActionBar? = supportActionBar
        actionbar?.apply {
            setDisplayHomeAsUpEnabled(true)
            setHomeAsUpIndicator(R.drawable.ic_menu)
        }

        ...
    }
}

Java

public class MainActivity extends AppCompatActivity {

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

        Toolbar toolbar = findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        ActionBar actionbar = getSupportActionBar();
        actionbar.setDisplayHomeAsUpEnabled(true);
        actionbar.setHomeAsUpIndicator(R.drawable.ic_menu);

        ...
    }
}

Open the drawer when the button is tapped

To open the drawer when the user taps on the nav drawer button, override onOptionsItemSelected() to hook into the options menu framework and listen for when the user taps the item with the ID android.R.id.home. Then call openDrawer() to open the nav drawer:

Kotlin

class MainActivity : AppCompatActivity() {

    private lateinit var mDrawerLayout: DrawerLayout

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

        mDrawerLayout = findViewById(R.id.drawer_layout)
        ...
    }

    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        return when (item.itemId) {
            android.R.id.home -> {
                mDrawerLayout.openDrawer(GravityCompat.START)
                true
            }
            else -> super.onOptionsItemSelected(item)
        }
    }
}

Java

public class MainActivity extends AppCompatActivity {

    private DrawerLayout mDrawerLayout;

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

        mDrawerLayout = findViewById(R.id.drawer_layout);
        ...
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case android.R.id.home:
                mDrawerLayout.openDrawer(GravityCompat.START);
                return true;
        }
        return super.onOptionsItemSelected(item);
    }
}

To learn more about handling options menu taps, see the Menus guide.

Notice that the code above passes GravityCompat.START as the open drawer animation gravity to openDrawer(). This ensures your nav drawer open animation behaves properly for both right-to-left and left-to-right layouts.

Listen for open/close events and other state changes

If your app needs to respond when the navigation drawer opens, closes, or simply changes its state and position—which might be useful if you need to adjust other parts of your UI based on the drawer's exact position—you should implement callback methods in the DrawerLayout.DrawerListener interface and pass it to addDrawerListener(). For example:

Kotlin

class MainActivity : AppCompatActivity() {

    private lateinit var mDrawerLayout: DrawerLayout

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

        mDrawerLayout = findViewById(R.id.drawer_layout)
        ...

        mDrawerLayout.addDrawerListener(
                object : DrawerLayout.DrawerListener {
                    override fun onDrawerSlide(drawerView: View, slideOffset: Float) {
                        // Respond when the drawer's position changes
                    }

                    override fun onDrawerOpened(drawerView: View) {
                        // Respond when the drawer is opened
                    }

                    override fun onDrawerClosed(drawerView: View) {
                        // Respond when the drawer is closed
                    }

                    override fun onDrawerStateChanged(newState: Int) {
                        // Respond when the drawer motion state changes
                    }
                }
        )
    }
    ...
}

Java

public class MainActivity extends AppCompatActivity {

    private DrawerLayout mDrawerLayout;

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

        mDrawerLayout = findViewById(R.id.drawer_layout);
        ...

        mDrawerLayout.addDrawerListener(
                new DrawerLayout.DrawerListener() {
                    @Override
                    public void onDrawerSlide(View drawerView, float slideOffset) {
                        // Respond when the drawer's position changes
                    }

                    @Override
                    public void onDrawerOpened(View drawerView) {
                        // Respond when the drawer is opened
                    }

                    @Override
                    public void onDrawerClosed(View drawerView) {
                        // Respond when the drawer is closed
                    }

                    @Override
                    public void onDrawerStateChanged(int newState) {
                        // Respond when the drawer motion state changes
                    }
                }
        );
    }
    ...
}