ViewPager 和 FragmentPagerAdapter 中 Fragments 的 Android 生命周期管理 [英] Android Lifecycle management of Fragments within ViewPager and FragmentPagerAdapter

查看:18
本文介绍了ViewPager 和 FragmentPagerAdapter 中 Fragments 的 Android 生命周期管理的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在努力找出使用 ViewPagerFragmentActivity 中正确管理片段的方法是什么.在我详细介绍之前,我所面临的问题的简要总结如下:

I have been struggling to find out what the correct management of Fragments within a FragmentActivity with a ViewPager is. Before I go into details, a quick summary of the issue that I am facing is the following:

我有一个带有 ViewPagerFragmentActivity.ViewPager 使用自定义但非常简单的 FragmentPagerAdapter.ViewPager 中的每个Fragment 都包含一个ExpandableListView.我还有一个名为刷新"的操作栏按钮.现在,我们假设 ViewPager 只有一个 Fragment.Activity 被创建,FragmentExpandableListView 被填充(到目前为止一切都很好).单击 Refresh 按钮时,FragmentActivity 中的处理方法会遍历分配给 FragmentPagerAdapter 的 Fragments 列表并调用 refresh() 在每个 Fragment 上填充其 ListView.但是,当设备的方向发生变化时(例如从纵向变为横向),Activity 会重新创建,片段也会重新创建.现在单击 Refresh 按钮将遍历未初始化的 Fragments.

I have a FragmentActivity with a ViewPager. The ViewPager uses a custom, yet very simple FragmentPagerAdapter. Each Fragment within the ViewPager comprises of an ExpandableListView. I also have an action bar button called "Refresh". For now, let's assume that the ViewPager has only one Fragment. The activity is created, and the Fragment's ExpandableListView is populated (so far so good). When the Refresh button is clicked, the handling method within the FragmentActivity iterates over the list of Fragments that are assigned to the FragmentPagerAdapter and calls refresh() on each Fragment to populate its ListView. However, when the orientation of the device changes (e.g. from portrait to landscape), the Activity is recreated and so are the fragments. Clicking the Refresh button now will iterate over non-initialised Fragments.

我知道我说的很模糊,尤其是没有示例代码,但请多多包涵.我从应用程序/活动的开始跟踪问题和方法调用如下:

I know that I am being quite vague, especially without sample code, but please bear with me. I have traced the problem and method calls as follows from the start of the application/activity:

  1. FragmentActivity.onCreate()
  2. FragmentActivity.setContentView()
  3. FragmentActivity.createPagerFragments() <-- 这会创建一个 Fragment 的 ArrayList 并将它们分配给一个新的 FragmentPagerAdapter,然后再分配给 ViewPager.
  4. Fragment.onAttach()
  5. Fragment.onCreate() <--这里没什么特别的,只是调用了super方法.
  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. <<崩溃:片段中的 mExpandableListView 出现 NullPointerException.>>
  1. FragmentActivity.onCreate()
  2. FragmentActivity.setContentView()
  3. FragmentActivity.createPagerFragments() <-- this creates an ArrayList of Fragments and assignes them to a new FragmentPagerAdapter which is in turn assigned to the ViewPager.
  4. Fragment.onAttach()
  5. Fragment.onCreate() <-- nothing special here, just calling the super method.
  6. Fragment.onCreateView() <-- nothing special here either, just inflating the layout
  7. Fragment.onActivityCreated() <-- nothing here either.
  8. << All good, orientation changes here >>
  9. FragmentActivity.onCreate()
  10. Fragment.onAttach()
  11. Fragment.onCreate()
  12. FragmentActivity.setContentView()
  13. FragmentActivity.createPagerFragments()
  14. Fragment.onCreateView()
  15. Fragment.onActivityCreated()
  16. << Refresh button clicked >>
  17. FragmentActivity.refresh() <-- iterates over the newly created Fragments from #13 (not these by Android!).
  18. << Crash: NullPointerException for mExpandableListView in Fragment. >>

