Android View Tutorial - Navigation Drawer
Quick Links

A common building block of Applications and Websites which follow Google’s Material Design standard, is the Navigation Drawer. There are other ways of allowing users to navigate through your App’s hierarchy, such as a TabLayout, but I would argue that the NavDrawer has one distinct advantage: It allows the user to decide when it is visible, thus hogging screen real-estate only when it needs to. Yeah, the difference probably won’t make or break an App unless you’re a real design freak (which I’m not), but it’s certainly worth taking into account.

For this first lesson, we’ll be building  a very simple Navigation Drawer implementation. Once we’re comfortable with the basics, I’ll show you a couple more complicated variations in the next lesson; as well as some design tips.

As with all my tutorials, I’ll be assuming that you are working in Android Studio. While there’s no reason that you shouldn’t be able to follow along in a different IDE, I would strongly suggest making the switch unless you have a good reason not to.

Basic Navigation Drawer

Check out the video here.
1. Create a new project called NavDrawerDemo; or whatever you’d like. We’ll be using the v7 support library, but I’ve set my minSDKVersion to 16, which covers us for most devices.  Let AS generate a new “Empty Activity” called MainActivity, and a layout called activity_main.xml. 2. We’ll be adding a few things to activity_main.xml, so go ahead and open it up. Make the root element a DrawerLayout, and add a TextView and a RecyclerView as child elements. As mentioned previously, I would suggest using the v7 support implementation for the DrawerLayout and RecyclerView. As such you may need to add the following libs to you “app” level builde.gradle file, in the dependencies block (check the video if confused):

compile 'com.android.support:appcompat-v7:24.2.0'
compile 'com.android.support:recyclerview-v7:24.2.0'

Obviously you may need to adjust for the appropriate versions, which AS usually suggest automatically.

Here’s what activity_main.xml should look like:

 
	?xml version="1.0" encoding="utf-8"?>
 <android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/nvd_act_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"

    tools:context="com.bracketcove.navdrawerdemo.MainActivity">

     <!-- The lower down an element is in the layout file, the higher it is on the Z axis. Therefore
    we should place the content of the Activity (cont_main_content) above the content of the Navigation Drawer, so that
     our Drawer on top (lst_nav_drawer)-->

     <FrameLayout
        android:id="@+id/cont_main_content"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        >
         <TextView
            android:id="@+id/lbl_act_main"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:text="Hello World!" />
     </FrameLayout>

     <!-- The element below is what we can consider as our Navigation Drawer itself. You can decide
    if the drawer pops out on the left or right via "layout_gravity" -->
     <android.support.v7.widget.RecyclerView
        android:id="@+id/lst_nav_drawer"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        app:layoutManager="android.support.v7.widget.LinearLayoutManager"
        android:layout_width="240dp"
        android:layout_height="match_parent"
        android:layout_gravity="start|left"
        android:background="@android:color/white"
        />
 </android.support.v4.widget.DrawerLayout>

3. Let’s get our RecyclerView’s s**t out of the way. Setting up a RecyclerView for the first time is quite the endeavor in and of itself, so feel free to check out my [INSERT TUTORIAL LINK] for a more in depth explanation.

Create item_navigation_list.xml:

 
	
 <?xml version="1.0" encoding="utf-8"?>
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal" android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:id="@+id/cont_nav_item_root"
    >

     <android.support.v7.widget.AppCompatButton
        android:id="@+id/btn_nav_item"
        android:layout_width="match_parent"
        android:layout_height="64dp"
        android:gravity="center"
        android:textColor="@android:color/black"
        android:background="?attr/selectableItemBackground"
        android:paddingLeft="32dp"
        android:paddingRight="32dp" />

 </LinearLayout>

Create NavigationListAdapter.java:

 
	/**
 * Adapter class for Globally used Navigation Drawer.
 * Created by Ryan on 18/09/2015.
 */
public class NavigationListAdapter extends RecyclerView.Adapter <NavigationListAdapter.ViewHolder> {
    private LayoutInflater inflater;
    private ArrayList <Drawable> icons;
    private ArrayList <String> labels;
    private OnItemClickListener mItemClickListener;

