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
}
我有一个使用 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
}