所以,在我看来,问题如下:当 Android 在屏幕方向改变后重新创建 FragmentActivity 及其 Views 时(上面调用 #9-15),它会创建新的 Fragment对象的状态恢复到原来的状态.但是,这些似乎完全由 FragmentManager 管理,而不是由 FragmentPagerAdapter 管理.相反,当 FragmentPagerAdapter 与 Activity 的 onCreate 方法中的 Fragments 一起重新创建时(参见调用 #13),分配给适配器的 Fragment 根本不会调用其 Fragment.onCreate()Fragment.onCreateView() 方法.因此,当调用 refresh() 方法时(参见 #17),该方法会遍历这些尚未初始化的 Fragments.因此,当他们尝试填充 ExpandableListView 时,视图的实例变量为 NULL.这是意料之中的,因为实例变量仅在 Fragment.onCreateView() 方法中分配,永远不会在这些 Fragments 上调用.

So the problem, as I see it, is as follows: When Android re-creates the FragmentActivity and its Views after a change of screen orientation (calls #9-15 above), it creates new Fragment objects with their state restored to what the original ones were. However, these ones appear to be completely managed by the FragmentManager, and not by the FragmentPagerAdapter. In contrast, when the FragmentPagerAdapter is re-created along with the Fragments in the activity's onCreate method (see call #13) the Fragments that get assigned to the adapter never have their Fragment.onCreate() or Fragment.onCreateView() methods called at all. So when the refresh() method is called (see #17) the method iterates over these Fragments that have not been initialised. Therefore, when they try to populate the ExpandableListView, the view's instance variable is NULL. This is to be expected as the instance variable is only assigned in the Fragment.onCreateView() method that never gets called on these Fragments.

所以我的问题是:如何在屏幕方向改变后正确地重新使用重新创建的(由 Android)Fragments 以避免创建没有初始化的新的?我需要对它们有一个有效的引用,以便调用它们的 refresh() 方法来按需填充它们.理想情况下,它们也应该分配给 FragmentPagerAdapter.

So my question is: how does one properly make re-use of the re-recreated (by Android) Fragments after the screen orientation has changed in order to avoid creating new ones that don't get initialised? I need to have a valid reference to them, in order to call their refresh() method that populates them on-demand. Ideally, they should also be assigned to the FragmentPagerAdapter as well.

我希望我已经清楚地描述了这个问题,而我没有提供示例代码的原因是因为问题(可以看出)不是来自代码本身,而是来自一个相当不正确(看似)的重新 -创建片段而不是重复使用.但是如果需要,我可以给你示例代码,我只是通过这种方式会更清楚.

I hope I have been clear in describing the issue, and the reason that I have not provided sample code is because the problem (as can be seen) is not from the code itself but from a rather incorrect (seemigly) re-creation of Fragments rather than re-use. But if needed, I can give you sample code, I just through this way would be clearer.

谢谢!

推荐答案

读了很多东西,但是看了介绍和问题,并且对类似于 FragmentPagerAdapter 的 FragmentStatePagerAdapter 有经验后,我可以告诉你:

It's lot to read, but after reading just introduction and the question and having experience with FragmentStatePagerAdapter, which is similar to FragmentPagerAdapter I can tell you that:

旋转后,您的适配器将自动附加旧片段.因此,似乎虽然正在重新创建活动创建适配器,但 FragmentManager 是全局的,并且它的实例保留活动的重新创建将检测到新的 FragmentStatePagerAdapter 与相同的 ViewPager 组合并要求相同的 Fragment,并且会简单地从 Fragment 的 BackStack 中获取它们.

After rotation your adapter will AUTOMAGICALLY attach old fragments. So it seems that although activity creating adapter is being recreated, FragmentManager, which is global and it's instance preserve activity's recreation will detect that new FragmentStatePagerAdapter is combined with the same ViewPager and is asking for the same Fragments and will simply fetch them from Fragment's BackStack.

作为 Fragment 的设计者,您可以通过继续调用 Fragment.onAttach() 和 Fragment.onDetach() 来注意到这种行为.当 onAttach() 发生时,它要么是创建 Fragment,要么是在旋转后重用它.您应该能够区分 Fragment 是使用回调 onRestoreRnstanceState() 旋转的.

You as designer of Fragments can notice this behavior by continues invocation of Fragment.onAttach() and Fragment.onDetach(). When onAttach() occurs it's either creation of your Fragment or reusing it after rotation. You should be able to distinguish that Fragment was rotated with use of callback onRestoreRnstanceState().

您将在日志中同时看到许多 onCreate() 和其他状态日志,因为 FragmentStatePagerAdapter 总是获取/创建至少 3 个片段(除非您将它们设置为仅 2 或 1 个),因此屏幕旋转后 3 个片段也会从后台重新附加.

You will see in your logs many onCreate() and other states logs simultaneously, because FragmentStatePagerAdapter always fetches/creates min 3 Fragments (except if you set that they are only 2 or 1), so also after screen rotation 3 fragments will be reattached from backstack.

希望对你有所帮助.

这篇关于ViewPager 和 FragmentPagerAdapter 中 Fragments 的 Android 生命周期管理的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