    public NavigationListAdapter(Context context, ArrayList <Drawable> icons, ArrayList <String> labels) {
        inflater = LayoutInflater.from(context);
        this.icons = icons;
        this.labels = labels;
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = inflater.inflate(R.layout.item_navigation_list, parent, false);
        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(NavigationListAdapter.ViewHolder holder, final int position) {
        holder.navitem.setText(labels.get(position));
        //The following line will draw an icon on the left side of the Button's text.
        //This is simply to avoid using an extra view for no reason.
        holder.navitem.setCompoundDrawablesWithIntrinsicBounds(icons.get(position), null, null, null);

    }

    @Override
    public int getItemCount() {
        return icons.size();
    }

    class ViewHolder extends RecyclerView.ViewHolder {

        // ViewGroup container;
        AppCompatButton navitem;

        public ViewHolder(View itemView) {
            super(itemView);

            //container = (ViewGroup) itemView.findViewById(R.id.nav_list_container);
            navitem = (AppCompatButton) itemView.findViewById(R.id.btn_nav_item);
            navitem.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    mItemClickListener.onNavigationItemClick(getAdapterPosition());
                }
            });
        }
    }

    public interface OnItemClickListener {
        void onNavigationItemClick(int position);
    }

    public void setOnClickListener(final OnItemClickListener mItemClickListener) {
        this.mItemClickListener = mItemClickListener;
    }
}

4. Finally, we’ll put together our code in ActivityMain.java. Since it’s pretty much a waste of time to actually create two more activities to navigate to, we’ll simply be setting the text to demonstrate that our clicks in the NavDrawer are behaving as they should. That being said, I left a code snippet commented out to show you the commands to open another Activity, for you noobs out there <3.

Also note that I’ve downloaded a couple of icons from here. If you don’t want to bother, use type in android.R.drawable.ic_delete or something like that.

Navigation Drawer w/ Toolbar Toggle Icon

Check out the video here.
In this lesson, we’ll be looking at implementing two variations for our Navigation Drawer:

  • Setting up a Toolbar with Icons to open/close the Drawer
  • Implementing Android's fancy NavigationView

Since you may wish to pick and choose which of these variations you’d like to use (unintentional rapping), we’ll create a seperate Activity for each variation. Also note that we’ll be continuing on in terms of code, from the previous lesson for a Basic Navigation Drawer.

Lastly, I’ll be using a couple icons downloaded from here. You’ll need to replace the resources appropriately, otherwise your App won’t build properly.

1. Our first variation should be pretty quick. Let’s prepare our Toolbar by creating a new file in res/layout called toolbar.xml.

toolbar.xml:

 
	 <?xml version="1.0" encoding="utf-8"?>
	 <android.support.v7.widget.Toolbar xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@color/colorPrimary" />

We’ll also need to make a tweak to our styles.xml file. Open up styles.xml (or create one if in res/values) and make the Apps base theme inherit from one of the NoActionBar themes. I’ve chosen “Theme.Appcompat.NoActionBar” for example.

2.  We’ll need to make some changes to activity_main.xml. Since we wish to make our NavDrawer open below our Toolbar (in this variation at least), we’ll need to encapsulate them both in either a LinearLayout or RelativeLayout which I’ll be using. We’ll also need to add our Toolbar of course.

activity_main.xml:

 
	<?xml version="1.0" encoding="utf-8"?>
 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/cont_act_main_root"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.bracketcove.navdrawerdemo.MainActivity">

     <include
        android:id="@+id/app_bar"
        layout="@layout/toolbar"/>

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

         <!-- The lower down an element is in the layout file, the higher it is on the Z axis. Therefore
        we should place the content of the Activity (cont_main_content) above the content of the Navigation Drawer, so that
         our Drawer on top (lst_nav_drawer)-->
         <FrameLayout
            android:id="@+id/cont_main_content"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

             <TextView
                android:id="@+id/lbl_act_main"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:text="Hello World!" />
         </FrameLayout>

         <!-- The element below is what we can consider as our Navigation Drawer itself. You can decide
        if the drawer pops out on the left or right via "layout_gravity" -->
         <android.support.v7.widget.RecyclerView xmlns:app="http://schemas.android.com/apk/res-auto"
            android:id="@+id/lst_nav_drawer"
            android:layout_width="240dp"
            android:layout_height="match_parent"
            android:layout_gravity="start|left"
            android:background="@android:color/white"
            app:layoutManager="android.support.v7.widget.LinearLayoutManager" />
     </android.support.v4.widget.DrawerLayout>
 </RelativeLayout>

