In Android, Navigation Drawer is a panel that displays the app main navigation options on the left edge of the screen like sliding menu. The navigation drawer is hidden most of the time, but it is revealed when the user swipes a finger from the left edge of the screen or, while at the top level of the app, the user touches the app icon in the action bar.
Note that if you have many different destinations (more than six, say) in your app, it's recommended that you use a navigation drawer.
To begin using DrawerLayout
and NavigationView
in your project, you'll need to import the design support and also the Android support artifact. So add these to your module's build.gradle file to import them.
dependencies { implementation 'com.android.support:design:27.0.2' implementation 'com.android.support:support-v4:27.0.2' }
Open activity_main.xml file from res/layout folder path and it will contain the code like as shown below.
<?xml version="1.0" encoding="utf-8"?> <android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/drawer_layout" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true" tools:openDrawer="start"> <include layout="@layout/app_bar_main" android:layout_width="match_parent" android:layout_height="match_parent" /> <android.support.design.widget.NavigationView android:id="@+id/nav_view" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_gravity="start" android:fitsSystemWindows="true" app:headerLayout="@layout/nav_header_main" app:menu="@menu/activity_main_drawer" /> </android.support.v4.widget.DrawerLayout>
Here DrawerLayout
acts as a top-level container for window content that allows for interactive "drawer" views to be pulled out from one or both vertical edges of the window.
Here is my app_bar_main.xml resource file. This file simply has a CoordinatorLayout
, an AppBarLayout
, and a Toolbar widget.
<?xml version="1.0" encoding="utf-8"?> <android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <android.support.design.widget.AppBarLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:fitsSystemWindows="true"> <android.support.v7.widget.Toolbar android:id="@+id/toolbar_main" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" app:layout_scrollFlags="scroll|enterAlways" /> </android.support.design.widget.AppBarLayout> </android.support.design.widget.CoordinatorLayout>
Finally, we created a NavigationView
widget. NavigationView
represents a standard navigation menu for application. The menu contents can be populated by a menu resource file.
We also included an app:headerLayout
attribute which points to @layout/nav_header_main
. This will add a View
as a header of the navigation menu.
Here is my nav_header_main.xml layout resource file:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/nav_header" android:layout_width="match_parent" android:layout_height="160dp" android:background="@color/colorAccent" android:clickable="true" android:focusable="true" android:foreground="?attr/selectableItemBackgroundBorderless" android:gravity="bottom" android:orientation="vertical" android:padding="16dp" android:theme="@style/ThemeOverlay.AppCompat.Dark"> <ImageView android:id="@+id/nav_header_imageView" android:layout_width="64dp" android:layout_height="64dp" android:src="@mipmap/ic_launcher" /> <TextView android:id="@+id/nav_header_textView" android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingTop="16dp" android:text="Title" android:textAppearance="@style/TextAppearance.AppCompat.Body1" /> </LinearLayout>
To include the menu items for the navigation drawer, we can use the attribute app:menu
with a value that points to a menu resource file.
<android.support.design.widget.NavigationView app:menu="@menu/activity_main_drawer" />
Here is the res/menu/activity_main_drawer.xml menu resource file:
<?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android"> <group> <item android:id="@+id/nav_item_one" android:icon="@android:drawable/ic_dialog_info" android:title="Item 1" /> <item android:id="@+id/nav_item_two" android:icon="@android:drawable/ic_dialog_info" android:title="Item 2" /> <item android:id="@+id/nav_item_three" android:icon="@android:drawable/ic_dialog_info" android:title="Item 3" /> </group> <group android:id="@+id/group_menu"> <item android:id="@+id/nav_item_four" android:title="Item 4" /> <item android:id="@+id/nav_item_five" android:title="Item 5" /> </group> <item android:title="Title 1"> <menu> <item android:id="@+id/nav_item_six" android:icon="@android:drawable/ic_dialog_info" android:title="Item 6" /> <item android:id="@+id/nav_item_seven" android:icon="@android:drawable/ic_dialog_info" android:title="Item 7" /> </menu> </item> </menu>
Each of the <item> elements has an id, an icon, and a title. Note that a horizontal line will be drawn at the end of each <group> for us when shown in the navigation drawer.
Note that when showing the navigation list items from a menu resource, we could use a ListView
instead. But, by configuring the navigation drawer with a menu resource, we get the material design styling on the navigation drawer for free! If you used a ListView
, you would have to maintain the list and also style it to meet the recommended material design specs for the navigation drawer.
Now open your main activity file MainActivity.java and write the code like as shown below
public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener { DrawerLayout drawer; ActionBarDrawerToggle toggle; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); drawer = (DrawerLayout) findViewById(R.id.drawer_layout); toggle = new ActionBarDrawerToggle( this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close); drawer.setDrawerListener(toggle); toggle.syncState(); NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view); navigationView.setNavigationItemSelectedListener(this); } @Override public void onBackPressed() { DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout); if (drawer.isDrawerOpen(GravityCompat.START)) { drawer.closeDrawer(GravityCompat.START); } else { super.onBackPressed(); } } @Override public boolean onOptionsItemSelected(MenuItem item) { if (toggle.onOptionsItemSelected(item)) { return true; } return super.onOptionsItemSelected(item); } @SuppressWarnings("StatementWithEmptyBody") @Override public boolean onNavigationItemSelected(MenuItem item) { // handle navigation view item clicks here int id = item.getItemId(); if (id == R.id.nav_item_one) { Toast.makeText(this, "Clicked item one", Toast.LENGTH_SHORT).show(); } else if (id == R.id.nav_item_two) { } else if (id == R.id.nav_item_three) { } else if (id == R.id.nav_item_four) { } drawer.closeDrawer(GravityCompat.START); return true; } }
The ActionBarDrawerToggle
sets up the app icon located on the left of the action bar or toolbar to open and close the navigation drawer. To be able to create an instance of ActionBarDrawerToggle
, we have to provide the following parameters:
Activity
you use this, while in a Fragment
you call getActivity()
DrawerLayout
widget to link to the activity's ActionBar
We invoked the method addDrawerListener()
on a DrawerLayout
so as to connect an ActionBarDrawerToggle
with a DrawerLayout
.
Note that we also enable the app icon via setHomeButtonEnabled()
and enable it for "up" navigation via setDisplayHomeAsUpEnabled()
.
We then forward the onPostCreate()
, onConfigurationChanged()
, and onOptionsItemSelected()
activity callback methods on to the toggle
.
We can also add action on open or close event.
toggle = new ActionBarDrawerToggle( this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close){ public void onDrawerClosed(View view){ super.onDrawerClosed(view); // do something } public void onDrawerOpened(View drawerView){ super.onDrawerOpened(drawerView); // do something } };
When we run above program in android studio we will get the result like as shown below.
Kotlin variant
Following is MainActivity in Kotlin language.
class MainActivity : AppCompatActivity() { private lateinit var drawer: DrawerLayout private lateinit var toggle: ActionBarDrawerToggle override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val toolbar: Toolbar = findViewById(R.id.toolbar_main) setSupportActionBar(toolbar) drawer = findViewById(R.id.drawer_layout) toggle = ActionBarDrawerToggle(this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close) drawer.addDrawerListener(toggle) supportActionBar?.setDisplayHomeAsUpEnabled(true) supportActionBar?.setHomeButtonEnabled(true) } override fun onPostCreate(savedInstanceState: Bundle?) { super.onPostCreate(savedInstanceState) toggle.syncState() } override fun onConfigurationChanged(newConfig: Configuration?) { super.onConfigurationChanged(newConfig) toggle.onConfigurationChanged(newConfig) } override fun onOptionsItemSelected(item: MenuItem?): Boolean { if (toggle.onOptionsItemSelected(item)) { return true } return super.onOptionsItemSelected(item) } override fun onBackPressed() { if (drawer.isDrawerOpen(GravityCompat.START)) { drawer.closeDrawer(GravityCompat.START) } else { super.onBackPressed() } } }
Now, let's see how to handle click events for each of the items in the navigation drawer. Note that clicking on any item is supposed to take you to a new Activity
or Fragment
- that's why it's called a navigation drawer!
First, your activity needs to implement the NavigationView.OnNavigationItemSelectedListener
. By implementing this contract or interface, we must now override the only method: onNavigationItemSelected()
.
class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelectedListener { override fun onCreate(savedInstanceState: Bundle?) { // ... val navigationView: NavigationView = findViewById(R.id.nav_view) navigationView.setNavigationItemSelectedListener(this) // ... } override fun onNavigationItemSelected(item: MenuItem): Boolean { when (item.itemId) { R.id.nav_item_one -> Toast.makeText(this, "Clicked item one", Toast.LENGTH_SHORT).show() R.id.nav_item_two -> Toast.makeText(this, "Clicked item two", Toast.LENGTH_SHORT).show() R.id.nav_item_three -> Toast.makeText(this, "Clicked item three", Toast.LENGTH_SHORT).show() R.id.nav_item_four -> Toast.makeText(this, "Clicked item four", Toast.LENGTH_SHORT).show() } //drawer.closeDrawer(GravityCompat.START) return true } }
This method is invoked when an item in the navigation menu is selected. We used the when
expression to perform different actions based on the menu item that was clicked—the menu item ids serve as constants for the when expression.
Next, we have to initialize our NavigationView
and set this listener inside onCreate()
of our activity.
Navigation Drawer and Fragments
In this section you will learn how to open Fragment from Navigation Drawer.
Let's modify app_bar_main.xml and add FrameLayout
.
<?xml version="1.0" encoding="utf-8"?> <android.support.design.widget.CoordinatorLayout 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="match_parent"> <android.support.design.widget.AppBarLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:fitsSystemWindows="true"> <android.support.v7.widget.Toolbar android:id="@+id/toolbar_main" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" app:layout_scrollFlags="scroll|enterAlways" /> </android.support.design.widget.AppBarLayout> <FrameLayout android:id="@+id/frame_layout" android:layout_width="match_parent" android:layout_height="match_parent" app:layout_behavior="@string/appbar_scrolling_view_behavior"/> </android.support.design.widget.CoordinatorLayout>
Now create Kotlin classes named as FragmentOne.kt
, FragmentTwo.java
, etc. The FragmentOne.kt
file called the fragment_one.xml file and so on.
Code for FragmentOne.kt
file.
class FragmentOne : Fragment() { companion object { fun newInstance(): FragmentOne = FragmentOne() } override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? = inflater!!.inflate(R.layout.fragment_one, container, false) }
Layout for FragmentOne.kt
.
<?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:orientation="vertical"> <TextView android:id="@+id/textView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:text="Item 1" android:textAppearance="?android:attr/textAppearanceLarge"/> </RelativeLayout>
Finally update MainActivity
.
class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelectedListener { private lateinit var drawer: DrawerLayout private lateinit var toggle: ActionBarDrawerToggle override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val navigationView: NavigationView = findViewById(R.id.nav_view) navigationView.setNavigationItemSelectedListener(this) val toolbar: Toolbar = findViewById(R.id.toolbar_main) setSupportActionBar(toolbar) drawer = findViewById(R.id.drawer_layout) toggle = ActionBarDrawerToggle(this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close) drawer.addDrawerListener(toggle) supportActionBar?.setDisplayHomeAsUpEnabled(true) supportActionBar?.setHomeButtonEnabled(true) loadFragment(R.id.nav_item_one); } private fun loadFragment(itemId: Int) { var fragment: Fragment? = null when (itemId) { R.id.nav_item_one -> fragment = FragmentOne() R.id.nav_item_two -> fragment = FragmentTwo() } if (fragment != null) { val fragmentTransaction = supportFragmentManager.beginTransaction() fragmentTransaction.replace(R.id.frame_layout, fragment) fragmentTransaction.commit() } drawer.closeDrawer(GravityCompat.START) } override fun onPostCreate(savedInstanceState: Bundle?) { super.onPostCreate(savedInstanceState) toggle.syncState() } override fun onConfigurationChanged(newConfig: Configuration?) { super.onConfigurationChanged(newConfig) toggle.onConfigurationChanged(newConfig) } override fun onOptionsItemSelected(item: MenuItem?): Boolean { if (toggle.onOptionsItemSelected(item)) { return true } return super.onOptionsItemSelected(item) } override fun onBackPressed() { if (drawer.isDrawerOpen(GravityCompat.START)) { drawer.closeDrawer(GravityCompat.START) } else { super.onBackPressed() } } override fun onNavigationItemSelected(item: MenuItem): Boolean { loadFragment(item.itemId); return true } }