防止导航到同一个片段
Posted
技术标签:
【中文标题】防止导航到同一个片段【英文标题】:Prevent navigating to the same fragment 【发布时间】:2020-02-01 06:16:35 【问题描述】:我正在使用带有 BottomNavigationView 的 android 导航喷气背包库。我已经实现了 NavHost、NavGraph 和我的片段。当我使用操作进行导航时,一切都按预期工作。
我使用以下代码来设置所有内容:
val navController = Navigation.findNavController(this, R.id.nav_host)
bottom_navigation.setupWithNavController(navController)
问题是,如果我单击一个选项卡 2 次,片段会重新创建两次。有没有办法拦截导航?我不想导航到正在显示的同一片段。
【问题讨论】:
【参考方案1】:根据this issue,
随意设置OnNavigationItemReselectedListener,它优先于 NavigationUI 设置的
OnNavigationItemSelectedListener
。
val navController = Navigation.findNavController(this, R.id.nav_host)
bottom_navigation.setupWithNavController(navController)
bottom_navigation.setOnNavigationItemReselectedListener
// Do nothing to ignore the reselection
【讨论】:
谢谢。这似乎解决了娱乐问题。但是,如果我在同一个选定选项卡内从片段 A 导航到 B,再次单击此选项卡应该导航到片段 A,但它没有。我可以设置一个 NavigationItemSelectedListener 并进行一些检查,但这会搞砸bottom_navigation.setupWithNavController(navController)
purpose。我错过了什么?
如果您在重新选择时想要执行自定义逻辑(例如调用 navController.popBackStack(it.itemId, false)
返回该选项卡的第一个屏幕),那么您希望将该逻辑放入重新选择中倾听者而不是什么都不做。
非常感谢。我什至不知道 BottomNavigationView 有可用的侦听器。
坦克这么多,我花了时间搜索这些关键字
对不起,但对我来说这没有用。我面临着上述问题,但使用setOnNavigationItemReselectedListener navController.popBackStack(it.itemId, false)
,当通过按底部选项卡导航回A 时,从A 导航到B 后会重新创建片段。因为在这种情况下不会触发 setOnNavigationItemReselectedListener
。请大家帮帮我好吗?【参考方案2】:
在setOnItemSelectedListener里面使用:
if( item.getItemId() == navController.getCurrentDestination().getId()) return true;
因为 OnNavigationItemSelectedListener 现在已被弃用。
【讨论】:
【参考方案3】:我写了这个扩展。它将检查当前片段和目的地,如果两者相同,它只会关闭抽屉。但是关闭抽屉时存在一些动画问题。
fun NavigationView.setupWithUniqueFragment(navController: NavController)
this.setNavigationItemSelectedListener(object : NavigationView.OnNavigationItemSelectedListener
override fun onNavigationItemSelected(item: MenuItem): Boolean
val parent = this@setupWithUniqueFragment.parent
if (item.itemId == navController.currentDestination?.id)
if (parent is DrawerLayout)
parent.closeDrawer(this@setupWithUniqueFragment, true)
return true
val handled = NavigationUI.onNavDestinationSelected(item, navController)
if (handled)
if (parent is DrawerLayout)
parent.closeDrawer(this@setupWithUniqueFragment, true)
return handled
)
val weakReference = WeakReference<NavigationView>(this@setupWithUniqueFragment)
navController.addOnDestinationChangedListener(
object : NavController.OnDestinationChangedListener
override fun onDestinationChanged(
controller: NavController,
destination: NavDestination, arguments: Bundle?
)
val view = weakReference.get()
if (view == null)
navController.removeOnDestinationChangedListener(this)
return
val menu = view.menu
var h = 0
val size = menu.size()
while (h < size)
val item = menu.getItem(h)
item.isChecked = matchDestination(destination, item.itemId)
h++
)
internal fun matchDestination(
destination: NavDestination,
@IdRes destId: Int
): Boolean
var currentDestination: NavDestination? = destination
while (currentDestination!!.id != destId && currentDestination.parent != null)
currentDestination = currentDestination.parent
return currentDestination.id == destId
【讨论】:
【参考方案4】:我在使用 About 页面时遇到了同样的问题(使用非常好的 AboutLibraries),它会堆积重复的页面。我最终在我的 OnOptionsItemSelected 方法中这样做了
case R.id.action_about:
NavController navController = Navigation.findNavController( this, R.id.nav_host_fragment );
if ( navController.getCurrentDestination().getId() != R.id.nav_about )
navController.navigate( R.id.nav_about );
现在它只显示一个片段,不会将同一个片段堆叠在上面。很遗憾,这种行为无法在 XML 中定义,我需要在代码中执行此操作。
【讨论】:
以上是关于防止导航到同一个片段的主要内容,如果未能解决你的问题,请参考以下文章