Design Support Library (I): Navigation View

Google I/O 2015 has brought a whole new set of tools for Android Developers which are meant to make our lives easier. I´d like to devote a set of articles to talk about the incredibly useful Design Support Library.

Though there is a good example from Chris Banes at Github, I´d like to talk a little deeper about each new features while migrating the example app Materialize Your App, which you can find at Github

Navigation View

In this article, I´ll start talking about the Navigation View. Since Material Design was released, we were given a standard definition on how a Navigation Drawer must look and feel.

navigation_drawer

Truth is that implementing those guidelines was rather time consuming. But now, with the navigation view, the implementation is much easier.

How Navigation View works?

You´d basically add it in the same position previously used for your custom view, inside a Drawer Layout. The Navigation View will receive a couple of parameters, and optional layout for a header, and a menu that will be used to build the navigation options. After that, you will only need to add a listener to capture selection events.

Implementation

First of all, we´re creating the menu. It´s quite straightforward, you just need to create a group and say only one item can be checked at the same time:

[xml]
<menu xmlns:android="http://schemas.android.com/apk/res/android">

<group
android:checkableBehavior="single">

<item
android:id="@+id/drawer_home"
android:checked="true"
android:icon="@drawable/ic_home_black_24dp"
android:title="@string/home"/>

<item
android:id="@+id/drawer_favourite"
android:icon="@drawable/ic_favorite_black_24dp"
android:title="@string/favourite"/>

<item
android:id="@+id/drawer_settings"
android:icon="@drawable/ic_settings_black_24dp"
android:title="@string/settings"/>

</group>
</menu>
[/xml]

Theoretically, you can also add sections with headers by adding a submenu to an item, something like this:

[xml]
<item
android:id="@+id/section"
android:title="@string/section_title">
<menu>
<item
android:id="@+id/drawer_favourite"
android:icon="@drawable/ic_favorite_black_24dp"
android:title="@string/favourite"/>

<item
android:id="@+id/drawer_downloaded"
android:icon="@drawable/ic_file_download_black_24dp"
android:title="@string/downloaded"/>
</menu>
</item>
[/xml]

This will create a divider and a header, and will add the items right below. However, I couldn´t find a way to mark any of those items as checked. I´ll update this lines if I come up with a solution. Anyway, I encourage you to try it and see how it works.

Now, we can add a navigation view to our activity layout and set the menu and the header layout. I won´t talk here about the header, because it can be any layout you want, but you can check an example at the Github repo.

[xml]
<android.support.v4.widget.DrawerLayout
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/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context=".MainActivity">

<FrameLayout
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">

</FrameLayout>

<android.support.design.widget.NavigationView
android:id="@+id/navigation_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
app:headerLayout="@layout/drawer_header"
app:menu="@menu/drawer"/>

</android.support.v4.widget.DrawerLayout>
[/xml]

Our last step will be the Java code. First you need to enable home as up:

[java]
final Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
final ActionBar actionBar = getSupportActionBar();

if (actionBar != null) {
actionBar.setHomeAsUpIndicator(R.drawable.ic_menu_black_24dp);
actionBar.setDisplayHomeAsUpEnabled(true);
}
[/java]

Next, initialize the navigation drawer.When an item is selected, it shows a snackbar (I´ll talk about it in next articles), selects the clicked item and closes the drawer:

[java]
drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);

NavigationView view = (NavigationView) findViewById(R.id.navigation_view);
view.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
@Override public boolean onNavigationItemSelected(MenuItem menuItem) {
Snackbar.make(content, menuItem.getTitle() + " pressed", Snackbar.LENGTH_LONG).show();
menuItem.setChecked(true);
drawerLayout.closeDrawers();
return true;
}
});
[/java]

And finally, open the drawer when the menu action is pressed:

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

return super.onOptionsItemSelected(item);
}
[/java]

Conclusion

Now it´s really easy to create a navigation drawer that meets Material guidelines thanks to the use of the design support library and the Navigation View. Next articles will cover some other new elements that will help us create user interfaces in a faster and easier way. You can move to the next article about Floating Action Button now. Remember you can see all this code in a small working app example at Github.

