Navigation drawer in Android
Android now includes a component that makes it very easy to add a navigation drawer to an app that the user can slide in from the side. I first remember seeing this in the Facebook app in iOS, and I thought it was a pretty nifty mobile design pattern (though others some people strongly disagree). I will assume you are here because you think it is good for your app, though!
In Android this component is called the navigation drawer. The Android design guidelines have a nice section on when and how we should use the navigation drawer in an app. One of my favorite uses if for power-user features that might be confusing or overwhelming for infrequent users of the app, but that are nice to have easily accessible in a menu that can be pulled in from the side.
Open or Create a Project
If you care to follow along, clone or download my super simple starter app from GitHub: OS List. (The finished source is available at the end of this article.) All you need is an Activity, though. This app will target Android 4.0 and above, but the support library that brings us the Navigation Drawer makes this available on even older versions.
Your project may already contain the required dependency to use the Android support library, but if not, add the following line to the dependencies section in the build.gradle file for your app module:
compile 'com.android.support:appcompat-v7:21.0.3'
(You will probably want to use the latest version of the library.)
Update the Layout
The first thing we need to do is modify the layout for our Activity. We simply need to provide a regular layout for the Activity plus a separate layout for the navigation drawer, but both layouts are contained inside the same container: DrawerLayout.
In OS List, we have a simple RelativeLayout with a TextView and ImageView. To add a navigation drawer, we add a new root element of type
android.support.v4.widget.DrawLayout
.<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"> </android.support.v4.widget.DrawerLayout>
Then we add a ListView for the side drawer view:
<ListView android:id="@+id/navList" android:layout_width="200dp" android:layout_height="match_parent" android:layout_gravity="left|start" android:background="#ffeeeeee"/>
The ListView (or whatever View we use as the side navigation drawer), should be no more than 320dp wide, so that it does not cover the entire Activity when it is out.
Set the List and Adapter in the Activity
Now that we have a ListView to work with, let’s set it in the Activity. In this example we will use a simple String array as our data set for the list, and an ArrayAdapter to adapt the data. (For more information about using lists and adapters, check out Android Lists and Adapters in the Treehouse library.)
First, let’s add two member variables to our Activity:
private ListView mDrawerList; private ArrayAdapter<String> mAdapter;
Then we can set the ListView in
onCreate()
:mDrawerList = (ListView)findViewById(R.id.navList);
To add items and configure the list, let’s create a helper method that we can call inside
onCreate()
:private void addDrawerItems() { String[] osArray = { "Android", "iOS", "Windows", "OS X", "Linux" }; mAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, osArray); mDrawerList.setAdapter(mAdapter); }
At this point we have a working navigation drawer! Hooray!
We can perform an action when each item in the side navigation list is tapped just like we would for a regular ListView. Let’s set an OnItemClickListener and simply make a Toast about each item in the list:
mDrawerList.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { Toast.makeText(MainActivity.this, "Time for an upgrade!", Toast.LENGTH_SHORT).show(); } });
This is the minimum basic code we need for a navigation drawer, but there is much more we can do!
Add a Toggle Switch in the Action Bar
Opinions may vary on this, but I personally prefer a hamburger menu or some kind of icon in the Action Bar that lets me know the navigation drawer is there. We can use the default icon provided by Android with a few more changes in our Activity. We can show it by adding two lines in our
onCreate()
method (though it won’t do anything yet):getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setHomeButtonEnabled(true);
To get this to work correctly, we need to configure something called anActionBarDrawerToggle.
Let’s start with a few new member variable for this new object. We need one for the toggle, one for the DrawerLayout we added to our layout, and a String that we will use to update the title in the Action Bar:
private ActionBarDrawerToggle mDrawerToggle; private DrawerLayout mDrawerLayout; private String mActivityTitle;
Before we forget, let’s set
mDrawerLayout
and mActivityTitle
in onCreate()
:mDrawerLayout = (DrawerLayout)findViewById(R.id.drawer_layout); mActivityTitle = getTitle().toString();
Using a toggle requires two String resources, so let’s add those in
strings.xml
in the res/values
directory before we proceed:<string name="drawer_open">Open navigation drawer</string> <string name="drawer_close">Close navigation drawer</string>
Okay! Now we can add a new helper method that we will call from
onCreate()
after the drawer items have been set up:private void setupDrawer() { }
Here is where we will initialize
mDrawerToggle
. Autocomplete is really helpful here; or just copy and paste from below. We want to create a new ActionBarDrawerToggle instance that uses the context, mDrawerLayout
, and those two new string resources we added, and then we need to implement two methods:onDrawerOpened()
and onDrawerClosed()
:mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout, R.string.drawer_open, R.string.drawer_close) { /** Called when a drawer has settled in a completely open state. */ public void onDrawerOpened(View drawerView) { } /** Called when a drawer has settled in a completely closed state. */ public void onDrawerClosed(View view) { } };
When the drawer is opened, we can set a new title for the Action Bar (if we want). We also want to invalidate the options menu in case it needs to be recreated with different options for when the navigation drawer is open:
public void onDrawerOpened(View drawerView) { super.onDrawerOpened(drawerView); getSupportActionBar().setTitle("Navigation!"); invalidateOptionsMenu(); // creates call to onPrepareOptionsMenu() }
And then when it is closed we will do the opposite. Revert the title (which we stored in
mActivityTitle
) and again invalidate the options menu:public void onDrawerClosed(View view) { super.onDrawerClosed(view); getSupportActionBar().setTitle(mActivityTitle); invalidateOptionsMenu(); // creates call to onPrepareOptionsMenu() }
At this point our toggle still won’t work. We need to add two more lines to enable the drawer indicator (the lovely hamburger menu) and then attach this new toggle object to our drawer layout. Add these two lines in
setupDrawer()
after mDrawerToggle
is set:mDrawerToggle.setDrawerIndicatorEnabled(true); mDrawerLayout.setDrawerListener(mDrawerToggle);
Finally, we need to activate this in the
onOptionsItemSelected()
method. Add this block of code anywhere in the method:if (mDrawerToggle.onOptionsItemSelected(item)) { return true; }
Keep Everything in Sync
Notice in the animated gif above that the icon isn’t quite in sync with the drawer. We need to call one more method in the
onPostCreate()
lifecycle method of our Activity. Inside onPostCreate()
, call mDrawerToggle.syncState()
to sync the indicator to match the current state of the navigation drawer:@Override protected void onPostCreate(Bundle savedInstanceState) { super.onPostCreate(savedInstanceState); mDrawerToggle.syncState(); }
The other scenario we need to plan for to keep things in sync is when the configuration of the Activity changes, like, for example, going from portrait to landscape or showing the soft keyboard on the screen. We do this in the Activity’s
onConfigurationChanged()
method, which you may need to add:@Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); mDrawerToggle.onConfigurationChanged(newConfig); }
No comments: