Navigation Drawer – Part 1

As I am writing this it is Google I/O week 2013. Amongst the new releases this week is a new version of the v4 support library (release 13) which contains support for the Navigation Drawer pattern. This pattern steadily grown in popularity, but until this point there has been no official support and guidelines from Google. This has now changed, and there are official design guidelines. In the recent series on Adapters we created an app which used Spinner navigation in the ActionBar to switch between different examples. In this short series we’ll convert this to use the Navigation Drawer pattern instead.

The first thing that we need to do is switch to release 13 of the support library. As this is a maven project, this is achieved via the POM, but if you are not using maven you’ll need to remove the existing library and re-import release 13.

The first thing that we need to do is alter our main layout and wrap it in a DrawerLayout (from the support library) and then add a new ListView to contain the navigation list:

<android.support.v4.widget.DrawerLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity" >

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

    <ListView
        android:id="@+id/drawer"
        android:layout_width="240dp"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:background="#FFF"
        android:choiceMode="singleChoice" />

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

Next we need to change our MainActivity to attach the Adapter to the ListView instead of the existing Spinner:

@Override
protected void onCreate(Bundle savedInstanceState)
{
	super.onCreate(savedInstanceState);
	setContentView(R.layout.activity_main);
	final String[] names = 
		getResources().getStringArray(R.array.nav_names);
	final String[] classes = 
		getResources().getStringArray(R.array.nav_classes);
	ArrayAdapter<String> adapter = 
		new ArrayAdapter<String>(
			getActionBar().getThemedContext(), 
			android.R.layout.simple_list_item_1, names);

	final DrawerLayout drawer = 
		(DrawerLayout)findViewById(R.id.drawer_layout);
	final ListView navList = 
		(ListView) findViewById(R.id.drawer);
	navList.setAdapter(adapter);
	navList.setOnItemClickListener(new OnItemClickListener()
	{

		@Override
		public void onItemClick(AdapterView<?> parent, 
				View view, final int pos, long id)
		{
			drawer.setDrawerListener( 
				new DrawerLayout.SimpleDrawerListener()
			{
				@Override
				public void onDrawerClosed(View drawerView)
				{
					super.onDrawerClosed(drawerView);
					FragmentTransaction tx = 
						getSupportFragmentManager()
							.beginTransaction();
					tx.replace(R.id.main, 
						Fragment.instantiate(
							MainActivity.this, 
							classes[pos]));
					tx.commit();
				}
			});
			drawer.closeDrawer(navList);
		}
	});

	FragmentTransaction tx = 
		getSupportFragmentManager().beginTransaction();
	tx.replace(R.id.main,
		Fragment.instantiate(MainActivity.this, classes[0]));
	tx.commit();
}

First we need to use a different layout for each item as this is now a ListView and not a Spinner. Next we find our DrawerLayout and ListView controls from the layout and set the Adapter to the ListView. Then we handle click events on our ListView – whenever an item is clicked, we close the drawer, wait for it to complete using a DrawerListener, and switch the Fragment once the drawer has closed. The reason for waiting for the drawer to close is to prevent stuttering during the drawer closing animation if we try and switch the Fragment at the same time. Alternatively we could switch the Fragment first, and then close the drawer, but for simplicity in this example we’ll wait for the drawer to close.

Finally we need to set set the default Fragment. The reason for this is that when we were using the Spinner, a default selection in the Spinner would be made which would trigger our OnNavigationListener to create the appropriate Fragment. In this new navigation model there is no initial event so we have to load the default Fragment manually.

That’s enough to get a basic Navigation Drawer working. If we swipe from the left hand edge of the screen our Navigation Drawer appears, and we can select the different examples:

simple

In the next article in this series we’ll look at how we can connect the Navigation Drawer up the the app icon in our ActionBar to show and hide it.

The source code for this article is available here.

© 2013, Mark Allison. All rights reserved. This article originally appeared on Styling Android.

Portions of this page are modifications based on work created and shared by Google and used according to terms described in the Creative Commons 3.0 Attribution License

Send the author to the moon!

Creative Commons License
Navigation Drawer – Part 1 by Styling Android, unless otherwise expressly stated, is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. Terms and conditions beyond the scope of this license may be available at blog.stylingandroid.com.

Tags:

20 Responses to “Navigation Drawer – Part 1”

  1. Spencer Egart says:

    There’s no way to integrate this with ActionBarSherlock yet, is there?

    • Mark Allison says:

      No, there isn’t. I suspect that ActionBarCompat will provide pre-Honeycomb support, though.

      <update>See Peter’s comment below: It appears that it works with ActionBarSherlock.</update>

  2. hugo says:

    Thanks, this works with Android API 8?

  3. Peter says:

    I tried it with Sherlock.no problems whatsoever.

    • Mark Allison says:

      Thanks for sharing that. Maybe Google realised that there were a lot of people using ABS so built in compatibility for it. That’s very good news.

  4. Kevin Tan says:

    It’s compatible with ActionBarSherlock. But I wonder how to get the subtle animated drawer icon…

    • Mark Allison says:

      The basic DrawerLayout stuff may be compatible with ABS, but I wonder whether ActionBarDrawerToggle (which is covered in next week’s article) is. I haven’t tried it yet.

  5. Miral says:

    Thanks for this! Simple. I was just about to start working on the SlidingMenu library when they updated this at the I/O.

    Works just how I want it to. Just need to get it designed correctly and get it working with ABS.

    Coming along well. Looking forward to part two!

  6. ActionbarSherlock is mostly compatible with navigation drawer. But I think there is one point where ABS and navigation drawer are not compatible, where we can open and close drawer with icon click.

    According to code
    if (mDrawerToggle.onOptionsItemSelected(item)) {
    return true;
    }
    It requires item to be android.view.MenuItem but we get com.actionbarsherlock.view.MenuItem. Which throws class cast exception.

    • Loui says:

      Hi Patrick.

      I tried this on android 2.2.2 & 2.3.3 and it doesn’t work on either. Error ‘…has stopped unexpectedly’. Does this mean I cannot run this project on devices below 2.3???

  7. Miral says:

    Does anyone know how I can add icons to the side of each List item with this code?

  8. Peter says:

    @Miral: Sure. Put it into the left Drawable of the TextView menu item.

  9. Neil says:

    To get rid of the stuttering you can call executePendingTransactions on the Fragment manager after doing fx.commit. You can then change the fragment before the drawer closes without a stutter.

  10. Anas Azeem says:

    @Miral: you have to add an Imageview in your “drawer_list_item” and then you’ll have to use a Custom List Adapter, like it is always done for a ListView control.

  11. Dana says:

    I want to use a ViewPager with tabs (The screen with tabs on top, under the action bar, that you can swipe left to right). This means that I will have to change the actionbar a bit when I switch between fragments. Would you recommend that I make separate activities and switch between them, or to keep using this model of fragmentactivities and make changes to the actionbar?

    Thanks for the wonderful tutorial and code sample.

    • Mark Allison says:

      You won’t be able to use a ViewPager if you make separate Activities. You will need to use separate Fragments for the contents of each page within the ViewPager. Each Fragment can modify the ActionBar, though. Just override onCreateOptionsMenu() in each Fragment.

  12. Jedix says:

    Hi, thanks for your article.
    Did you notice this bug ? http://www.youtube.com/watch?v=yduSWtEM4u8 (tested on Android 4.2 and 4.3 at least) This is pretty ennoying, is the DrawerLayout buggy ? Yet Google Play Music doesn’t seem to have this problem…

Leave a Reply