3. Finally, we need to update our MainActivity.java in order to have our Toolbar and Drawer function correctly. Lots of boilerplate code here, but don’t get flustered. MainActivity.java:

 
	public class MainActivity extends AppCompatActivity implements NavigationListAdapter.OnItemClickListener {

    private ArrayList <Drawable> icons;
    private ArrayList <String> labels;
    private DrawerLayout navDrawer;
    private RecyclerView navList;
    private TextView title;
    private Toolbar toolbar;
    private ActionBarDrawerToggle toggle;

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

        toolbar = (Toolbar) findViewById(R.id.app_bar);
        setSupportActionBar(toolbar);
        title = (TextView) findViewById(R.id.lbl_act_main);

        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
        getSupportActionBar().setHomeButtonEnabled(true);

        icons = new ArrayList <>();
        icons.add(ContextCompat.getDrawable(this, R.drawable.ic_dashboard_black_24dp));
        icons.add(ContextCompat.getDrawable(this, R.drawable.ic_event_black_24dp));
        icons.add(ContextCompat.getDrawable(this, R.drawable.ic_settings_black_24dp));

        //For brevity's sake, resources and strings are hard coded. These should come values instead.
        labels = new ArrayList <>();
        labels.add("Dashboard");
        labels.add("Calendar");
        labels.add("Preferences");

        setUpNavDrawer(icons, labels);
    }

    private void setUpNavDrawer(ArrayList <Drawable> icons, ArrayList <String> labels) {
        navDrawer = (DrawerLayout) findViewById(R.id.nvd_act_main);
        NavigationListAdapter adapter = new NavigationListAdapter(this, icons, labels);
        adapter.setOnClickListener(this);

        navList = (RecyclerView) findViewById(R.id.lst_nav_drawer);
        navList.setAdapter(adapter);

        toggle = new ActionBarDrawerToggle(
                this,                 
                navDrawer,         
                toolbar,  
                R.string.open,  
                R.string.close  
        ) {

            public void onDrawerClosed(View drawer) {
                super.onDrawerClosed(drawer);
                invalidateOptionsMenu();
            }

            public void onDrawerOpened(View drawer) {
                super.onDrawerOpened(drawer);
                invalidateOptionsMenu();
            }
        };
        navDrawer.addDrawerListener(toggle);
    }

    @Override
    protected void onPostCreate(Bundle savedInstanceState) {
        super.onPostCreate(savedInstanceState);
        // Sync the toggle state after onRestoreInstanceState has occurred.
        toggle.syncState();
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        toggle.onConfigurationChanged(newConfig);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Pass the event to ActionBarDrawerToggle, if it returns
        // true, then it has handled the app icon touch event
        if (toggle.onOptionsItemSelected(item)) {
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    /**
     * Updates the textview relative to the position of the List item which was clicked.
     *
     * @param position
     */
    @Override
    public void onNavigationItemClick(int position) {
        switch (position) {
            case 0:
                title.setText("Dashboard Activity");
                break;
            case 1:
                title.setText("Calendar Activity");
                break;
            case 2:
                title.setText("Preference Activity");
                break;
        }
    }
}

NavigationDrawer w/ NavigationView

Check out the video here. 1.  Instead of creating a new Activity/Project, we’ll simply modify our existing files. We’ll start by adding a new lib to our app level build.gradle file. In the dependecies section, add (accounting for version changes of course):

compile 'com.android.support:design:24.2.0'

2. Open up activity_main.xml and replace the RecyclerView (the last child of our DrawerLayout) with the following element:

 
	<android.support.design.widget.NavigationView
            android:id="@+id/nav_view"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_gravity="start"
            app:headerLayout="@layout/header_nav_item"
            app:menu="@menu/menu_items"
             />

3. Naturally, we’ll need to make a couple new resources. As opposed to a RecyclerView which has its data handled by an Adapter, we’ll be using an xml Menu resource to supply the icons and titles for each item in our NavigationView. Create a new Android Resource Directory called menu. Within that folder, create a menu resource file called menu_items.xml:

 
	<?xml version="1.0" encoding="utf-8"?>
 <menu xmlns:android="http://schemas.android.com/apk/res/android">
     <group
        android:id="@+id/group_one"
        android:checkableBehavior="single">
         <item
            android:id="@+id/nav_item_dash"
            android:icon="@drawable/ic_dashboard_black_24dp"
            android:title="Dashboard" />

         <item
            android:id="@+id/nav_item_cal"
            android:icon="@drawable/ic_event_black_24dp"
            android:title="Calendar" />

     </group>
     <group
        android:id="@+id/group_two"
        android:checkableBehavior="single">
         <item
            android:id="@+id/nav_item_prefs"
            android:icon="@drawable/ic_settings_black_24dp"
            android:title="Preferences" />
     </group>
 </menu>

We’ll also need a header for our NavigationView. In res/layout, create header_nav_item.xml:

 
	<?xml version="1.0" encoding="utf-8"?>
 <LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_height="wrap_content"
    android:layout_width="match_parent"
    android:orientation="vertical"
    android:background="@color/colorPrimary"
    android:padding="16dp"
    >
     <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab_header"
        android:layout_width="match_parent"
        android:layout_height="64dp"
        android:src="@android:drawable/ic_input_get"
        android:background="@color/colorPrimary"
        />

     <TextView
        android:id="@+id/lbl_header"
        android:text="Header Text"
        android:layout_height="wrap_content"
        android:layout_width="match_parent"
        android:background="@color/colorPrimary"
        android:textColor="@android:color/white"
        android:gravity="center"
        />
 </LinearLayout>

4. Now that our xml stuff is out of the way, we’ll repurpose MainActivity.java for our new set up. This pretty much means deleting our RecyclerView/Adapter sh*t, adding a new listener, and fixing onNavigationItemClick() to accept view id for our menu items, instead of going by position.

MainActivity.java:

 
	public class MainActivity extends AppCompatActivity {

    private DrawerLayout navDrawer;
    private TextView title;
    private Toolbar toolbar;
    private ActionBarDrawerToggle toggle;
    private NavigationView navView;

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

        toolbar = (Toolbar) findViewById(R.id.app_bar);
        setSupportActionBar(toolbar);
        title = (TextView) findViewById(R.id.lbl_act_main);

        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
        getSupportActionBar().setHomeButtonEnabled(true);

        setUpNavDrawer();
    }

    private void setUpNavDrawer() {
        navDrawer = (DrawerLayout) findViewById(R.id.nvd_act_main);
        navView = (NavigationView) findViewById(R.id.nav_view);
        navView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
            @Override
            public boolean onNavigationItemSelected(@NonNull MenuItem item) {
                item.setChecked(true);
                navDrawer.closeDrawers();
                onNavigationItemClick(item.getItemId());
                return true;
            }
        });

        toggle = new ActionBarDrawerToggle(
                this,                  /* host Activity */
                navDrawer,         /* DrawerLayout object */
                toolbar,  /* nav drawer icon to replace 'Up' caret */
                R.string.open,  /* "open drawer" description */
                R.string.close  /* "close drawer" description */
        ) {
            /** Called when a drawer has settled in a completely closed state. */
            public void onDrawerClosed(View drawer) {
                super.onDrawerClosed(drawer);
                invalidateOptionsMenu();
            }

            /** Called when a drawer has settled in a completely open state. */
            public void onDrawerOpened(View drawer) {
                super.onDrawerOpened(drawer);
                invalidateOptionsMenu();
            }
        };
        navDrawer.addDrawerListener(toggle);
    }

    @Override
    protected void onPostCreate(Bundle savedInstanceState) {
        super.onPostCreate(savedInstanceState);
        // Sync the toggle state after onRestoreInstanceState has occurred.
        toggle.syncState();
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        toggle.onConfigurationChanged(newConfig);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Pass the event to ActionBarDrawerToggle, if it returns
        // true, then it has handled the app icon touch event
        if (toggle.onOptionsItemSelected(item)) {
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    /**
     * Updates the textview relative to the position of the List item which was clicked.
     * @param id
     */
    public void onNavigationItemClick(int id) {
        switch (id) {
            case R.id.nav_item_dash:
                title.setText("Dashboard Activity");
                break;
            case R.id.nav_item_cal:
                title.setText("Calendar Activity");
                break;
            case R.id.nav_item_prefs:
                title.setText("Preference Activity");
                break;
        }
    }
}