Android 在将BottomNavigationView与新的NavController一起使用时,有没有办法保持片段的活动状态?

Android 在将BottomNavigationView与新的NavController一起使用时,有没有办法保持片段的活动状态?,android,navigation,bottomnavigationview,Android,Navigation,Bottomnavigationview,我正在尝试使用新的导航组件。我将BottomNavigationView与navController一起使用:NavigationUI.setupWithNavController(bottomNavigation,navController) 但当我切换片段时,它们每次都会被销毁/创建,即使它们以前被使用过 有没有办法让我们的主片段链接保持活力,链接到我们的BottomNavigationView?试试这个 领航员 创建自定义导航器 @Navigator.Name(“custom_fragme

我正在尝试使用新的导航组件。我将BottomNavigationView与navController一起使用:
NavigationUI.setupWithNavController(bottomNavigation,navController)

但当我切换片段时,它们每次都会被销毁/创建,即使它们以前被使用过

有没有办法让我们的主片段链接保持活力,链接到我们的BottomNavigationView?

试试这个

领航员 创建自定义导航器

@Navigator.Name(“custom_fragment”)//在navigation.xml中用作自定义标记
类CustomNavigator(
私有val上下文:上下文,
私人val经理:碎片经理,
私人val集装箱ID:Int
):FragmentNavigator(上下文、管理器、容器ID){
覆盖有趣的导航(目的地:目的地,参数:Bundle?,导航选项:导航选项?){
val tag=destination.id.toString()
val事务=manager.beginTransaction()
val currentFragment=manager.primaryNavigationFragment
如果(currentFragment!=null){
事务分离(currentFragment)
}
var fragment=manager.findFragmentByTag(标记)
if(片段==null){
fragment=destination.createFragment(args)
事务.添加(containerId、片段、标记)
}否则{
事务。附加(片段)
}
transaction.setPrimaryNavigationFragment(片段)
transaction.setReorderingAllowed(true)
commit()事务
DispatchOnNavigator导航(destination.id,BACK\u STACK\u destination\u添加)
}
}
导航碎片 创建自定义NavHostFragment

class CustomNavHostFragment:NavHostFragment(){
override fun onCreateNavController(navController:navController){
super.onCreateNavController(navController)
navController.navigatorProvider+=PersistentNavigator(上下文!!,childFragmentManager,id)
}
}
navigation.xml 使用自定义标记而不是片段标记


活动布局 使用CustomNavHostFragment而不是NavHostFragment


我不创建自定义的NavHostFragment。我使用的
navController.navigatorProvider+=navigator

目前不可用

作为一种解决方法,您可以将所有获取的数据存储到视图模型中,并在重新创建片段时使这些数据随时可用。确保使用活动上下文获取视图


您可以使用LiveData使您的数据生命周期意识可观察到

更新19.05.2021多回退
自Jetpack Navigation 2.4.0-alpha01以来,我们已经将其开箱即用。 检查

旧答案:

只需将NavigationExtensions复制到应用程序并通过示例进行配置。效果很好。

经过几个小时的研究,我找到了解决办法。它一直就在我们面前:)有一个函数:
popBackStack(包括目的地)
,如果在backStack中找到它,它将导航到给定的目的地。它返回布尔值,因此如果控制器找不到片段,我们可以手动导航到那里

if(findNavController().popBackStack(R.id.settingsFragment, false)) {
        Log.d(TAG, "SettingsFragment found in backStack")
    } else {
        Log.d(TAG, "SettingsFragment not found in backStack, navigate manually")
        findNavController().navigate(R.id.settingsFragment)
    }

我使用了@STAR_ZERO提供的链接,效果很好。对于那些对后退按钮有问题的人,您可以在activity/nav主机中这样处理它

override fun onBackPressed() {
        if(navController.currentDestination!!.id!=R.id.homeFragment){
            navController.navigate(R.id.homeFragment)
        }else{
            super.onBackPressed()
        }
    }
只需检查当前目的地是否是您的根/主片段(通常是底部导航视图中的第一个),如果不是,只需导航回该片段,如果是,只需退出应用程序或执行任何您想要的操作


顺便说一句,这个解决方案需要与上面STAR_ZERO提供的解决方案链接一起使用keep_state_fragment由@piotr prus提供的解决方案帮助了我,但我必须添加一些当前目的地检查:

if (navController.currentDestination?.id == resId) {
    return       //do not navigate
}

如果没有此检查,如果您错误地导航到当前目标,则将重新创建该目标,因为在后堆栈中找不到它。

如果您在传递参数时遇到问题,请添加:

fragment.arguments=args


在class
KeepStateNavigator

中,如果您在这里只是为了在使用
BottomNavigationView
NavController
在片段之间导航时保持精确的
循环视图
滚动状态,然后有一种简单的方法,将
layoutManager
状态存储在
onDestroyView
中,并在
onCreateView

我使用ActivityViewModel来存储状态。如果您使用的是不同的方法,请确保将状态存储在父活动或任何比片段本身存活时间更长的内容中

碎片

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    recyclerview.adapter = MyAdapter()
    activityViewModel.listStateParcel?.let { parcelable ->
        recyclerview.layoutManager?.onRestoreInstanceState(parcelable)
        activityViewModel.listStateParcel = null
    }
}

override fun onDestroyView() {
    val listState = planet_list?.layoutManager?.onSaveInstanceState()
    listState?.let { activityViewModel.saveListState(it) }
    super.onDestroyView()
}
视图模型

var plantListStateParcel: Parcelable? = null

fun savePlanetListState(parcel: Parcelable) {
    plantListStateParcel = parcel
}

根据评论,这似乎最终会得到解决。不过,我也很好奇,人们是否有一个廉价的解决方案,不需要放弃库来在过渡期间实现这一点。虽然这个解决方案有效(
片段被重用,而不是重新创建),但导航存在问题。
bottomnavigationview
中的每个
menuitem
都被认为是主要的-因此,当按下
BACK
时,应用程序完成(或转到顶部组件)。一个更好的解决方案是像YouTube应用程序一样:(1)点击主页;(2) 抽头趋势;(3) 点击收件箱;(4) 抽头趋势;(5) 轻触
返回
-进入收件箱;(6) 点击
BACK
-返回主页;(7) 点击
BACK
-应用程序存在。总之,YouTube
BACK
在“
menuitems
”之间的功能可以追溯到最新的,没有重复的项目。@jL4,我也有同样的问题,可能您忘记了从活动的NavHostFragment中删除
app:navGraph
。为什么google不将此功能从盒子中取出?NavigationComponent应该成为一个解决方案,而不是另一个问题,所以程序员必须创建类似这样的解决方法。@SyedAhmedJamil我的答案是旧的。所以请检查这个存储库这是indee