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:

What's the one thing every developer wants? More screens! Enhance your coding experience with an external monitor to increase screen real estate.

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.

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

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):

class NavItem {
    String mTitle;
    String mSubtitle;
    int mIcon;

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

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:

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;
    }
}

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

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

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

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);
        }
    });
}

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.

/*
* 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);
}

This is how PreferencesFragement.java looks like:

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);
    }


}

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

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

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():

setTitle(mNavItems.get(position).mTitle);

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.

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);

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.

// 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);
}

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():

// More info: http://codetheory.in/difference-between-setdisplayhomeasupenabled-sethomebuttonenabled-and-setdisplayshowhomeenabled/
getSupportActionBar().setDisplayHomeAsUpEnabled(true);

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.

@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);
}

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

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

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!

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download

Author: Rishabh

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

15 thoughts on “Creating Android Sliding Sidebar (Hamburger) Menu with Navigation Drawer Icon”

  1. Hi, i was trying to implement your solution but i got an error on this line “view = inflater.inflate(R.layout.drawer_item, null);”. Cannot resolve R.layout.drawer_item

    Do you have any idea?, by the way pretty good article!!

    1. Hey Luis,

      Thanks for pointing that out. I’ve added the code for res/layout/drawer_item.xml. Just create that file and shove in the code and you’ll be good to go 🙂

      Cheers!

  2. Hi,i’ve got this error when i try to execute…
    java.lang.ClassCastException: android.widget.RelativeLayout$LayoutParams cannot be cast to android.support.v4.widget.DrawerLayout$LayoutParams
    How can i solve?
    Thanks 🙂

    1. Ok solved…i ‘ve done a mistake!
      I’ve put mDrawerList instead of mDrawerPane …now it works 🙂 …maybe u have to correct ur code 🙂
      How ever thanks for ur example!
      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_settings).setVisible(!drawerOpen);
      return super.onPrepareOptionsMenu(menu);
      }

  3. There is another thing that is incorrect
    mDrawerToggle miss a parameter,the icon from resource.drawable…now is complete 🙂
    mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout,R.drawable.abc_ic_go, R.string.hello_world, R.string.app_name) {…}

    1. Pastebin your code and let me know where exactly do you get the NullPointer Exception ? If you follow the article step by step you probably shouldn’t get that Exception.

  4. Hi, in the last part of my MainActivity, i add the code as you describe in the tutorial, but i get this error: Error:(154, 70) error: incompatible types: PreferencesFragment cannot be converted to Fragment in the line

    Line: fragmentManager.beginTransaction().replace(R.id.mainContent, fragment).commit();

    Can you please tell me if you know how to fix?

    Regards
    Brus

    1. Hi,

      Are you sure you created a PreferencesFragment class that extends Fragment ? If yes then I’m unable to understand why that error would be thrown. Maybe try StackOverflow and see if someone can help you out.

      Also note you could use multiple fragments instead of just one PreferencesFragment for different items in the list. I used only once just for the sake of simplicity as my main objective is to show how to build the navigation drawer. Also based on the position you could set some Bundle data/arguments and then set layouts accordingly in the Fragment’s onCreateView method.

  5. Hey . Thank u for the guide it was useful
    There is a very one thing that I wanna know
    how is it possible to open the sliding menu by tapping the menu button on the phone?
    would u pls help me with it?
    thnx in advance

  6. hey, i want to a little discuss wiht you. i follow your tutorial.
    i adding some component in activity_main. why component in activity)main visible when i show fagment activity ?
    thanks before, and sorry for my bad englis 😀

Leave a Reply

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