Navigation Drawer

 

Navigation Drawer is a UI panel that shows your application’s main pages menu items. It opens when user slide finger from left edges of the screen or when user touch on drawer icon located in the toolbar. We can see real example of Navigation Drawer in many applications like Gmail, Google Play Store etc. Today we are going to learn about how to create Navigation Drawer using Navigation View in android app with example. Before we start most important question is

What is Navigation View?

Google has introduced Navigation View for displaying navigation menu using a menu resource. It is mandatory to place Navigation View inside DrawerLayout. Like this

<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"
     android:fitsSystemWindows="true">

     <!-- Your contents -->

     <com.google.android.material.navigation.NavigationView
         android:id="@+id/navigation"
         android:layout_width="wrap_content"
         android:layout_height="match_parent"
         android:layout_gravity="start"
         app:menu="@menu/my_navigation_items" />
 </android.support.v4.widget.DrawerLayout>

 

Now let’s start with example.

Add Dependency


You need to add following dependencies into build.gradle file of your android kotlin project.

dependencies {
    implementation 'com.android.support:appcompat-v7:28.0.0-rc01'
    implementation 'com.android.support:design:28.0.0-rc01'
}

Create menu items for Navigation Drawer


Create one menu file as drawer_items.xml into menu resource directory. If menu resource directory not exists please create one inside resource directory.

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <group android:checkableBehavior="single">
        <item
            android:id="@+id/navItemInbox"
            android:icon="@drawable/ic_inbox_black"
            android:title="@string/inbox" />
        <item
            android:id="@+id/navItemSent"
            android:icon="@drawable/ic_send_black"
            android:title="@string/sent" />
        <item
            android:id="@+id/navItemDraft"
            android:icon="@drawable/ic_drafts_black"
            android:title="@string/draft" />
        <item
            android:id="@+id/navItemTrash"
            android:icon="@drawable/ic_delete_forever"
            android:title="@string/trash" />

        <item
            android:id="@+id/navItemSettings"
            android:icon="@drawable/ic_settings_black"
            android:title="@string/settings" />
    </group>
</menu>

 

Make a header layout file for Side Drawer


Make a layout file as navigationview_header.xml. This file is user to display header into navigation view.

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="192dp">

    <ImageView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:contentDescription="@string/app_name"
        android:scaleType="fitXY"
        android:src="@drawable/side_drawer_header_drawable" />

    <TextView
        android:id="@+id/textEmail"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="20dp"
        android:layout_marginStart="20dp"
        android:text="lokeshdesai@android4dev.com"
        android:textColor="@android:color/white"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent" />

</android.support.constraint.ConstraintLayout>

 

Create Navigation Drawer layout


For creating Navigation Drawer make one layout file using DrawerLayout as a root view. Add NavigationView and FrameLayout inside DrawerLayout. For adding drawer button into action bar we will also declare Toolbar into layout file. 

<?xml version="1.0" encoding="utf-8"?><!-- Use DrawerLayout as root container for activity -->
<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/drawerLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true">

    <android.support.constraint.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <android.support.v7.widget.Toolbar xmlns:android="http://schemas.android.com/apk/res/android"
            xmlns:local="http://schemas.android.com/apk/res-auto"
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="?attr/colorPrimary"
            android:minHeight="?attr/actionBarSize"
            app:contentInsetEnd="0dp"
            app:contentInsetStart="0dp"
            local:popupTheme="@style/ThemeOverlay.AppCompat.Light"
            local:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" />

        <!-- Layout to contain contents of main body of screen (drawer will slide over this) -->
        <FrameLayout
            android:id="@+id/frameLayout"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintTop_toBottomOf="@id/toolbar" />

    </android.support.constraint.ConstraintLayout>

    <!-- Container for contents of drawer - use NavigationViewActivity to make configuration easier -->
    <android.support.design.widget.NavigationView
        android:id="@+id/navigationView"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:fitsSystemWindows="true"
        app:headerLayout="@layout/navigationview_header"
        app:itemIconTint="@color/navigation_icon_color_state"
        app:menu="@menu/drawer_items" />

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

 

As you can see we can attach menu and header layout file with NavigationView using

app:headerLayout="@layout/navigationview_header"
app:menu="@menu/drawer_items"

 

Always remember that whenever you add Toolbar to your layout make changes in style.xml. Like use parent theme as Theme.AppCompat.Light.NoActionBar.

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

 