84 thoughts on “Design Support Library (I): Navigation View”

    1. Antonio Leiva

      If you try that code, you can click on subItems, but they don´t keep mark as checked. Try the same on the top items, and you´ll see they do.

      1. subitems are not wrapped into a there
        top group goes like: menu -> group -> item, item,…
        subitems are: menu -> item, item,…

    1. Antonio Leiva

      You only can if it’s with a header as far as I know. Can’t add a line divider without text.

  1. I’m still confused why setHomeAsUpIndicator is required. On my previous navigation menu the actionbar automatically had the proper symbol.

    1. I agree. using the api *setHomeAsUp* is very confusing. Home as Up is used for Home as Hamburger. Home doesn’t refer to home at all.

      The best way to do this is to just use toolbar to build your own action bar where the concept of home is not present.

  2. Mayur Solanki

    Design support lib
    Is this lib support from API Level 14 and above ?

  3. How can i add items in Navigation view programmatically instead of data in xml menu file? Is there way i can do this?

    1. “`java
      Menu menu = mNavigationView.getMenu();
      menu.add(“new item”);
      “`
      But it brings me another bug.

      1. you need to add menuitems once navigationView is created:
        menu.add(groupId, itemId, order, title).setIcon(resId)

        I don’t know how to add items to group or menu item subsection, but it’s possible to add them dynamically

    1. Antonio Leiva

      Not for what it used to be used. But it may have other usages. Check Gmail for instance.

      1. From what I can tell you can still use it to get the icon but that’s about it (annoying that everyone has to provide their own hamburger icon).

    1. Antonio Leiva

      It may be possible by creating different groups, but I think it will have the same problem as Submenus. Couldn’t try it yet btw

  4. This new support library is a dream come true. I always used to dread making a ListView/RecyclerView(Which was a bit better for me) in a navigation drawer. Thanks for the tutorial by the way.

    1. Antonio Leiva

      It’s based on the theme. The yellow is because of the colorControlHighlight. But you have methods background, text and some other things in NavigationView

  5. Nice post, definitely helped get me started with this widget. Today I need to look into having a NavigationView that can inflate custom views apart from the standard menu items. In my current project I have an item with a custom EditText as one of the navigation items, and so far I haven’t figured out how I might be able to add this with the new support library widget.

      1. Well, you were right about that. I ended up just switching to a regular menu item which inflates the custom view in a dialog, though I feel it’s a downgrade from my previous UX. I guess it’s a tradeoff for being able to get rid of the old navigation drawer implementation with a RecyclerView adapter 🙁 Being able to use a custom view for a menu item would be a nice enhancement, though.

  6. Hi Antonio Leiva,

    Regarding your point in post “However, I couldn´t find a way to mark any of those items as checked. I´ll update this lines if I come up with a solution”

    Solution:

    android:checkableBehavior=”single” for group will apply only to the main group items as you pointed out, and it won’t work for items in sub menu like in the case you have mentioned. A workaround solution for this will be to remove the tag from xml and set android:checkable = “true” for each item individually, then declare a MenuItem object in the activity and in onNavigationItemSelected event if previously declared MenuItem object is not null then set checked value as false for it and then save current selected menuItem received as parameter to earlier declared MenuItem object.

    This will set checked selection on even subitems.

    if (prevMenuItem != null) {
    prevMenuItem.setChecked(false);
    }
    menuItem.setChecked(true);
    mDrawerLayout.closeDrawers();
    prevMenuItem = menuItem;
    return true;

    1. Antonio Leiva

      Don’t know, but Eclipse is no longer the official Android IDE, so you will find nothing but problems if you use it in new projects.

  7. If you want to have two groups of checkable items do it like:

    private MenuItem prevMenuItem;

    navigationView.getMenu().getItem(0).setChecked(true); // just to check the first one on boot
    prevMenuItem = navigationView.getMenu().getItem(0);

    and in xml:

    1. Can’t post xml tags? Oh well..

      In xml you need to create groups with ids + set each item as android:checkable=true

  8. So I suppose that easiest and “right” way to set data into drawer header is to use data binding library, right? In my opinion doesn’t make sense to change navigation drawer fragment with NavigationView and then have to add header programmatically…

      1. It was more rhetorical question but what I meant is when I want to set username (in this example) I have to do ((TextView)findViewById(R.id.tvHeaderName)).setText(“name”) or to use data binding library… Nevertheless I updated one of my apps and it’s working fine, but what if you have counter beside icon and title you still need to do it “old” way or not?

  9. Brien Gerber

    If I have an image in my header layout that needs to be downloaded from a website, how do I update it after its completed? Previously this was part of a ListView so I could do notifydatasetchanged()

    1. Antonio Leiva

      If you see the code of the repo, I’m doing that with the user image. It’s a custom view, so you can do anything you want with it, you don’t need to notify the adapter.

  10. 1. Is there support for badges (like gmail inbox count).
    2. is it possible to set the menu layout xml programatically and not via the navigation view xml. (different navigations for different user types)

    1. Antonio Leiva

      1. No, it´s not possible
      2. Yeah, you have the methods addHeaderView(view) and inflateHeaderView(resId) for headers, and inflateMenu(resId) for the menu.

  11. Thanks you for this great tutorial.
    How to update menu programmatically ?

    For example, after button clicked I called : navigationView.getMenu().setGroupVisible(R.id.group_actions_user, sessionManager.isLoggedIn());
    but doesn’t work

  12. How do we share Drawer with all the activities?

    In the lister: onNavigationItemSelected of setNavigationItemSelectedListener…
    we can get the id and navigate to it

    what I am looking for is something like this

    Intent intent;
    int id = menuItem.getItemId();

    switch(id) {

    case R.id.home:
    case R.id.drawer_home:
    // call activity instead of fragment
    return true;

    case R.id.drawer_gallery:
    // call activity instead of fragment
    intent = new Intent(MainActivity.this, GalleryActivity.class);
    startActivity(intent);
    return true;

    case R.id.drawer_about:
    // call activity instead of fragment
    intent = new Intent(MainActivity.this, AboutActivity.class);
    startActivity(intent);
    return true;

    I know I can make all the menuItems to add/replace Fragment, but then handling fragments and memory is a big pain.
    Instead I want each menuItem select/click to invoke Activity. i.e. each MainMenuItem to have Activity and those will hold fragments with complex layouts.
    And all these activities can share same DrawerNavigation.
    Is this the recommended way? or do we always add Fragments for NavagitionDrawer item clicks??
    Should I add NavigationView to BaseActivity and then extend all activities from there??

    1. If you look at my comment somewhere earlier you’ll see that there is no problem with checkable bug and it’s easy to avoid without custom logic, only with xml.

  13. How do you get a click event on the navigation header of the navigation menu . Can you please suggest a pointer for it

    1. Antonio Leiva

      The header is an independent view, so you can add a click to it (or any of its internal views) the same way you’ll do it in any other view, using setOnClickListener.

      1. I agree but the think is I am unable to get specific Id of the header in my layout and hence I am stucked there

    2. I just dealt with this in my own app. A NavigationView is a ScrimInsetsFrameLayout wrapped around a ListView and it explicitly ignores (throws out) clicks on the first item of the list, which is the header. However, you can retrieve them yourself by wrapping the NavigationView’s onItemClickListener with your own:

      ListView menuList = (ListView) navigationView.getChildAt(0);
      final AdapterView.OnItemClickListener nativeListener = menuList.getOnItemClickListener();
      menuList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
      @Override public void onItemClick(AdapterView parent, View view, int position, long id) {
      if (position == 0) {
      // Perform header click here.
      }
      nativeListener.onItemClick(parent, view, position, id);
      }
      });

      Normally I don’t endorse this kind of relying on the internals of Android widgets, but in this case it’s the support library, which is packaged and shipped with your app. It’s probably better to build your own custom navigation view in the long run, but sometimes you need a quick fix!

  14. Can we add a custom view like a checkbox or toggle button to the item similar to the google play movie app?

  15. How can I use NavigationView with ActionBar and Activity (instead of SupportActionBar, AppCompatActivity)?

    Thanks

    1. Antonio Leiva

      You mean with the Toolbar instead of the Toolbar in Support Library? Didn’t try to be honest, but it’d only work in 5.0+,so not sure it is of much use.

  16. Ismael Calle

    Hi, I need help!!.

    How can I change the Activity theme or the Navigation View theme when I select an Item?

    Thanks

  17. Christopher Rucinski

    Hey Antonio, do you think the NavigationView should have persistent and temporary selection ability (setting these via new app:x attributes)?

    Clicking on something like Settings you would want to display selection to the user, but as soon as the NavigationView is closed (almost right away), it is automatically deselected without developer programming.

    Clicking on something that navigates to a new part of the app should be persistently selected – liked clicking on Downloaded in your example. It will be automatically selected and remain selected without developer programming.

    Do you think this is a good idea? I am thinking of sending a feature request, unless this is easily done right now. I am running into issues with selection with grouped items.

    By the way, are the grouped menu items the same as the submenu items? I know there are issues with submenu items

    1. Antonio Leiva

      Yeah, it’s the same and there are issues afaik. I don’t know of a way to leave a position from a different subgroup selected,but the only thing I played with it was what I did before I wrote before this article, soy there may be people who got better results.

  18. Hi,
    I am unable to import interface NavigationView in my activity class. Can you please guide me here?
    Many thanks,
    Ishaan

  19. Hi, nice tutorial, as always.
    I would like to know if there’s a simple way to change the items available in the drawer e.g. In my app, a logged in user should see a different menu than non-logged in users. How to achieve that since the menu items are declared in xml files.

    Thanks.

  20. hey can you tell me how to get headerview from navigation view?
    i want to change user image so how can do it programmatically ?

    1. When I wrote this tutorial, Design Support Library wouldn’t let you do so, don’t know if current versions are more flexible.

  21. Could you please explain how to fix nav-drawer header. So that when we scroll, nav drawer header still stays visible.

  22. Nice Tutorial….I have created Navigationdrawer with four frames.. Is it possible to transfer data from one Fragment to other in Navigationdrawer?

    1. Don’t completely understand the question, but transferring data among fragments probably doesn’t have anything to do with the NavigationView.

  23. Hi. I’m searching a method to replicate Google Play Store version 6.0 navigationView! It has submenus. I’m going crazy to find a solution without result. Could you help me?

Comments are closed.