防止导航到同一个片段

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 中定义,我需要在代码中执行此操作。

【讨论】:

以上是关于防止导航到同一个片段的主要内容,如果未能解决你的问题,请参考以下文章

使用导航控制器按下后退按钮后如何防止前一个片段显示?

在片段的后按防止使用导航图调用前一个片段的 onViewCreated

导航组件防止在后按时重新创建片段

如何防止在背面片段导航上再次设置视图模型

使用导航控制器按下后退按钮后,如何防止先前的片段出现?

使用底部导航栏防止片段刷新