FrameLayout contains main layout of the body or screen. Drawer will slide over this. For detail information related to FrameLayout please check developer’s blog. In out case we will display all screens like Inbox, Sent, Draft, Settings, Trash into framelayout. Whenever user select any option from Navigation Drawer we will display that menu related screen into FrameLayout. 

Add drawer to layout


For handling navigation drawer menu item click we will register NavigationItemSelectedListener by using below code.

navigationView.setNavigationItemSelectedListener { menuItem ->
            when (menuItem.itemId) {
                R.id.navItemInbox -> {
                    toolbar.title = getString(R.string.inbox)
                    navigationPosition = R.id.navItemInbox
                    navigateToFragment(InboxFragment.newInstance())
                }
                R.id.navItemSent -> {
                    toolbar.title = getString(R.string.sent)
                    navigationPosition = R.id.navItemSent
                    navigateToFragment(SentFragment.newInstance())
                }
                R.id.navItemDraft -> {
                    toolbar.title = getString(R.string.draft)
                    navigationPosition = R.id.navItemDraft
                    navigateToFragment(DraftFragment.newInstance())
                }
                R.id.navItemTrash -> {
                    toolbar.title = getString(R.string.trash)
                    navigationPosition = R.id.navItemTrash
                    navigateToFragment(TrashFragment.newInstance())
                }
                R.id.navItemSettings -> {
                    toolbar.title = getString(R.string.settings)
                    navigationPosition = R.id.navItemSettings
                    navigateToFragment(SettingsFragment.newInstance())
                }
            }
            // set item as selected to persist highlight
            menuItem.isChecked = true
            // close drawer when item is tapped
            drawerLayout.closeDrawers()
            true
        }

 

When user tap navigation view item this listener will mark menu item as checked. Using drawerLayout.closeDrawers() line we can close drawer after user select any menu item from Navigation Drawer. We will use Fragment to display menu item contents. 

Now we will create a link between DrawerLayout and ActionBar using below code.

val toggle = ActionBarDrawerToggle(
                this, drawerLayout, toolbar, R.string.drawerOpen, R.string.drawerClose)
        drawerLayout.addDrawerListener(toggle)
        toggle.syncState()

 

toggle.syncState()  use to synchronize the indicator with the state of the linked DrawerLayout after onRestoreInstanceState has occurred. 

If you want that your Toolbar work as ActionBar than add below line in your code.

setSupportActionBar(toolbar)

 

You can also change Hamburger icon using below line

setHomeAsUpIndicator(int)

 

For handling Drawer States like close, open, statechange , slide. We need to add DrawerListener.

 

drawerLayout.addDrawerListener(object:DrawerLayout.DrawerListener{
            override fun onDrawerStateChanged(p0: Int) {
                TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
            }

            override fun onDrawerSlide(p0: View, p1: Float) {
                TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
            }

            override fun onDrawerClosed(p0: View) {
                TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
            }

            override fun onDrawerOpened(p0: View) {
                TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
            }
        })

 

Now finally your file will look like this

package com.android4dev.navigationview

import android.os.Bundle
import android.support.v4.app.Fragment
import android.support.v4.app.FragmentTransaction
import android.support.v4.widget.DrawerLayout
import android.support.v7.app.ActionBarDrawerToggle
import android.support.v7.app.AppCompatActivity
import android.view.Gravity
import android.view.View
import com.android4dev.navigationview.fragment.*
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.android.synthetic.main.navigationview_header.view.*


class NavigationViewActivity : AppCompatActivity() {

