Creating Android Sliding Sidebar (Hamburger) Menu with Navigation Drawer Icon

By now most of us are familiar with Navigation Drawers as they’ve been in use by Facebook, Gmail, Google Play Music, Google Play Music and tons of others apps. It’s that sliding panel that comes out onto the screen when you swipe a finger from the left edge of the screen (or sometimes right edge) or tap on the 3 bar button (sometimes called hamburger icon) in the Action Bar. If still confused then check out the design guide and you’ll know what I’m referring to. The design guide will also tell you when to use it and when not to use it.

In this article I’ll go through how to create a navigation drawer (also referred to as sidebar menu, slide-out menu, sliding menu, hamburger menu, basements, etc.) for your own apps using the DrawerLayout APIs in the support library. But before getting into how to create a navigation (hamburger) drawer, you should not only go through the design guide but also read a couple of articles on how you might be making a wrong decision by using a hamburger menu and you could possibly resort to other methods to avoid it:

That’s a lot to read (but won’t take much time) and too much negativity around navigation drawers. But if you think you’re fixed on using it because it does make sense in several cases, then let’s head towards creating one on Android. Here’s a screenshot of the navigation drawer we’ll create (with an Action Bar).

android navigation drawer

Simple and Clean!

DrawerLayout ViewGroup in Your Layout

We’ll first code our layout in res/layout/activity_main.xml. In order to build a drawer, we’ll have to use a DrawerLayout as the root view of our layout which’ll contain two child ViewGroups where one of them will represent the main content, whereas the other will contain the contents of the navigation drawer.

[java]
<android.support.v4.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/drawerLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">

<!– The main content view –>
<RelativeLayout
android:id="@+id/mainContent"
android:layout_width="match_parent"
android:layout_height="match_parent" />

<!– The navigation drawer –>
<RelativeLayout
android:layout_width="280dp"
android:layout_height="match_parent"
android:id="@+id/drawerPane"
android:layout_gravity="start">

<!– Profile Box –>

<RelativeLayout
android:id="@+id/profileBox"
android:layout_width="match_parent"
android:layout_height="100dp"
android:background="@color/material_blue_grey_800"
android:padding="8dp" >

<ImageView
android:id="@+id/avatar"
android:layout_width="50dp"
android:layout_height="50dp"
android:src="@drawable/ic_launcher"
android:layout_marginTop="15dp" />

<LinearLayout
android:layout_width="wrap_content"
android:layout_height="42dp"
android:layout_centerVertical="true"
android:layout_marginLeft="15dp"
android:layout_toRightOf="@+id/avatar"
android:orientation="vertical" >

<TextView
android:id="@+id/userName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Rishabh"
android:textColor="#fff"
android:textSize="16sp"
android:textStyle="bold" />

<TextView
android:id="@+id/desc"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:layout_marginTop="4dp"
android:text="View Profile"
android:textColor="#fff"
android:textSize="12sp" />
</LinearLayout>
</RelativeLayout>

<!– List of Actions (pages) –>
<ListView
android:id="@+id/navList"
android:layout_width="280dp"
android:layout_height="match_parent"
android:layout_below="@+id/profileBox"
android:choiceMode="singleChoice"
android:background="#ffffffff" />

</RelativeLayout>

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

As you can see, we’ve our DrawerLayout root view and then its first child is a RelativeLayout that represents the main content UI whereas the second child represents the navigation drawer. The navigation drawer first contains a box to represent the profile details followed by a ListView that’ll display a list of different sections that the user can visit (Home, Preferences, About).

The ViewGroup representing the main content UI should be the first child inside the DrawerLayout as the ordering in XML defines the z-index and the drawer should overlay the content, not the other way round.

The XML code is enough to initialize a functional drawer. You can test your app in the emulator or device to start swiping your finger from the left to draw the hamburger menu. However, we’ve some more work to do like initializing the list and adding onclick events on the list items.

Populating the Drawer List

This is the first step, we’ll initialize the ListView inside the Drawer with several options. Each list option will be represented by a custom NavItem class object. So create this inner class (of the MainActivity):

[java]
class NavItem {
String mTitle;
String mSubtitle;
int mIcon;

public NavItem(String title, String subtitle, int icon) {
mTitle = title;
mSubtitle = subtitle;
mIcon = icon;
}
}
[/java]

Next we need another inner class that’ll be a simple BaseAdapter implementation that we’ll bind to the ListView for the sake of population:

