ViewPager和FragmentPagerAdapter中的Fragment的Android生命周期管理

14
我一直在努力找到在含有 ViewPager 的 FragmentActivity 中正确管理 Fragment 的方法。在详细说明问题之前,以下是我面临的问题的简要概述:
我有一个带有 ViewPager 的 FragmentActivity。ViewPager 使用一个自定义但非常简单的 FragmentPagerAdapter。ViewPager 中的每个 Fragment 都包含一个 ExpandableListView。我还有一个名为“Refresh”的操作栏按钮。现在,让我们假设 ViewPager 只有一个 Fragment。创建了活动,并填充了 Fragment 的 ExpandableListView(到目前为止都很好)。当点击刷新按钮时,FragmentActivity 中的处理方法会迭代分配给 FragmentPagerAdapter 的 Fragment 列表,并对每个 Fragment 调用 refresh() 以填充其 ListView。然而,当设备的方向发生变化(例如从纵向到横向),活动将被重新创建,Fragments 也将被重新创建。现在再次点击刷新按钮将迭代未初始化的 Fragments。
我知道我描述得很模糊,特别是没有示例代码。请耐心等待。我从应用程序/活动的开始一直跟踪问题和方法调用如下:
1. FragmentActivity.onCreate() 2. FragmentActivity.setContentView() 3. FragmentActivity.createPagerFragments() <-- 这将创建一个 Fragment 列表,并将它们分配给新的 FragmentPagerAdapter,该适配器又分配给 ViewPager。 4. Fragment.onAttach() 5. Fragment.onCreate() <-- 在这里没有什么特别之处,只是调用父方法。 6. Fragment.onCreateView() <-- 这里也没有什么特别之处,只是膨胀布局 7. Fragment.onActivityCreated() <-- 这里也没有任何东西。 8. <<一切正常,在这里更改方向>> 9. FragmentActivity.onCreate() 10. Fragment.onAttach() 11. Fragment.onCreate() 12. FragmentActivity.setContentView() 13. FragmentActivity.createPagerFragments() 14. Fragment.onCreateView() 15. Fragment.onActivityCreated() 16. <<点击刷新按钮>> 17. FragmentActivity.refresh() <-- 迭代从 #13 创建的新 Fragments(不是 Android!)。 18. <<崩溃:Fragment 中的 mExpandableListView 为空指针异常。>> 因此,我认为问题如下:当 Android 在屏幕方向更改后重新创建 FragmentActivity 及其 Views(调用上述 #9-15)时,它会创建具有其状态恢复为原始状态的新 Fragment 对象。但是,这些对象似乎完全由 FragmentManager 管理,而不是由 FragmentPagerAdapter 管理。相反,当 FragmentPagerAdapter 在活动的 onCreate 方法中与 Fragments 一起重新创建时(请参见调用#13),分配给适配器的 Fragments 没有调用它们的 Fragment.onCreate() 或 Fragment.onCreateView() 方法。所以当调用 refresh() 方法(请参见#17)时,该方法迭代这些未初始化的 Fragments。因此,当它们尝试填充 ExpandableListView 时,视图的实例变量为 NULL。预期这样,因为实例变量仅在未调用这些 Fragments 的 Fragment.onCreateView() 方法中分配。
因此,我的问题是:如何正确地重复使用屏幕方向更改后由 Android 重新创建的 Fragments,以避免创建不初始化的新 Fragments?我需要对它们有有效的引用,以调用它们的 refresh() 方法以按需填充它们。最理想情况下,它们还应分配给 FragmentPagerAdapter。
我希望我已经清楚地描述了问题,之所以没有提供示例代码的原因是,问题并不在于代码本身,而是因为错误的(看起来是)重新创建 Fragments 而不是重复使用。但是,如果需要,我可以提供示例代码,我只是认为这种方式会更清晰。谢谢!

嗨!我也遇到了同样的问题,你是怎么解决的呢?因为下面的答案并不是很有用。 - yozhik
3个回答

6
阅读起来很多,但是只需阅读引言和问题并且具有 FragmentStatePagerAdapter 经验,它与 FragmentPagerAdapter 类似,我可以告诉你:
旋转后,您的适配器将自动连接旧片段。因此,尽管创建适配器的活动正在重新创建,但是 FragmentManager(全局的,它的实例保留了活动的重建)将检测到新的 FragmentStatePagerAdapter 与相同的 ViewPager 组合,并且正在请求相同的片段,将简单地从片段的 BackStack 中获取它们。
您作为 Fragment 的设计者可以通过连续调用 Fragment.onAttach() 和 Fragment.onDetach() 来注意此行为。当 onAttach() 发生时,它可能是您的 Fragment 的创建或旋转后重用。您应该能够通过使用回调 onRestoreRnstanceState() 区分 Fragment 是否已旋转。
您将在日志中同时看到许多 onCreate() 和其他状态日志,因为 FragmentStatePagerAdapter 总是获取/创建至少 3 个 Fragment(除非您设置它们仅为 2 或 1),因此在屏幕旋转后,将重新附加 3 个片段从 backstack 中。
希望这有所帮助。

2

我相信这个关于从ViewPager中检索当前fragment的问题将会对您有所帮助。正如已经指出的那样,fragments是由Fragment(State)PagerAdapter管理的,而不是Activity或Fragment的生命周期。

  • 在activity第一次创建时,通过getItem方法返回fragments。即使activity重新创建,该方法也只会被调用一次。
  • 随后的时间里,fragments通过instantiateItem方法返回。很可能,这就是您需要获取fragments并调用它们的刷新方法的地方。

-4

你可以尝试将以下代码添加到你的Manifest文件中Activity标签内:

android:configChanges="orientation"

或者对于API 13或更高版本

android:configChanges="orientation|screenSize" 

这样当屏幕方向改变时,它就不会重新创建你的片段。


5
最好避免这种情况。其中一个原因是它不允许你为横向和纵向方向(来自XML)设置不同的布局。此外,它只是把问题藏了起来。当活动由于低内存条件而被销毁并重新创建时,会出现类似的情况。 - kgiannakakis

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接