Navigation Component: How to move the Drawer under the ToolBar?

Navigation Drawer Activity Template

Recently I was struggling with Navigation Component and succeded in solving some of its issues. I am happy to share with you the solutions I’ve found.

GitHub Repo: https://github.com/Pulimet/NavigationComponentSample

Create a project and select the template “Navigation Drawer Activity”. You will get the ToolBar, Drawer and navigation stuff for free as shown in the screenshot.

Now, let’s assume that we got a request to move the drawer under the ToolBar. We need to modify activity_main.xml for that.

Create vertical LinearLayout and make it wrapping DrawerLayout

Open app_bar_main.xml copy AppBarLayout and paste it as a first item in the previously created LinearLayout.

Also from the app_bar_main.xml get the include (content_main) and put it as a first child of DrawerLayout instead of existing include of app_bar_main.xml.

Delete app_bar_main.xml file

In v21\styles.xml – remove the line that makes status bar transparent.

Before editing (activity_main.xml):

<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.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/drawerLayout"
    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" />

    <com.google.android.material.navigation.NavigationView
        android:id="@+id/navView"
        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" />

</androidx.drawerlayout.widget.DrawerLayout>

The result (activity_main.xml):

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
    android:fitsSystemWindows="true"
    android:orientation="vertical">

    <com.google.android.material.appbar.AppBarLayout
        android:layout_height="wrap_content"
        android:layout_width="match_parent"
        android:theme="@style/AppTheme.AppBarOverlay">

        <androidx.appcompat.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:popupTheme="@style/AppTheme.PopupOverlay" />

    </com.google.android.material.appbar.AppBarLayout>

    <androidx.drawerlayout.widget.DrawerLayout
        android:id="@+id/drawerLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fitsSystemWindows="true"
        tools:openDrawer="start">

        <include layout="@layout/content_main"/>

        <com.google.android.material.navigation.NavigationView
            android:id="@+id/navView"
            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" />

    </androidx.drawerlayout.widget.DrawerLayout>
</LinearLayout>

The Drawer is under the ToolBar

Looks awesome, but we are missing the animation for the hamburger menu icon, while Drawer opens or closes.

To fix this we will add a DrawerListener. (The code is below, setDrawerListener())

But now, when the drawer is open and we click on the arrow, nothing is happing.

To fix this we need to handle the icon click event. (The code is below, fixNavigationIconBehavior())

Remove onSupportNavigateUp() method, since we will be doing it’s work instead:)

One last thing, we need to handle the back click by closing the drawer first. And just when the drawer closed, the back button should close the app.(onBackPressed())

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

private fun setToolBarAndNavigation() {
  setSupportActionBar(toolbar)
  navController = findNavController(R.id.nav_host_fragment)

  // Passing each menu ID as a set of Ids because each
  // menu should be considered as top level destinations.
  appBarConfiguration = AppBarConfiguration(
    setOf(
      R.id.nav_home, R.id.nav_gallery, R.id.nav_slideshow,
      R.id.nav_tools, R.id.nav_share, R.id.nav_send
    ), drawerLayout
  )

  setupActionBarWithNavController(navController, appBarConfiguration)
  navView.setupWithNavController(navController)

  menuDrawer = toolbar.navigationIcon as DrawerArrowDrawable
  setDrawerListener()
  fixNavigationIconBehavior()
}

private fun setDrawerListener() {
  drawerLayout.addDrawerListener(object : DrawerLayout.SimpleDrawerListener() {
    override fun onDrawerSlide(drawerView: View, slideOffset: Float) {
      menuDrawer.progress = slideOffset
    }
  })
}

private fun isMenuDrawerIsAnArrowNow() = menuDrawer.progress == 1.0f

private fun fixNavigationIconBehavior() {
  toolbar.setNavigationOnClickListener {
    if (drawerLayout.isDrawerOpen(GravityCompat.START)) {
      drawerLayout.closeDrawer(GravityCompat.START)
    } else {
      if (isMenuDrawerIsAnArrowNow()) {
        navController.navigateUp()
      } else {
        drawerLayout.openDrawer(GravityCompat.START)
      }
    }
  }
}

override fun onBackPressed() {
  when {
    drawerLayout.isDrawerOpen(GravityCompat.START) -> drawerLayout.closeDrawer(GravityCompat.START)
    else -> super.onBackPressed()
  }
}


Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.