Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to set up Navigation Component with Navigation Drawer in Android?

How do I set up navigation component with navigation drawer? How do I use it in my app?

Can everything be done with one Activity?

How do I handle toolbar visibility with just one Activity and fragments which have a dynamic toolbar visibility. Also, there are fragments which I need to close the drawer and make it inaccessible.

This question is a self answered question and works more as a tutorial than a real-life QA.

like image 873
coroutineDispatcher Avatar asked Dec 12 '19 12:12

coroutineDispatcher


People also ask

Where is the navigation drawer on my Android phone?

The user can view the navigation drawer when they swipe a finger from the left edge of the activity. They can also find it from the home activity (the top level of the app) by tapping the app icon (also known as the Android "hamburger" menu) in the action bar.

Which view is used to add the navigation drawer to an app?

Figure 3. An open drawer displaying a navigation menu. The drawer icon is displayed on all top-level destinations that use a DrawerLayout . To add a navigation drawer, first declare a DrawerLayout as the root view.


1 Answers

How do I set up navigation component with navigation drawer?

How do I use it in my app?

The navigation drawer set up differs a little when it comes to Navigation component.

Note, if you create a new app with drawer navigation, the current tutorial is not needed. However I'm going to explain some things that might look strange around here and if you decide to add a drawer at a later stage of the app

First, you need to set up your activity_main.xml and MainActivity to be ready for the Navigation Architecture:

<?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/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" />

    <com.google.android.material.navigation.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" />

</androidx.drawerlayout.widget.DrawerLayout>

where app_bar_main is just:

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

    <com.google.android.material.appbar.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        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>

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

</androidx.coordinatorlayout.widget.CoordinatorLayout>

And the content_main is where your fragments are going to be held:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:showIn="@layout/app_bar_main">

    <fragment
        android:id="@+id/nav_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:defaultNavHost="true"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:navGraph="@navigation/mobile_navigation" />
</androidx.constraintlayout.widget.ConstraintLayout>

Things you should know: The activity mustn't have a AppBar set on AndroidManifest.xml:

android:theme="@style/AppTheme.NoActionBar"

If you notice app:menu="@menu/activity_main_drawer" in the NavigationView tag, the fragment names should be the same that they are inside the mobile_navigation.xml:

<group android:checkableBehavior="single">
        <item
            android:id="@+id/homeFragment"
            android:icon="@drawable/ic_menu_camera"
            android:title="@string/menu_home" />
        <item
            android:id="@+id/galleryFragment"
            android:icon="@drawable/ic_menu_gallery"
            android:title="@string/menu_gallery" />
        <item
            android:id="@+id/slideshowFragment"
            android:icon="@drawable/ic_menu_slideshow"
            android:title="@string/menu_slideshow" />
        <item
            android:id="@+id/toolsFragment"
            android:icon="@drawable/ic_menu_manage"
            android:title="@string/menu_tools" />
    </group>

    <item android:title="Communicate">
        <menu>
            <item
                android:id="@+id/shareFragment"
                android:icon="@drawable/ic_menu_share"
                android:title="@string/menu_share" />
            <item
                android:id="@+id/sendFragment"
                android:icon="@drawable/ic_menu_send"
                android:title="@string/menu_send" />
        </menu>
    </item>

</menu>

This way, with what is going to be explained below there will be no need to call for onCreateOptionsMenu to detect the clicks. Android team has already solved that for us. Follow below.

Until now, this doesn't differ too much from the usual drawer set up we actually do. But, there are some configurations we would need to do in the logic part of the app. So let's open MainActivity.kt. First you will need these:

  private var appBarConfiguration: AppBarConfiguration? = null
  private var drawerLayout: DrawerLayout? = null
  private var toolbar: Toolbar? = null
  private var navController: NavController? = null

After that in your onCreate method:

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        toolbar = findViewById(R.id.toolbar)
        setSupportActionBar(toolbar) //set the toolbar

        drawerLayout = findViewById(R.id.drawer_layout)
        val navView: NavigationView = findViewById(R.id.nav_view)
        navController = findNavController(R.id.nav_host_fragment)
        appBarConfiguration = AppBarConfiguration(
            setOf(
                R.id.homeFragment,
                R.id.galleryFragment,
                R.id.slideShowFragment,
                R.id.toolsFragment,
                R.id.shareFragment,
                R.id.sendFragment,
                R.id.loginFragment,
                R.id.phoneConfirmationFragment
            ), drawerLayout
        )

        setupActionBarWithNavController(navController!!, appBarConfiguration!!) //the most important part
        navView.setupWithNavController(navController!!) //the second most important part

      //other things unrelated
    }

Let's see what's going on here:

First you would need a reference to the navController. The AppBarConfiguration is just a class which holds the fragments that are going to be opened as top level destinations. Which means that the fragment which is going to be opened after them would release the current one from the fragment back stack. It's important to tell to the AppBarConfiguration that we have a drawer also (passed as a parameter in the constructor).

Down below you would have a method called onSupportNavigateUp():

override fun onSupportNavigateUp(): Boolean {
        val navController = findNavController(R.id.nav_host_fragment)
        return navController.navigateUp(appBarConfiguration!!) || super.onSupportNavigateUp()
    }

This method has to do with the up back button. But you won't need it too much if you have drawer navigation. This really comes in handy when you have a lot of fragments added on the backstack (or at least two).

Can everything be done with one Activity?

Yes, definitely! but still it requires a little bit more work when it comes to conditional navigation. Like when you want to show fragments which are not part of your drawer app. But still Google has done a huge progress with it. You can refer to conditional navigation here.

How do I handle toolbar visibility with just one Activity and fragments which have a dynamic toolbar visibility. Also, there are fragments which I need to close the drawer and make it inaccessible.

You can use addOnDestinationChangedListener from the navController:

navController.addOnDestinationChangedListener { _, destination, _ ->
            when (destination.id) {
                R.id.loginFragment, R.id.registerFragment, R.id.phoneConfirmationFragment -> {
                    toolbar?.visibility = View.GONE
                    drawerLayout?.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
                }
                else -> {
                    toolbar?.visibility = View.VISIBLE
                    drawerLayout?.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED)
                }
            }
        }

Now you have a drawer and navigation component on your app.

like image 121
coroutineDispatcher Avatar answered Oct 07 '22 20:10

coroutineDispatcher