    var navigationPosition: Int = 0

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        initView()
    }

    private fun initView() {
        setSupportActionBar(toolbar)
        setUpDrawerLayout()

        //Load Inbox fragment first
        navigationPosition = R.id.navItemInbox
        navigateToFragment(InboxFragment.newInstance())
        navigationView.setCheckedItem(navigationPosition)
        toolbar.title = getString(R.string.inbox)

        navigationView.setNavigationItemSelectedListener { menuItem ->
            when (menuItem.itemId) {
                R.id.navItemInbox -> {
                    toolbar.title = getString(R.string.inbox)
                    navigationPosition = R.id.navItemInbox
                    navigateToFragment(InboxFragment.newInstance())
                }
                R.id.navItemSent -> {
                    toolbar.title = getString(R.string.sent)
                    navigationPosition = R.id.navItemSent
                    navigateToFragment(SentFragment.newInstance())
                }
                R.id.navItemDraft -> {
                    toolbar.title = getString(R.string.draft)
                    navigationPosition = R.id.navItemDraft
                    navigateToFragment(DraftFragment.newInstance())
                }
                R.id.navItemTrash -> {
                    toolbar.title = getString(R.string.trash)
                    navigationPosition = R.id.navItemTrash
                    navigateToFragment(TrashFragment.newInstance())
                }
                R.id.navItemSettings -> {
                    toolbar.title = getString(R.string.settings)
                    navigationPosition = R.id.navItemSettings
                    navigateToFragment(SettingsFragment.newInstance())
                }
            }
            // set item as selected to persist highlight
            menuItem.isChecked = true
            // close drawer when item is tapped
            drawerLayout.closeDrawers()
            true
        }

        //Change navigation header information
        changeNavigationHeaderInfo()

        drawerLayout.addDrawerListener(object:DrawerLayout.DrawerListener{
            override fun onDrawerStateChanged(p0: Int) {
                TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
            }

            override fun onDrawerSlide(p0: View, p1: Float) {
                TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
            }

            override fun onDrawerClosed(p0: View) {
                TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
            }

            override fun onDrawerOpened(p0: View) {
                TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
            }
        })
    }

    private fun changeNavigationHeaderInfo() {
        val headerView = navigationView.getHeaderView(0)
        headerView.textEmail.text = "lokeshdesai@android4dev.com"
    }

    private fun setUpDrawerLayout() {
        val toggle = ActionBarDrawerToggle(
                this, drawerLayout, toolbar, R.string.drawerOpen, R.string.drawerClose)
        drawerLayout.addDrawerListener(toggle)
        toggle.syncState()
    }

    private fun navigateToFragment(fragmentToNavigate: Fragment) {
        val fragmentTransaction = supportFragmentManager.beginTransaction()
        fragmentTransaction.replace(R.id.frameLayout, fragmentToNavigate)
        fragmentTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
        fragmentTransaction.addToBackStack(null)
        fragmentTransaction.commit()
    }

    override fun onBackPressed() {

        if (drawerLayout.isDrawerOpen(Gravity.START)) {
            drawerLayout.closeDrawer(Gravity.START)
        }

        if (navigationPosition == R.id.navItemInbox) {
            finish()
        } else {
            //Navigate to Inbox Fragment
            navigationPosition = R.id.navItemInbox
            navigateToFragment(InboxFragment.newInstance())
            navigationView.setCheckedItem(navigationPosition)
            toolbar.title = getString(R.string.inbox)
        }
    }

}

 

For changing Navigation Drawer header content we need to first get view from header position. In our case header position is 0. So we will update header content using below line.

private fun changeNavigationHeaderInfo() {
        val headerView = navigationView.getHeaderView(0)
        headerView.textEmail.text = "lokeshdesai@android4dev.com"
    }

When user tap on any menu item we need to display content related to that menu item. For that purpose we will use Fragment. For navigating from one Fragment to another we will use below code. We are going to user FrameLayout as a container for Fragment.

private fun navigateToFragment(fragmentToNavigate: Fragment) {
        val fragmentTransaction = supportFragmentManager.beginTransaction()
        fragmentTransaction.replace(R.id.frameLayout, fragmentToNavigate)
        fragmentTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
        fragmentTransaction.addToBackStack(null)
        fragmentTransaction.commit()
    }

 

Create one color file for drawer menu item


For displaying  different color for selected menu item and deselected menu item we will create one color file into color directory. Name that file as a navigation_item_color_state.xml.

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:color="@color/colorPrimary" android:state_checked="true" />
    <item android:color="@android:color/black" />
</selector>

 

We can integrate this color file with Navigation View using below line

app:itemIconTint="@color/navigation_icon_color_state"

Tips: Never declare you DrawerLayout or NavigationView as static or companion. It will cause memory leak. 

Now run the project and wait for the result.

 

Conclusion

I recommend that try to use Navigation View when you have more than 5 menu items. If you have less than 5 items than try to use BottomNavigationView.

Source Code

Full Navigation Drawer Demo in Kotlin

You can also refer

How to start with kotlin?

Hi, I am Android Developer and Founder of Android4Dev. Right now my interest is in RxKotlin, MVVM and Dagger2. I am Kotlin Lover. Kotlin made me more productive in terms of coding. Do you want me to code for you ? Please don’t hesitate to contact me.