Adapter / ArrayAdapter / DrawerLayout

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:



    
    

    


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 adapter = 
		new ArrayAdapter(
			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 – 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.

22 Comments

    1. 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>

    1. 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.

    1. 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.

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

  2. 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.

    1. 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???

  3. 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.

  4. @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.

  5. 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.

    1. 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.

  6. Hi,
    First off, thanks for a nice article.
    I had set up my navigation drawer with listview (custom row layout). But I am not able to scroll through the listview. The listview appears with an imageview and textview but I am not able to scroll the list which is inside the navigation drawer layout. Also I would like to know if I can change the icon of navigation drawer at the top (action bar) to the right side of the screen ? Thanks in advance

    1. Without looking at your code it is almost impossible to say why your ListView does not scroll – and I simply do not have the bandwidth to provide free developer support. I would suggest asking the good folks over at stackoverflow.com who may be able to help you.

      With regards the ActionBar icon that is set by calling the setLogo() method – although perhaps it would be better to think about migrating to Toolbar instead of ActionBar as that looks to be the way things are going – watch for future articles on that.

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.