I would like to have most of menu item handled by Navigation Component's controller, but I also want to handle one "Log out" menu item individually like this:
val navController = findNavController(R.id.nav_host_fragment)
nav_view.setNavigationItemSelectedListener { item ->
when(item.itemId) {
R.id.logout_menu_item -> {
Toast.makeText(context, "Logut Menu Item Touched", Toast.LENGTH_LONG).show()
true
}
else -> false
}
}
nav_view.setupWithNavController(navController)
bottom_navigation.setupWithNavController(navController)
Why this doesn't work, and how to resolve this issue?
I'm not sure that there is a proper way to do this at the moment, but you could always just not use the setupWithNavController
method.
I ran into this as well and, as a test, moved my call to setNavigationItemSelectedListener
to after the call to setupWithNavController
and and my navigation code for signing out was running but the rest of the navigation was not. I take this to mean that the navigationListener is overwritten on subsequent calls to setNavigationItemSelectedListener
and that setupWithNavController
calls setNavigationItemSelectedListener
internally.
I attempted to verify this in google source, but couldn't find the repo easily.
I worked around the problem by not calling setupWithNavController
and instead doing something like:
navigationDrawer?.setNavigationItemSelectedListener { menuItem ->
if (menuItem.itemId == R.id.action_sign_out) {
// sign out logic
return@setNavigationItemSelectedListener true
}
val result = menuItem.onNavDestinationSelected(navigationController)
drawerLayout?.closeDrawers()
result
}
Similar to Justin's answer I copied the listener setting code from NavigationUI like this:
fun NavigationView.setupWithNavController(navController: NavController, onMenuItemSelected: (MenuItem) -> Boolean) {
NavigationUI.setupWithNavController(this, navController) // because this does more than set the nav view listener
setNavigationItemSelectedListener { item ->
if (onMenuItemSelected(item)) {
return@setNavigationItemSelectedListener true
}
NavigationUI.onNavDestinationSelected(item, navController).also {
val parent = parent
if (parent is DrawerLayout) {
parent.closeDrawer(this)
} else {
findBottomSheetBehavior()?.state = BottomSheetBehavior.STATE_HIDDEN
}
}
}
}
private fun View.findBottomSheetBehavior(): BottomSheetBehavior<*>? {
val params = layoutParams
if (params !is CoordinatorLayout.LayoutParams) {
return (parent as? View)?.findBottomSheetBehavior()
}
return params.behavior as? BottomSheetBehavior<*>
}
and then, in Activity.onCreate()
:
navigation_view.setupWithNavController(navController) { item ->
when (item.itemId) {
R.id.mySpecialFragment ->
// do something special like: navController.popBackStack(id, true)
}
false // usually still want to perform default steps
}
A more bullet-proof solution might involve subclassing NavigationView
and overriding setNavigationItemSelectedListener()
so that you can wrap the actual listener being set by NavigationUI
with one that includes a call to your own listener passed in via some public setter.