How to Have Navigation Drawer setup with Navigatio

2020-03-05 06:34发布

问题:

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?

回答1:

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
    }


回答2:

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.