Adding a Navigation Drawer to Our Android Apps

Standard

 

Many current applications make use of a navigation drawer (ND) to easily navigate through the whole app. But, what is a ND? Its very likely you’ve seen one of them at least once in an android application. If you haven’t, here is an example.


ND items (also known as navigation items) may be added either dynamically or statically. In this example we’ll make use of static items described in an XML file, representing options and sub-options contained within the drawer menu. In this file we could also attach an image to options, giving them more sense.

Additionally, UI elements called from the drawer could be activities or fragments. Fragments are of particular use in some cases like where we want to keep a ND in all our pages. We can inflate a frame layout with whatever fragment we need and still have access to the ND. If we opt for using activities, we have to keep the ND in all activities, which results in redundant code. Thus, we can use as many fragments as we need inside a single activity, saving both code complexity and UI processing.

So, let’s get started. This guide tries to explain how to setup a basic ND containing static navigation items. Each of these navigation items switch to different fragments into a content area (a special area within an activity designed to show a fragment). With this logic, we can define multiple fragments, and then define an XML file containing navigation items which will be displayed is the ND. Each item when clicked will switch the relevant fragment into the activity’s container view.


Downloads

In order to follow this guide, we’ll need three icons to populate our ND items. After download, we need to save them in our drawable folder by copying and pasting (the easy way).

We also need the Google’s official menu icon for this guide. We can get it from here.


 

Setup Drawer Resources

Let’s create a file in menu/drawer_view.xml with the next content. This file will represent our navigation items.

<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <group android:checkableBehavior="single">
        <item
            android:id="@+id/nav_first_fragment"
            android:icon="@drawable/ic_one"
            android:title="First fragment" />
        <item
            android:id="@+id/nav_second_fragment"
            android:icon="@drawable/ic_two"
            android:title="Second fragment" />
        <item
            android:id="@+id/nav_third_fragment"
            android:icon="@drawable/ic_three"
            android:title="Third fragment" />
    </group>
</menu>

android:id – the identifier of an specific navigation item.
android:icon – the icon that will be displayed within the navigation item.
android:title – the description shown in the navigation item.

Note that if we need sub-items, we can easily add them the next way:

<item android:title="Sub items">
        <menu>
            <item
                android:icon="@drawable/ic_one"
                android:title="Sub item 1" />
            <item
                android:icon="@drawable/ic_two"
                android:title="Sub item 2" />
        </menu>
</item>

subitems


 

Creating fragments

The next step is to define our fragments that will be displayed within the ND. We need to make sure that all fragments extend from android.support.v4.app.Fragment.

Setup ToolBar

In order to slide our ND over the ActionBar, we need to use the new Toolbar widget as defined in the AppCompat v21 library. The Toolbar can be embedded into your view hierarchy which makes sure that the drawer slides over the ActionBar.

So let’s create a new file in res/layout/toolbar.xml with the following code:

<android.support.v7.widget.Toolbar
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/toolbar"
    android:layout_height="wrap_content"
    android:layout_width="match_parent"
    android:fitsSystemWindows="true"
    android:minHeight="?attr/actionBarSize"
    app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
    android:background="?attr/colorPrimaryDark">
</android.support.v7.widget.Toolbar>

We want our main content view to have the navigation bar and hence android:fitsSystemWindows is set to true for the Toolbar, otherwise it would look like this:

To use the Toolbar as an ActionBar, we need to disable the default ActionBar. This can be easily done by setting the app theme in styles.xml file as follows:

<resources>
    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <item name="colorPrimary">#673AB7</item>
        <item name="colorPrimaryDark">#512DA8</item>
        <item name="colorAccent">#FF4081</item>
    </style>
</resources>

If we don’t like the current colors, we can make use of Material Palette to choose a primary and dark primary colors. For this guide, we will pick purple-based colors as shown in screenshots.

Setup ND in Main Activity

Next, let’s setup our basic ND based in the following layout file in res/layout/activity_main.xml which has the entire drawer setup.

<android.support.v4.widget.DrawerLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
        <!-- The ActionBar -->
        <include
            layout="@layout/toolbar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />
        <!-- The main content view -->
        <FrameLayout
            android:id="@+id/flContent"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    </LinearLayout>
    <!-- The navigation drawer -->
    <android.support.design.widget.NavigationView
        android:id="@+id/nvView"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:background="@android:color/white"
        app:menu="@menu/drawer_view" />
</android.support.v4.widget.DrawerLayout>

We must highlight three things here:

  • Our custom Toolbar is included as the first child of the main content view using the include tag.
  • We use a FrameLayout as a content layout. This will be used to display the different fragments we have.
  • the ND object has the app:menu attribute set to our drawer_view.xml file, which contains our static options.

Don’t forget to add the Gradle dependency support and sync the project.

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

 

Now let’s setup the drawer in our main activity.

class MainActivity extends AppCompatActivity {
	private DrawerLayout mDrawer;
	private Toolbar toolbar;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		// Set a Toolbar to replace the ActionBar.
		toolbar = (Toolbar) findViewById(R.id.toolbar);
		setSupportActionBar(toolbar);
		// Find our drawer view
		mDrawer = (DrawerLayout) findViewById(R.id.drawer_layout);
		// Set the menu icon instead of the launcher icon.
		final ActionBar ab = getSupportActionBar();
		ab.setHomeAsUpIndicator(R.drawable.ic_menu);
	 	ab.setDisplayHomeAsUpEnabled(true);
	}
	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		// The action bar home/up action should open or close the drawer.
		switch (item.getItemId()) {
			case android.R.id.home:
				mDrawer.openDrawer(GravityCompat.START);
				return true;
		}
		return super.onOptionsItemSelected(item);
	}
	@Override
	protected void onPostCreate(Bundle savedInstanceState) {
		super.onPostCreate(savedInstanceState);
	}

