Layout
This is the main content layout. There could be a upper hierarchy layout which hold CoordinatorLayout
, AppBarLayout
and Toolbar
.
<androidx.drawerlayout.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"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
>
<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:navGraph="@navigation/mobile_navigation"
/>
<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:menu="@menu/test_drawer"
/>
</androidx.drawerlayout.widget.DrawerLayout>
Menu
res/menu/test_drawer
<menu xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/action_item1"
android:title="Item 1" />
<item
android:id="@+id/action_item2"
android:title="Item 2" />
</menu>
Activity
class MainActivity : AppCompatActivity() { private val navController by lazy { findNavController(R.id.nav_host_fragment) } private lateinit var appBarConfiguration: AppBarConfiguration override fun onCreate(savedInstanceState: Bundle?) { ... val appBarConfiguration = AppBarConfiguration( setOf( R.id.home, R.id.search, R.id.profile // top level destinations ), drawerLayout // Navigation Drawer ) setupActionBarWithNavController(navController, appBarConfiguration) navView.setupWithNavController(navController) // Navigation Drawer bottomNavView.setupWithNavController(navController) } override fun onSupportNavigateUp(): Boolean { // return navController.navigateUp() // This will misbehave when using with BottomNavigationView: after clicking on 2nd or 3rd Tab, the Hamburger icon trigger back // instead of showing Drawer // return navController.navigateUp(drawerLayout) || super.onSupportNavigateUp() return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp() } // enable close drawer on back pressed override fun onBackPressed() { if (this.drawerLayout.isDrawerOpen(GravityCompat.START)) { this.drawerLayout.closeDrawer(GravityCompat.START) } else { super.onBackPressed() } }}
Show/Hide Drawer on specific fragment
I wanted to achive the following
- Hide drawer on main bottomNavView fragments (R.id.home, R.id.search, R.id.profile)
- Show drawer on specific fragment (R.id.import_photos)
Add R.id.import_photos
as top level destinations for drawer to be shown.
val appBarConfiguration = AppBarConfiguration( setOf( R.id.home, R.id.search, R.id.profile, R.id.import_photos // top level destinations ), drawerLayout)
Seems like the only way to hide the Drawer is via supportActionBar?.setDisplayHomeAsUpEnabled(false)
(actually hiding the button which trigger the drawer).
navController.addOnDestinationChangedListener { controller, destination, arguments -> when(destination.id) { R.id.home, R.id.search, R.id.profile -> { // bottomNavView supportActionBar?.setDisplayHomeAsUpEnabled(false) bottomNavView.isVisible = true } R.id.import_photos -> { supportActionBar?.setDisplayHomeAsUpEnabled(true) // dynamic loading of menu navView.menu.clear() navView.inflateMenu(R.menu.import_photos_drawer) bottomNavView.isGone = true } else -> { bottomNavView.isGone = true } }}
/res/menu/import_photos_drawer
.
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/action_gallery"
android:title="Gallery"
android:checkable="true" />
<item
android:id="@+id/action_google_photos"
android:title="Google Photos"
android:checkable="true" />
</menu>
Handle Drawer/NavigationView Item Click in Activity
navView.setNavigationItemSelectedListener { item -> drawerLayout.closeDrawer(GravityCompat.START, false) // navView.setCheckedItem(item) when(item.itemId) { R.id.action_gallery -> { supportActionBar?.title = "Gallery" true } R.id.action_google_photos -> { supportActionBar?.title = "Google Photos" true } else -> false }}
Handle Drawer/NavigationView Item Click in Fragment
Direct access activity (tight coupling)
class ImportPhotosFragment : Fragment() { override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) // ... val activity = activity as? AppCompatActivity activity?.navView?.setNavigationItemSelectedListener { item -> activity.drawerLayout.closeDrawer(GravityCompat.START, false) when(item.itemId) { R.id.action_gallery -> { activity.supportActionBar?.title = "Gallery" true } R.id.action_google_photos -> { activity.supportActionBar?.title = "Google Photos" true } else -> false } } }}
Via LiveData
class MainActivity : AppCompatActivity() { private val viewModel by viewModels<MainViewModel>() override fun onCreate(savedInstanceState: Bundle?) { // ... navView.setNavigationItemSelectedListener { item -> drawerLayout.closeDrawer(GravityCompat.START, false) viewModel.navigationDrawerItemClickEvent.value = Event(item.itemId) true } viewModel.titleEvent.observe(this, Observer { event -> event.getIfPending()?.also { title -> supportActionBar?.title = title } }) }}
class MainViewModel : ViewModel() { internal val navigationDrawerItemClickEvent = MutableLiveData<Event<Int>>() internal val titleEvent = MutableLiveData<Event<String>>()}
class ImportPhotosFragment : Fragment() { private val viewModel by viewModels<ImportPhotosViewModel>() private val mainViewModel by activityViewModels<MainViewModel>() override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) mainViewModel.navigationDrawerItemClickEvent.observe(this, Observer { event -> event.getIfPending()?.also { menuId -> when(menuId) { R.id.action_gallery -> { mainViewModel.titleEvent.value = Event("Gallery") } R.id.action_google_photos -> { mainViewModel.titleEvent.value = Event("Google Photos") } } } }) }}
NOTE: Refer Get ViewModel via KTX.
References: