ActionBar / DrawerLayout

Navigation Drawer – Part 2

In the previous article we got a basic navigation drawer working. In this article of this series, we’ll begin hooking it up to the ActionBar.

There are a number of things that we need to do to correctly connect the navigation drawer up to the ActionBar:

  1. Connect the Home/Up button open and close the navigation drawer.
  2. Change the ActionBar title when the drawer is open.
  3. Hide any content-specific options menu items when the drawer is open.

To connect up the Home/Up button, we need to toggle the drawer state when it is clicked, and we also need to change the “Up” caret which appears next to the app icon in the ActionBar to indicate that the behaviour of the button has changed. Those nice folks at Google have actually provided a standard drawable resource which we can use, and this can be downloaded from here and copied in to your res/drawable-*dpi folder(s).

Any developers familiar with Android are probably already thinking about adding handlers in to onOptionsItemSelected() to handle the showing and hiding of the drawer when the Home/Up button is pressed, but actually we don’t have to.One of the new classes introduced with these new features is ActionBarDrawerToggle. This is an implementation of the DrawerListener interface that we used previously, but it hooks up the Home/Up button for us, and also manages the changing of the “Up” indicator to the Navigation Drawer indicator as well.

To use this we need to create an instance of ActionBarDrawerToggle, and then set it as the DrawerListener of the DrawerLayout. For now this will break out navigation item selection, but we’ll restore that in due course.

@Override
protected void onCreate(Bundle savedInstanceState)
{
	.
	.
	.
	drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
	drawerToggle = new ActionBarDrawerToggle(this, drawer,
			R.drawable.ic_drawer, 
			R.string.open, R.string.close);
	drawer.setDrawerListener(drawerToggle);
	getActionBar().setDisplayHomeAsUpEnabled(true);
	getActionBar().setHomeButtonEnabled(true);
	updateContent();

The 5 arguments to the ActionBarDrawerToggle constructor are:

  1. The Context that the ActionBarToggle is created within.
  2. The DrawerLayout that the ActionBarToggle will control.
  3. The drawable to use to replace the standard “Up” indication.
  4. A string resource to describe the “open drawer” action for accessibility purposes.
  5. A string resource to describe the “close drawer” action for accessibility purposes.

We must also make the necessary setDisplayHomeAsUpEnabled() and setHomeButtonEnabled() calls on the ActionBar to get it to enable the Up/Home button, and display the mode indicator.

We’ve also moved the logic to display the selected Fragment in to its own method:

private int selection = 0;
private int oldSelection = -1;
private void updateContent()
{
	getActionBar().setTitle(names[selection]);
	if (selection != oldSelection)
	{
		FragmentTransaction tx = getSupportFragmentManager()
			.beginTransaction();
		tx.replace(R.id.main,
			Fragment.instantiate(MainActivity.this, 
				classes[selection]));
		tx.commit();
		oldSelection = selection;
	}
}

We also need to do some handling of the Up/Home button press in onOptionsItemSelected, but our ActionBarDrawerToggle handles all the hard work for us:

@Override
public boolean onOptionsItemSelected(MenuItem item)
{
	if(drawerToggle.onOptionsItemSelected(item))
	{
		return true;
	}
	return super.onOptionsItemSelected(item);
}

If you run this, you’ll see that the Up/Home button now correctly shows and hides the navigation drawer, but the “Up” indication is still showing. That’s because there is another step required to hook things up. In our onPostCreate() method we need to call syncState() on the ActionBarDrawerToggle object:

@Override
protected void onPostCreate(Bundle savedInstanceState)
{
	super.onPostCreate(savedInstanceState);
	drawerToggle.syncState();
}

The reason for this is that final initialisation of the drawer state needs to be performed after the activity state has been restored. That way, if an orientation change occurs while the drawer is visible, the correct drawer state is restored following the orientation change. The change of the mode indicator also occurs here, and it is a fortunate side effect that we get a visual indication that things aren’t quite right if we omit this important step.

If we run this we can see that we now see the correct indication, and the Up/Home button now toggles the drawer state:

closed

open

In the concluding article in this series we’ll look at changing the ActionBar title, and hiding any content-specific menus.

The source code for this article is available here.

© 2013 – 2014, Mark Allison. All rights reserved.

Copyright © 2013 Styling Android. All Rights Reserved.
Information about how to reuse or republish this work may be available at http://blog.stylingandroid.com/license-information.

13 Comments

  1. Everything is perfect and thank you for your great articles. But there is one little thing that comes to my mind. You are using the stock ActionBar that means you have to use minSDK=’11’. And you are using support fragments. There is no need to use support library there I guess.

  2. @Said Tahsin Dane:
    You must use the support library beacuse the DrawerLayout isn’t available as “normal” API…

  3. This implemetantion on NavDrawer and the Google official Example is really laggy and slow, even on my Nexus4 ! even using android:hardwareAccelerated=”true” !

    The youtube/G+/Hangout isn’t. How can we have lag free, fast Navigation Drawer ?

    Your opinion ?

    Thanks

    1. It seems fine on my Galaxy Nexus. Maybe there’s something else running on your N4 that’s causing the lag.

      1. Thanks for your prompt reply!

        I’ve tried also on nexus S (cyano 10.1). The same lag.

        The major lag appears during the fragment switch (during fragment inflate).
        Tap menu item -> load corresponding fragment.

        There is a way to address this issue ?
        a ViewSwitcher ? ViewStub ?

        1. I realised that this may be because you are trying to perform your Fragment change concurrently with the drawer animation. There is a stutter if you do that. I covered this in the first article – you may need to ensure that do these sequentially.

  4. Is there a way to add icons to this piece of code? I used it to set up my navigation drawer and now I want to add icons to each listview item.

    Any ideas?

  5. Hi he author and the commenters,

    I could not find a way to do a tablet multi pane layout easily with NavigationDrawer. Play Music app does that. I have used LOCK_MODE_LOCKED_OPENED but it opens the drawer on top of the content as expected and it cannot be closed. Therefore the content is not completely visible. Do we have to do it manually?

  6. nice work! Is there a way to acces the indicator drawable icon and change it ? I need to put a warning sign on the drawer icon (indeed change the icon) when a message is retrieved.

  7. I think this line:
    drawer.setDrawerListener(drawerListener);

    is supposed to be:
    drawer.setDrawerListener(drawerToggle);

    This seemed to work for me. Cheers, thanks for all the great content!

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.