Navigating Between Menu Items

To achieve the navigation between menu items we need to setup a handler which responds to click events on the navigation items and swap out the fragment.

@Override
    protected void onCreate(Bundle savedInstanceState) {
         // Find our drawer view
        nvDrawer = (NavigationView) findViewById(R.id.nvView);
        // Setup drawer view
        setupDrawerContent(nvDrawer);
    }
    private void setupDrawerContent(NavigationView navigationView) {
        navigationView.setNavigationItemSelectedListener(
                new NavigationView.OnNavigationItemSelectedListener() {
                    @Override
                    public boolean onNavigationItemSelected(MenuItem menuItem) {
                        selectDrawerItem(menuItem);
                        return true;
                    }
                });
    }
    public void selectDrawerItem(MenuItem menuItem) {
        // Create a new fragment and specify the planet to show based on
        // position
        Fragment fragment = null;
        Class fragmentClass;
        switch(menuItem.getItemId()) {
            case R.id.nav_first_fragment:
                fragmentClass = FirstFragment.class;
                break;
            case R.id.nav_second_fragment:
                fragmentClass = SecondFragment.class;
                break;
            case R.id.nav_third_fragment:
                fragmentClass = ThirdFragment.class;
                break;
            default:
                fragmentClass = FirstFragment.class;
        }
        try {
            fragment = (Fragment) fragmentClass.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        // Insert the fragment by replacing any existing fragment
        FragmentManager fragmentManager = getActivity().getSupportFragmentManager();
        fragmentManager.beginTransaction().replace(R.id.flContent, fragment).commit();
        // Highlight the selected item, update the title, and close the drawer
        menuItem.setChecked(true);
        setTitle(menuItem.getTitle());
        mDrawer.closeDrawers();
    }

At this point we can now run the application and our ND will successfully work. We can swap the screen from left to right or simply click the menu icon in the upper left corner.


Enhance our Navigation Drawer

Now we have a working ND, but… What about the header section we can see on screenshots? Well, we can add a header to our ND in a simple way. Let’s check it out!

The NavigationView control accepts a custom attribute that can reference a layout that provides a header of our layout. For instance, you can create a layout/nav_header.xml file similar to the following:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="192dp"
    android:background="?attr/colorPrimaryDark"
    android:padding="16dp"
    android:theme="@style/ThemeOverlay.AppCompat.Dark"
    android:orientation="vertical"
    android:gravity="bottom">
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Header"
        android:textColor="@android:color/white"
        android:textAppearance="@style/TextAppearance.AppCompat.Body1"/>
</LinearLayout>

And then we just need to reference this layout in our NavigationView with the app:headerLayout custom attribute:

<!-- The navigation drawer -->
    <android.support.design.widget.NavigationView
        app:headerLayout="@layout/nav_header">
    </android.support.design.widget.NavigationView>

Alternatively  we can add some other controls to the header layout as we need, for instance, we can add a picture, a button, more textviews, etc.


What if i don’t want to use Fragments?

Although many ND examples make use of fragments, we can also use a RelativeLayout / LinearLayout if we need it. With this we can us a drawer as an overlay to our currently displayed activity.

So, what should we do?

All we need to do is, instead of we just substitute that for a

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

        <LinearLayout
                android:id="@+id/content_frame"
                android:orientation="horizontal"
                android:layout_width="match_parent"
                android:layout_height="match_parent"/>

        <!-- The navigation drawer -->
        <ListView android:id="@+id/left_drawer"
                  android:layout_width="240dp"
                  android:layout_height="wrap_content"
                  android:layout_gravity="start"
                  android:choiceMode="singleChoice"
                  android:divider="@android:color/transparent"
                  android:dividerHeight="0dp"
                  android:background="#111"/>

</android.support.v4.widget.DrawerLayout>

And instead of this:

// Insert the fragment by replacing any existing fragment
getFragmentManager().beginTransaction()
       .replace(R.id.content_frame, fragment)
       .commit();

We can just use the container to directly inflate the Activity:

LayoutInflater inflater = getLayoutInflater();
LinearLayout container = (LinearLayout) findViewById(R.id.content_frame);
inflater.inflate(R.layout.activity_main, container);

And that’s it!! we have our Navigation Drawer working properly. From now on, if we wanted to add new options, just add them to the menu/drawer_view.xml file and redirect it from the Fragment selector code:

switch(menuItem.getItemId()) {
            case R.id.nav_first_fragment:
                fragmentClass = FirstFragment.class;
                break;
            case R.id.nav_second_fragment:
                fragmentClass = SecondFragment.class;
                break;
            case R.id.nav_third_fragment:
                fragmentClass = ThirdFragment.class;
                break;
            case R.id.nav_new_fragment:
                fragmentClass = NewFragment.class
                break;
            default:
                fragmentClass = FirstFragment.class;

And this is how we implement a Navigation Drawer in our Android applications. I hope it results useful for all you guys.!

Leave a Reply

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