Android Navigation Drawer with Material Design

Android Navigation Drawer with Material Design

In this tutorial we are going to learn how to develop android Navigation Drawer with Material Design. Android Navigation Drawer is one of popularly used top navigation widget.

It will introduce in Android 5 and the android support library version 4 is updated so that the Navigation drawer will be compatible with older versions of android.

We are going to use android ToolBar to replace the android ActionBar in our application. If you do not know how to use android ToolBar with Material Design I will suggest you read my post on this topic Android ToolBar Tutorial with Material design.

If you are using Android Studio version 1.0.0 and up, it has a template for create android Navigation Drawer. We will first see how we can achieve this with Android Studio and we will further went through our own path to create it from scratch.

Start up your android studio and follow the steps below

Click on the File > New Project to create a new android application project as shown below

navione

Enter the project name as shown below

navitwo

Select the android SDK versions the application will support and the devices

navithree

In the Activity template, select the Navigation Drawer Activity as show below

navifour

Enter the name of the default Activity that the project will create automatically.

navifive

Finally, click the finish button and the project will be created. Once the project is created, you will see the following image below

navisix

When we run the project, we will get an application with the following UI interface as shown below

naviseven

The complete source code can be downloaded below

Creating Android Navigation Drawer from scratch

Since we have seen how Android Studio create a Navigation drawer menu with built-in template, we will proceed now to learn how we can replicate this ourselves without using the default template.

We are going to use a ToolBar and elements of Material Design to create the Nagivation drawer menu.

Before we start it will be better for us to have an idea about what we are going to create. The final working android nagivation drawer is as shown

navieight

Before we start, the first thing I will do is to list the environment and tools I used in this android tutorial but feel free to use whatever environment or tools you are familiar with.

Windows 7

Android Studio

Sony Xperia ZL

Min SDK 14

Target SDK 19

To create a new android application project, following the steps as stipulated below.

Go to File menu

Click on New menu

Click on Android Application

Enter Project name: AndroidNavigationDrawer

Package: com.inducesmile.androidnavigationdrawer

Keep other default selections.

Continue to click on next button until Finish button is active, then click on Finish Button

Once you are done with creating your project, make sure you change the package name if you did not use the same package.

Open the default layout that was created by Android Studio (activity_main.xml). We are going to use a LinearLayout as the root layout. We will add a ToolBar widget which we will use to replace our application ActionBar.

The android DrawerLayout is a ViewGroup use for Navigation drawer. We will add the DrawerLayout in the main layout. The DrawerLayout will contain two other views. The first one is a FrameLayout while the second is a ListView.

The FrameLayout will come first before the ListView. Since android View uses z-index for View arrangement. The FrameLayout will contain the main content of the page. We are going to use a Fragment instances in the FrameLayout depending on the navigation item selected from the ListView.

The complete code snippet for the layout is as shown below.

<LinearLayout 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"
    android:orientation="vertical">

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="64dp"
        android:background="@color/color_primary"
        android:minHeight="?attr/actionBarSize" />

    <android.support.v4.widget.DrawerLayout
        android:id="@+id/drawer_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <!-- Main layout -->
        <FrameLayout
            android:id="@+id/main_fragment_container"
            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="match_parent"
            android:layout_gravity="start"
            android:choiceMode="singleChoice"
            android:divider="@android:color/transparent"
            android:dividerHeight="0dp"
            android:background="#ccc"/>

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

</LinearLayout>

Since we are going to use a ToolBar, we will change our application Theme to inherit from Theme.AppCompat.Light.NoActionBar. The theme will remove the application ActionBar and we will set the ToolBar as our ActionBar. The new code for the styles.xml is shown.

<resources>
    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/color_primary</item>
        <item name="colorPrimaryDark">@color/color_primary_dark</item>
        <item name="colorAccent">@color/accent_color</item>
    </style>
</resources>

The color resources added to the theme to override the default android color were placed in the colors.xml. The updated colors.xml file is as shown below.

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="color_primary">#2196F3</color>
    <color name="color_primary_dark">#1976D2</color>
    <color name="accent_color">#c2110e</color>
</resources>

The strings.xml resource is as shown below.

<resources>
    <string name="app_name">Navigation Drawer</string>
    <string name="hello_world">Hello world!</string>
    <string name="action_settings">Settings</string>
    <string name="logo_desc">App logo</string>
    <string name="drawer_open">Open</string>
    <string name="drawer_close">Close</string>
    <string name="hello_blank_fragment">Hello blank fragment</string>