[java]
class DrawerListAdapter extends BaseAdapter {

Context mContext;
ArrayList<NavItem> mNavItems;

public DrawerListAdapter(Context context, ArrayList<NavItem> navItems) {
mContext = context;
mNavItems = navItems;
}

@Override
public int getCount() {
return mNavItems.size();
}

@Override
public Object getItem(int position) {
return mNavItems.get(position);
}

@Override
public long getItemId(int position) {
return 0;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view;

if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(R.layout.drawer_item, null);
}
else {
view = convertView;
}

TextView titleView = (TextView) view.findViewById(R.id.title);
TextView subtitleView = (TextView) view.findViewById(R.id.subTitle);
ImageView iconView = (ImageView) view.findViewById(R.id.icon);

titleView.setText( mNavItems.get(position).mTitle );
subtitleView.setText( mNavItems.get(position).mSubtitle );
iconView.setImageResource(mNavItems.get(position).mIcon);

return view;
}
}
[/java]

We inflated R.layout.drawer_item, so let’s create res/layout/drawer_item.xml now:

[xml]
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="10dp"
android:paddingBottom="10dp">

<ImageView
android:id="@+id/icon"
android:layout_width="40dp"
android:layout_height="40dp"
android:src="@drawable/ic_action_home"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_marginRight="20dp"
android:layout_marginLeft="10dp"
android:layout_marginTop="5dp"
/>

<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="18sp"
android:gravity="center_vertical"
android:textColor="#000"
android:text="Line 1"
android:textStyle="bold"
android:layout_alignParentTop="true"
android:layout_toRightOf="@+id/icon"
android:layout_toEndOf="@+id/icon" />

<TextView android:id="@+id/subTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Line 2"
android:layout_toRightOf="@+id/icon"
android:layout_below="@+id/title"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true" />

</RelativeLayout>
[/xml]

Now, we’ll set the adapter for the ListView inside onCreate() of our MainActivity:

[java]
private static String TAG = MainActivity.class.getSimpleName();

ListView mDrawerList;
RelativeLayout mDrawerPane;
private ActionBarDrawerToggle mDrawerToggle;
private DrawerLayout mDrawerLayout;

ArrayList<NavItem> mNavItems = new ArrayList<NavItem>();

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

mNavItems.add(new NavItem("Home", "Meetup destination", R.drawable.ic_action_home));
mNavItems.add(new NavItem("Preferences", "Change your preferences", R.drawable.ic_action_settings));
mNavItems.add(new NavItem("About", "Get to know about us", R.drawable.ic_action_about));

// DrawerLayout
mDrawerLayout = (DrawerLayout) findViewById(R.id.drawerLayout);

// Populate the Navigtion Drawer with options
mDrawerPane = (RelativeLayout) findViewById(R.id.drawerPane);
mDrawerList = (ListView) findViewById(R.id.navList);
DrawerListAdapter adapter = new DrawerListAdapter(this, mNavItems);
mDrawerList.setAdapter(adapter);

// Drawer Item click listeners
mDrawerList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
selectItemFromDrawer(position);
}
});
}
[/java]

Our ListView will be populated now so you should see it in the navigation drawer but it’s time to write code for what will happen once a list item is clicked, which will be defined by the selectItemFromDrawer() Activity method which is what we called inside the onItemClick() method of the OnItemClickListener interface.

So when a list item is clicked, we could do several things. Since they’re different sections, we could just open different Activities using intents, insert a different Fragment for every list item into the main content view or even show dialogs. In this case we’ll keep it simple by creating a fragment called PreferencesFragment and inserting it whenever an option is selected. Although you might want to create one fragment for each option and based on the position of the clicked item, insert its respective fragement. Let’s get into some code now.

[java]
/*
* Called when a particular item from the navigation drawer
* is selected.
* */
private void selectItemFromDrawer(int position) {
Fragment fragment = new PreferencesFragment();

FragmentManager fragmentManager = getFragmentManager();
fragmentManager.beginTransaction()
.replace(R.id.mainContent, fragment)
.commit();

mDrawerList.setItemChecked(position, true);
setTitle(mNavItems.get(position).mTitle);

// Close the drawer
mDrawerLayout.closeDrawer(mDrawerPane);
}
[/java]

This is how PreferencesFragement.java looks like:

[java]
public class PreferencesFragment extends Fragment {

public PreferencesFragment() {
// Required empty public constructor
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_preferences, container, false);
}

}
[/java]

and here’s the res/layout/fragment_preferences.xml:

[java]
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent" tools:context="com.looper.loop.PreferencesFragment">

<!– TODO: Update blank fragment layout –>
<TextView android:layout_width="match_parent" android:layout_height="match_parent"
android:text="@string/hello_blank_fragment" />

</FrameLayout>
[/java]

It’s time to test your app in the emulator or your device itself. You’ll see a nice navigation drawer when you swipe right from the left edge with the profile box and the list in which you can select various options which’ll reflect in the action bar title because of this piece of code inside selectItemFromDrawer():

