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

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. 如何防止在两个父级之间导航时重新加载相同的嵌套组件?

如何防止在方向更改时重新创建片段寻呼机中的片段?

如何防止片段在活动重新创建时触发 onCreate onCreateView

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

防止导航到同一个片段

Android 组件导航如何防止地图页面在每次点击时刷新