</resources>

Now that we have all the resources in place, we will move over to create a custom adapter we will use to populate the ListView for our navigation Drawer.

The custom adapter will inflate a layout which contains an ImageView and a TextView. The adapter will use the data set to populate the View widgets.

In the layout folder, create a file called listview_with_text_image.xml. Copy and paste the following code to the layout file.

<?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="12dp"
    android:paddingBottom="12dp">

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/imageView"
        android:layout_marginLeft="16dp"
        android:layout_marginStart="16dp"
        android:contentDescription="@string/hello_world"
        android:src="@drawable/imagetwo"
        android:layout_marginTop="16dp" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/hello_world"
        android:id="@+id/textView"
        android:textStyle="bold"
        android:textSize="14dp"
        android:layout_alignTop="@+id/imageView"
        android:layout_toRightOf="@+id/imageView"
        android:layout_marginLeft="24dp" />

</RelativeLayout>

The customAdapter will inherit from the BaseAdapter. In the BaseAdapter constructor, we will pass a Context object and a List object that contains the data object. The adapter class is shown below.

package inducesmile.com.androidnavigation;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;

import java.util.List;

public class CustomAdapter extends BaseAdapter {

    private LayoutInflater lInflater;
    private List<ItemObject> listStorage;

    public CustomAdapter(Context context, List<ItemObject> customizedListView) {
        lInflater =(LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        listStorage = customizedListView;
    }

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

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

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

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

        ViewHolder listViewHolder;
        if(convertView == null){
            listViewHolder = new ViewHolder();
            convertView = lInflater.inflate(R.layout.listview_with_text_image, parent, false);

            listViewHolder.textInListView = (TextView)convertView.findViewById(R.id.textView);
            listViewHolder.imageInListView = (ImageView)convertView.findViewById(R.id.imageView);
            convertView.setTag(listViewHolder);
        }else{
            listViewHolder = (ViewHolder)convertView.getTag();
        }
        listViewHolder.textInListView.setText(listStorage.get(position).getName());
        listViewHolder.imageInListView.setImageResource(listStorage.get(position).getImageId());

        return convertView;
    }

    static class ViewHolder{

        TextView textInListView;
        ImageView imageInListView;
    }
}

The entity class is used to stored the data that will be use to bind to the layout View widgets. The code for the entity class is as shown below.

package inducesmile.com.androidnavigation;

public class ItemObject {

    private String name;
    private int imageId;

    public ItemObject(String name, int imageId) {
        this.name = name;
        this.imageId = imageId;
    }

    public int getImageId() {
        return imageId;
    }

