0

我在 Android 导航架构的片段中取得了进展。

当我在最后一个屏幕上按下键或在屏幕上放置一个按钮并将按钮功能设置为

val navıgattor = activity.findNavController(R.id.nav_host_fragment)
navigator.popBackStack()

并点击,我回到了上一个fragment,但是又调用了上一个fragment中的onCreateView()方法。我期待它保持正常行为的状态。

我在哪里做错了?

4

3 回答 3

4

我分两部分回答——

解决方案:

每次返回上一个片段时不要放大视图。将 View 保存在局部变量中并仅对其进行一次膨胀。由Ian Lake推荐 这里

private var savedViewInstance: View? = null

override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
): View? {
    return if (savedViewInstance != null) {
        savedViewInstance
    } else {
        savedViewInstance =
                inflater.inflate(R.layout.fragment_professional_details, container, false)
        savedViewInstance
    }
}

解释

您得到的行为是默认行为,片段将在您每次调用navigator.popBackStack()或使用设备上的后退按钮时重新创建它们的视图。

让我们了解导航架构下片段的生命周期。

场景:我们采用两个片段,HomeFragment 和 DashboardFragment。两个片段都属于同一个 NavGraph,起始目的地是 Home Fragment。

应用程序启动时的片段生命周期-

HomeFragment:onAttach:

HomeFragment: onCreate:

HomeFragment:onCreateView:

HomeFragment:onViewCreated:

HomeFragment:onActivityCreated:

HomeFragment:onStart:

HomeFragment: onResume:

在导航上:主页片段 ---> 仪表板片段

仪表板片段:onAttach:

仪表板片段:onCreate:

仪表板片段:onCreateView:

仪表板片段:onViewCreated:

DashboardFragment:onActivityCreated:

仪表板片段:onStart:

仪表板片段: onResume:

HomeFragment:onPause:

HomeFragment:onStop:

HomeFragment: onDestroyView:

在导航上:仪表板片段 ---> 主页片段

HomeFragment:onAttach:

HomeFragment: onCreate:

HomeFragment:onCreateView:

HomeFragment:onViewCreated:

HomeFragment:onActivityCreated:

HomeFragment:onStart:

HomeFragment: onResume:

DashboardFragment:onPause:

仪表板片段: onStop:

HomeFragment: onDestroy:

仪表板片段:onDestroyView:

仪表板片段:onDestroy:

如果我们在初始 HomeFragment: onCreateView() 上保存视图并在每次调用 HomeFragment: onCreateView() 时膨胀相同的视图,我们可以恢复旧视图。

如果您注意到HomeFragment: onDestroy()将被调用但之后HomeFragment: onViewCreated()已调用。调用HomeFragment: onDestroy()只是破坏 HomeFragment 的旧实例。

我仍然相信这种做事方式不是最佳实践,但直到谷歌会提出类似onFragemntRestore().

另一方面,假设每次删除或替换片段时都会重新创建片段,并且您应该使用onSaveInstanceState().

ViewModel 来了保存片段状态并恢复它们。要实际更新视图,您必须需要 ViewModel 并观察视图中的更改。

在此处输入图像描述 简而言之,如果something无论您身在何处,都可以为您的视图处理数据,如果您回到相同的位置而没有任何变化,那么您就会知道您之前的看法something。那something就是ViewModel

还有很多其他值得一读的关于同一个主题的文章,比如这个这个这个

快乐编码!

于 2020-08-10T14:20:03.573 回答
2

您没有做错任何事情,这种行为对于片段来说是正常的。当您导航到另一个片段时,片段管理器只保存事务而不保存当前片段的状态。

要保存片段的实例状态,您可以重写该onSaveInstanceState()函数并将数据放入 out-state bundle 中。要检索保存的数据,您可以覆盖onActivityCreated()并检查数据是否包含在 savedInstanceState 包中。

您可以在此处查看片段生命周期:

在此处输入图像描述

于 2020-08-10T13:49:12.083 回答
0

从片段文档:

/**
 * Called when the view previously created by {@link #onCreateView} has
 * been detached from the fragment.  The next time the fragment needs
 * to be displayed, a new view will be created.  This is called
 * after {@link #onStop()} and before {@link #onDestroy()}.  It is called
 * <em>regardless</em> of whether {@link #onCreateView} returned a
 * non-null view.  Internally it is called after the view's state has
 * been saved but before it has been removed from its parent.
 */
public void onDestroyView()

因此,无论何时调用 onDestroyView (无论出于何种原因),当片段回到顶部时,您都会看到 onCreateView 被调用。为了重新获得视图的状态,需要考虑一些工具(您可以决定哪种工具或它们的哪种组合适合您):

1-覆盖onSaveInstanceState,将当前状态作为bundle传递并在android系统以bundle作为参数调用onCreateView时重用它。

2- 使用包含视图状态信息的ViewModel,该信息与 Fragment、其 View 或其父 Activity 绑定,并与此类信息一起被销毁。

3- 将 onCreateView 返回的 View 对象的副本保留在 Fragment 中,如果再次调用 onCreateView 时不为空,则重新使用它(谨慎使用,因为某些视图可能会在重绘时遇到麻烦,例如OSM MapView):

private var viewCopy : View? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
    if(viewCopy!=null)
        return viewCopy
    ...
    viewCopy = inflatedView
    return viewCopy
}
于 2020-08-10T14:42:28.883 回答