ViewPager和FragmentPagerAdapter中的片段的Android生命周期管理 [英] Android Lifecycle management of Fragments within ViewPager and FragmentPagerAdapter
问题描述
我一直在努力寻找用ViewPager
在FragmentActivity
中对Fragments的正确管理是什么.在详细介绍之前,我先简要概述一下我所面临的问题:
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:
我有一个FragmentActivity
和一个ViewPager
. ViewPager
使用自定义但非常简单的FragmentPagerAdapter
. ViewPager
中的每个Fragment
都包含一个ExpandableListView
.我还有一个名为刷新"的操作栏按钮.现在,让我们假设ViewPager
只有一个Fragment
.活动已创建,并且Fragment
的ExpandableListView
已填充(到目前为止非常好).单击刷新"按钮时,FragmentActivity
中的处理方法将遍历分配给FragmentPagerAdapter的Fragments
列表,并在每个Fragment
上调用refresh()
来填充其ListView.但是,当设备的方向发生变化(例如,从纵向更改为横向)时,将重新创建活动",并且也会重新创建片段.现在,单击刷新"按钮将遍历未初始化的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:
-
FragmentActivity.onCreate()
-
FragmentActivity.setContentView()
-
FragmentActivity.createPagerFragments()
<-创建一个ArrayArray of Fragments,并将它们分配给新的FragmentPagerAdapter,后者又分配给ViewPager. -
Fragment.onAttach()
-
Fragment.onCreate()
<-在这里没有什么特别的,只是调用了super方法. -
Fragment.onCreateView()
<-在这里也没什么特别的,只是夸大了布局 -
Fragment.onActivityCreated()
<-这里也没有. - <<一切都很好,方向在这里>>
-
FragmentActivity.onCreate()
-
Fragment.onAttach()
-
Fragment.onCreate()
-
FragmentActivity.setContentView()
-
FragmentActivity.createPagerFragments()
-
Fragment.onCreateView()
-
Fragment.onActivityCreated()
- <<单击刷新按钮>>
-
FragmentActivity.refresh()
<-迭代#13上新创建的片段(Android则不行!). - <<崩溃:Fragment中的mExpandableListView发生NullPointerException. >>
FragmentActivity.onCreate()
FragmentActivity.setContentView()
FragmentActivity.createPagerFragments()
<-- this creates an ArrayList of Fragments and assignes them to a new FragmentPagerAdapter which is in turn assigned to the ViewPager.Fragment.onAttach()
Fragment.onCreate()
<-- nothing special here, just calling the super method.Fragment.onCreateView()
<-- nothing special here either, just inflating the layoutFragment.onActivityCreated()
<-- nothing here either.- << All good, orientation changes here >>
FragmentActivity.onCreate()
Fragment.onAttach()
Fragment.onCreate()
FragmentActivity.setContentView()
FragmentActivity.createPagerFragments()
Fragment.onCreateView()
Fragment.onActivityCreated()
- << Refresh button clicked >>
FragmentActivity.refresh()
<-- iterates over the newly created Fragments from #13 (not these by Android!).- << Crash: NullPointerException for mExpandableListView in Fragment. >>
因此,正如我所看到的,问题如下:
当Android在更改屏幕方向后重新创建FragmentActivity
及其Views
时(上面的调用#9-15),它将创建新的Fragment
对象,其状态恢复为原始状态.但是,这些文件似乎完全由FragmentManager
而不是FragmentPagerAdapter
进行管理.相反,在活动的onCreate
方法中重新创建FragmentPagerAdapter
和Fragments
时(请参阅调用#13),分配给适配器的Fragments
永远不会具有其Fragment.onCreate()
或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.
我希望我已经清楚地描述了这个问题,而我没有提供示例代码的原因是因为(可以看到)问题不是源于代码本身,而是源于一个相当不正确的(seemigly)re-创建碎片而不是重复使用.但是如果需要,我可以给您示例代码,通过这种方式我会更清楚.
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.
谢谢!
推荐答案
要阅读很多东西,但是在阅读了介绍和问题并具有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组合在一起并请求相同的Fragments,并且只需从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.
您作为Fragments的设计者可以通过继续调用Fragment.onAttach()和Fragment.onDetach()来注意到此行为.发生onAttach()时,要么创建您的Fragment,要么在旋转后重新使用它.您应该能够通过使用回调onRestoreRnstanceState()来区分Fragment是旋转的.
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中的片段的Android生命周期管理的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!