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 visibi
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.