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:
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()
<-- 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.FragmentActivity.onCreate()
Fragment.onAttach()
Fragment.onCreate()
FragmentActivity.setContentView()
FragmentActivity.createPagerFragments()
Fragment.onCreateView()
Fragment.onActivityCreated()
FragmentActivity.refresh()
<-- iterates over the newly created Fragments from #13 (not these by Android!).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
.
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.
Thank you!
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:
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.
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().
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.
I hope that it helped.