[java]
setTitle(mNavItems.get(position).mTitle);
[/java]

Observe Open/Close Events of the Drawer

You can listen for the drawer’s open and close events by implementing DrawerLayout.DrawerListener whose onDrawerOpened() and onDrawerClosed() will be called. But if you’re making use of the action bar, then extending ActionBarDrawerToggle is a better idea. The ActionBarDrawerToggle implements DrawerLayout.DrawerListener, so you’ll have access to those callbacks but apart from that it also makes sure that the state of both the action bar icon (we’ll see how to add that in a bit) and the drawer is synced. Let’s see some code now.

[java]
mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout, R.string.drawer_open, R.string.drawer_close) {
@Override
public void onDrawerOpened(View drawerView) {
super.onDrawerOpened(drawerView);

invalidateOptionsMenu();
}

@Override
public void onDrawerClosed(View drawerView) {
super.onDrawerClosed(drawerView);
Log.d(TAG, "onDrawerClosed: " + getTitle());

invalidateOptionsMenu();
}
};

mDrawerLayout.setDrawerListener(mDrawerToggle);
[/java]

The four arguments passed to the ActionBarDrawerToggle constructor are:

  • this – Activity object containing the drawer with an ActionBar.
  • mDrawerLayout – The DrawerLayout to link to the Activity’s ActionBar.
  • R.string.drawer_open – String resource to describe the “open drawer” action for accessibility purpose.
  • R.string.drawer_close – String resource to describe the “close drawer” action for accessibility purpose.

There’s another version of this constructor that you should use when not using an ActionBar but a ToolBar.

You’ll notice in the callbacks we called invalidateOptionsMenu(). This method declares that the option menu has changed and hence should be recreated invoking onPrepareOptionsMenu(). So basically you might want to override onPrepareOptionsMenu() to get rid of options from your overflow menu that are irrelevant to your navigation drawer.

[java]
// Called when invalidateOptionsMenu() is invoked
public boolean onPrepareOptionsMenu(Menu menu) {
// If the nav drawer is open, hide action items related to the content view
boolean drawerOpen = mDrawerLayout.isDrawerOpen(mDrawerList);
menu.findItem(R.id.action_search).setVisible(!drawerOpen);
return super.onPrepareOptionsMenu(menu);
}
[/java]

Showing Up the Hamburger Menu Icon/Indicator

According to the design guidelines, apart from the touch gesture, when the drawer is closed the user should be able to open it by touching the navigation drawer indicator (those 3 lines) in the action bar. So now we’ll see how to display that special icon.

First, add this piece of code inside onCreate():

[java]
// More info: https://kgs.hfl.temporary.site/website_d3afa00c/difference-between-setdisplayhomeasupenabled-sethomebuttonenabled-and-setdisplayshowhomeenabled/
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
[/java]

This piece of code will start showing up an arrow which is generally used for “up” (ancestral) navigation. Now when the user taps on this, onOptionsItemSelected() will be called where you can delegate the event to the ActionBarDrawerToggle.

[java]
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Pass the event to ActionBarDrawerToggle
// If it returns true, then it has handled
// the nav drawer indicator touch event
if (mDrawerToggle.onOptionsItemSelected(item)) {
return true;
}

// Handle your other action bar items…

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

Now you’ll see the back arrow on which you can tap to open/close the drawer. But it won’t change to the 3 bar indicator icon when the drawer is closed. So in order to do that we’ve to override the onPostCreate() method of the Activity where we’ll call the syncState() method of the ActionBarDrawerToggle. This method syncs the state of the indicator with the drawer. So when the drawer is closed it’ll show the correct indicator (indicating tap to open) while when the drawer is open it’ll show the back arrow indicator (indicating tap to close).

[java]
@Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
mDrawerToggle.syncState();
}
[/java]

With that, its time to test your app that is baked with a navigation drawer now.

Conclusion

So we went through how to use the DrawerLayout ViewGroup to create a navigation drawer and then populate that with a list of navigation options. Then we bounded click events to the list items that would replace the main content UI with a particular fragment. We also learnt how to listen to close/open events and show a navigation drawer indicator while using an action bar. You can also definitely make use of a Toolbar in place of an action bar and bind that to ActionBarDrawerToggle.

I don’t think by default the DrawerLayout supports sliding over the action bar which is what the material design guideline specify. So doing that might be a good exercise for you along with using custom images for the drawer icon/indicator in the action bar. If you give any of them a shot, then feel free to share your code in the comments section below!

Author: Rishabh

Rishabh is a full stack web and mobile developer from India. Follow me on Twitter.