导航组件防止在后按时重新创建片段
Posted
技术标签:
【中文标题】导航组件防止在后按时重新创建片段【英文标题】:Navigation Component prevent to recreate fragment on back press 【发布时间】:2019-06-30 09:20:06 【问题描述】:我在我的项目中使用 Jetpack 导航组件,其中包含一个活动和一些片段。
我有一个片段,其中包含一个从服务器端填充的列表。我在onViewCreated
方法上调用getDataFromServer
然后,当用户单击一个项目时,会显示一个新片段。
问题是当我按下返回按钮时,onViewCreated
在我的列表片段中再次被调用。
那么,如何防止我的第一个片段再次重新创建?我不想要不必要的onViewCreated
电话。
【问题讨论】:
通过在你的片段中覆盖它来使用 onActivityCreated 函数,并在那里获取你的 getDataFromServer! @Rizwanatta 这是一个好技巧,我会这样做,但我正在等待更好的答案。谢谢 由于您使用的是导航组件,因此您可能也应该使用 ViewModels。它让生活更轻松 这就是他们发明ViewModel
+ fragment.getViewLifecycleOwner()
的原因。
你可以从NetworkBoundResource, where it is an effect of observing the LiveData inside the ViewModel获得一些灵感
【参考方案1】:
当然,我们不能阻止调用oncrateView,但是有一个简单的方法。 我们可以在初始化 ViewModel 时调用它,而不是在 onCreateView 或其他生命周期方法中调用 view.loadData()
这篇文章帮助我更好地了解 ViewModel 5 common mistakes when using Architecture Components
更新:
当前的导航组件 (V 2.3.0) 不支持此功能,它总是在导航到另一个 Fragment 时杀死该 Fragment。想象一下,您在 Fragment A 中有谷歌地图,因此每次返回 Fragment 时,它都会再次初始化,并且相机会移动到用户位置! (真是个坏主意)。
所以如果你有同样的问题,最好的办法就是不要使用导航组件。
Navigation, Saving fragment state, GitHub issue
更新 2:
在某些情况下,例如过滤器或分页,我们可以在 ViewModel 中使用 switchMap 之类的转换,而不是在 init 函数中获取数据。
更新 3:
如果您必须调用一个函数来从源加载数据,有很多方法可以防止再次调用该函数,第一种也是最简单的方法是在视图中调用 getData(),而不是在 ViewModel init 中调用功能。第二个是使用惰性变量,另一个是在 livedata 上使用 SwitchMap。有关更多信息,您可以找到所有解决方案here
【讨论】:
你找到解决办法了吗? @iamkdblue 不,这个问题没有任何解决方案。但你可以用一些技巧来做到这一点。例如,不要从片段中调用 getItems(),而是在 ViewModel 初始化函数中执行此操作,或者在视图模型中使用 switch map 之类的转换。 即使使用ViewModel
,我们仍然在最后重新创建基本/家庭片段,这可能非常昂贵,对吗?【参考方案2】:
也许你已经激活了图表。
app:popUpTo="@+id/nav_fingerprint_capture"
app:popUpToInclusive="true"
【讨论】:
【参考方案3】:当按下后退按钮时,您不能阻止调用 onViewCreated 方法或片段的任何方法,因此您应该更好地将视图模型与列表片段一起使用,并在视图模型中从服务器获取数据。避免在片段中从服务器获取数据,因为您已经在使用导航 UI。
【讨论】:
那么我应该在哪里告诉 ViewModel 获取数据? 在视图模型类中。也许您了解视图模型,这是一种更好的方法。 我的意思是在片段中我应该告诉哪里查看模型?我也有分页。好的,在我的视图模型中我得到了数据,但是当在 onViewCreated 方法中再次重新创建片段时,我告诉 ViewModel 获取数据,它会得到它,但是我有它,而且我有分页,我怎么知道得到数据是否用于分页? 看看这个要点viewmodel和mainFragment 即使使用ViewModel
,它也不能阻止重新创建基本/主片段视图的事实,这可能非常昂贵,对吧?【参考方案4】:
Jetpack 导航组件替换了片段。它不会将片段添加到堆栈中。因此,当您从 Fragment A 打开 Fragment B 并单击 Fragment A 中的返回按钮时,会重新创建 Fragment B。
如果 Fragment A 中有 API 调用,并且您不想在每次片段重新创建时都进行 API 调用,那么您可以将 API 响应保存在视图模型中,并在片段重新创建时重用响应。
例子:
override fun onViewCreated(view: View, savedInstanceState: Bundle?)
if(foodViewModel.foodDataList.isEmpty())
//make API call here and
//save the response in foodViewModel.foodDataList
else
//use the saved response from viewmodel
//and populate recyclerview
【讨论】:
这并不能防止您再次填充片段视图,这可能非常昂贵。 @MihaeKheel 是的,我知道。我确实看到了这个问题的任何其他可能的解决方案。从本质上讲,Jetpack Navigation 会替换 Fragments。以上是关于导航组件防止在后按时重新创建片段的主要内容,如果未能解决你的问题,请参考以下文章
Vue JS 2. 如何防止在两个父级之间导航时重新加载相同的嵌套组件?