    public void setImageId(int imageId) {
        this.imageId = imageId;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

We will go over to the main Activity class. In the main activity class, we will first of all get a handler for the ToolBar widget.

Thereafter, we instantiate the customAdapter and pass it in setAdapter() method of the ListView class.

We will get a handler for the Drawerlayout and we will use it to set the drawer listener. The detail code for the main activity is as shown below.

package inducesmile.com.androidnavigation;

import android.content.res.Configuration;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.List;


public class MainActivity extends ActionBarActivity {

    private DrawerLayout mDrawerLayout;
    private ListView mDrawerList;
    String[]titles = {"Nigeria", "Ghana", "Senegal", "Togo"};
    private CharSequence mTitle;
    private CharSequence mDrawerTitle;
    private ActionBarDrawerToggle mDrawerToggle;
    private Toolbar topToolBar;

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

        mTitle = mDrawerTitle = getTitle();

        topToolBar = (Toolbar)findViewById(R.id.toolbar);
        setSupportActionBar(topToolBar);
        topToolBar.setLogo(R.drawable.logo);
        topToolBar.setLogoDescription(getResources().getString(R.string.logo_desc));

        mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
        mDrawerList = (ListView) findViewById(R.id.left_drawer);

        List<ItemObject> listViewItems = new ArrayList<ItemObject>();
        listViewItems.add(new ItemObject("Nigeria", R.drawable.imageone));
        listViewItems.add(new ItemObject("Ghana", R.drawable.imagetwo));
        listViewItems.add(new ItemObject("Senegal", R.drawable.imagethree));
        listViewItems.add(new ItemObject("Togo", R.drawable.imagefour));

        mDrawerList.setAdapter(new CustomAdapter(this, listViewItems));

        mDrawerToggle = new ActionBarDrawerToggle(MainActivity.this, mDrawerLayout, R.string.drawer_open, R.string.drawer_close) {

            /** Called when a drawer has settled in a completely closed state. */
            public void onDrawerClosed(View view) {
                super.onDrawerClosed(view);
                getSupportActionBar().setTitle(mTitle);
                invalidateOptionsMenu(); // creates call to onPrepareOptionsMenu()
            }

            /** Called when a drawer has settled in a completely open state. */
            public void onDrawerOpened(View drawerView) {
                super.onDrawerOpened(drawerView);
                getSupportActionBar().setTitle(mDrawerTitle);
                invalidateOptionsMenu(); // creates call to onPrepareOptionsMenu()
            }
        };

        // Set the drawer toggle as the DrawerListener
        mDrawerLayout.setDrawerListener(mDrawerToggle);
        mDrawerToggle.setDrawerIndicatorEnabled(true);

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

        mDrawerList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                // make Toast when click
                Toast.makeText(getApplicationContext(), "Position " + position, Toast.LENGTH_LONG).show();
                selectItemFragment(position);
            }
        });
    }

    private void selectItemFragment(int position){

        Fragment fragment = null;
        FragmentManager fragmentManager = getSupportFragmentManager();
        switch(position) {
            default:
            case 0:
                fragment = new DefaultFragment();
                break;
            case 1:
                fragment = new DefaultFragment();
                break;
            case 2:
                fragment = new DefaultFragment();
                break;
            case 3:
                fragment = new DefaultFragment();
                break;
        }
        fragmentManager.beginTransaction().replace(R.id.main_fragment_container, fragment).commit();

        mDrawerList.setItemChecked(position, true);
        setTitle(titles[position]);
        mDrawerLayout.closeDrawer(mDrawerList);
    }
    @Override
    public void setTitle(CharSequence title) {
        mTitle = title;
        getSupportActionBar().setTitle(mTitle);
    }

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

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

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }
    @Override
    public boolean onPrepareOptionsMenu(Menu menu) {
        // If the nav drawer is open, hide action items related to the content view
        boolean drawerOpen = mDrawerLayout.isDrawerOpen(mDrawerList);
        return super.onPrepareOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }
        if (mDrawerToggle.onOptionsItemSelected(item)) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
}

We will attach an event listener to the list items of our navigation drawer so that when an item is click a new Fragment object will be added to the main contain FrameLayout.

We have created a private method selectItemFragment(int position) in our main activity class that uses the item click position as parameter to determine the kind of content to be added in the main content area. The method description is as shown.

private void selectItemFragment(int position){

        Fragment fragment = null;
        FragmentManager fragmentManager = getSupportFragmentManager();
        switch(position) {
            default:
            case 0:
                fragment = new DefaultFragment();
                break;
            case 1:
                fragment = new DefaultFragment();
                break;
            case 2:
                fragment = new DefaultFragment();
                break;
            case 3:
                fragment = new DefaultFragment();
                break;
        }
        fragmentManager.beginTransaction().replace(R.id.main_fragment_container, fragment).commit();

        mDrawerList.setItemChecked(position, true);
        setTitle(titles[position]);
        mDrawerLayout.closeDrawer(mDrawerList);
    }

We are going to create a single Fragment class that will be called when a click is registered. For brevity we are going to use a single Fragment but feel free to create individual Fragment for each of the case or your project specification.

The Fragment inflate a layout that is used to construct its UI interface. The layout file for the Fragment is shown below.

<RelativeLayout 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"
    android:background="@drawable/bg"
    tools:context="inducesmile.com.androidnavigation.DefaultFragment">

</RelativeLayout>

The Fragment class is very simple. Create a Fragment class called DefaultFragment. Copy and paste the code below to the file.

package inducesmile.com.androidnavigation;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

/**
 * A simple {@link Fragment} subclass.
 */
public class DefaultFragment extends Fragment {


    public DefaultFragment() {
        // 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_default, container, false);
    }


}

Save the file and run your project.

You can download the code for this tutorial below. If you are having hard time downloading the tutorials, kindly contact me.

Remember to subscribe with your email so that you will be among the first to receive our new post once it is published

No Responses

Add a Comment