androidx.navigation 菜单项的多个目的地

androidx.navigation multiple destinations for menu item

我有一个使用 androidx 导航库的 activity 应用程序。对于其中一个菜单目标,我实际上有一个片段作为目标,没有任何视图,这取决于用户提供的配置的状态,要么重定向到应该存在的真实目标,要么重定向到当前两个不同视图之一,告诉用户他需要先设置一个配置,或者当前没有活动配置(已删除?),他需要 select 一个可用配置。

现在,从功能上讲,这种方法非常有效。但是,由于 androidx 导航通过 id 将菜单项与目的地联系起来,因此将您带到该视图的菜单项是 never selected 因为它与没有视图的片段目的地匹配.

我尝试将 NavController.OnDestinationChangedListener 添加到我的 Activity 并将其添加到 navController navController.addOnDestinationChangedListener(this)。但是之后好像被导航覆盖了

override fun onDestinationChanged(controller: NavController, destination: NavDestination, arguments: Bundle?) {
    val destinations = listOf(R.id.destinationA, R.id.destinationB, R.id.destinationC)

    if(destinations.contains(destination.id)) {
        nav_view.menu.getItem(0).isChecked = true
    }
}

绝对是正确的菜单项。当我将 isChecked = true 更改为 isEnabled = false 时,我无法再点击它。

此外,当我做这个奇怪的 hack 时它有效

GlobalScope.launch(Dispatchers.Main) {
    delay(1000)
    nav_view.menu.getItem(0).isChecked = true
}

不用说这不是一个很好的解决方案。


这里有人知道如何在这方面覆盖 androidx 导航的默认行为吗?

如果我找到合适的解决方案,我会稍后再回来报告。

向抽屉打开添加一个侦听器并设置 selected 菜单项可能是一个很好的解决方法,如果目前无法做到的话。

我会将此添加为可能的解决方案并暂时坚持使用。我仍然觉得应该有更好的方法来做到这一点,所以我不会接受它作为awnswer。

这基本上是我在写完问题时得到的想法

Adding a listener to the drawer opening and setting the selected menu item then might be a good workaround for this if it is not possible to do currently.

class SetActiveMenuDrawerListener(
        private val navController: NavController,
        navigationView: NavigationView) : DrawerLayout.DrawerListener {

    private var checked = false
    private val destinations = listOf(R.id.destinationA, R.id.destinationB, R.id.destinationC)
    private val menu = navigationView.menu.getItem(0)

    init {
        navController.addOnDestinationChangedListener { _, _, _ -> checked = false }
    }

    override fun onDrawerSlide(drawerView: View, slideOffset: Float) {
    }

    override fun onDrawerOpened(drawerView: View) {
    }

    override fun onDrawerClosed(drawerView: View) {
    }

    override fun onDrawerStateChanged(newState: Int) {
        if(checked) return
        val currentDestination = navController.currentDestination ?: return

        if(destinations.contains(currentDestination.id)) {
            menu.isChecked = true
        }
        checked = true
    }
}

然后将其添加到 DrawerLayout

drawer_layout.addDrawerListener(SetActiveMenuDrawerListener(navController, nav_view))

我确实将代码添加到 onDrawerStateChanged 而不是 onDrawerOpened,因为 onDrawerOpened 在单击抽屉时被调用有点晚,而在拖动它时根本不会被调用。

它看起来并不漂亮,但它完成了工作。

不要使用 setupWithNavController(),如 documentation 中所述,请自行设置。

如前所述 here,当您使用 setupWithNavController() 设置菜单项时,在单击菜单项时调用 NavigationUI 中的 onNavDestinationSelected() 辅助方法。所以你可以尝试这样的事情:

yourNavigationView.setNavigationItemSelectedListener { item: MenuItem ->
    if(item.itemId == R.id.noViewFragmentId) {
        val isConfigurationProvided = ...
        if(!isConfigurationProvided) {
            //Perform your actions (navigate to either of the two alternate views)
            return@setNavigationItemSelectedListener true
        }
    }

    val success = NavigationUI.onNavDestinationSelected(item, navController)

    if(success) {
        drawerLayout.closeDrawer(GravityCompat.START)
        item.isChecked = true
    }
            
    success
}