问题
In portrait mode, my ViewPager
has 3 fragments A, B, C but in landscape mode, it has only 2 fragments A and C. So I create 2 FragmentStatePagerAdapter
s for each mode. The problem is when screen orientation changed, ViewPager
restores and uses previous fragments of old orientation. For example, when change orientation from portrait to landscape, ViewPager
now shows 2 fragments A, B instead of A and C. I know why this happen but can't find a good solution for this.
My current workaround is to use different ids for ViewPager
(eg: id/viewpager_portrait for portrait and id/viewpager_landscape for landscape layout) to prevent from reusing fragments but this cause me a memory leak because old fragment will not be destroyed and still be kept in memory.
I have tried some workaround like call super.onCreate(null) in activity's onCreate
, or remove fragments of ViewPager
in activity's onSaveInstanceState
but they all makes my app crash.
So my question is how to avoid reusing one or many fragments in FragmentStatePagerAdapter
when orientation changed?
Any helps will be appreciated. Thank in advance.
回答1:
The issue probably is that the built-in PagerAdapter
implementations for Fragment
s provided by Android assume that the items will remain constant, and so retain and reuse index-based references to all Fragment
s that are added to the ViewPager
. These references are maintained through the FragmentManager
even after the Activity
(and Fragment
s) is recreated due to configuration changes or the process being killed.
What you need to do is to write your own implementation of PagerAdapter
that associates a custom tag with each Fragment
and stores the Fragment
s in a tag-based (instead of index-based) format. You could derive a generic implementation of this from one of the existing ones after adding an abstract method for providing a tag based on the index alongside the getItem()
method. Of course, you will have to remove orphaned/unused Fragment
s added in the previous configuration from the ViewPager
(while ideally holding on to it's state).
If you don't want to implement the whole solution yourself, then the ArrayPagerAdapter
in the CWAC-Pager library can be used to provide a reasonable implementation of this with little effort. Upon initialization, you can detach the relevant Fragment
based on it's provided tag, and remove/add it from the adapter as well, as appropriate.
回答2:
Override getItemPosition()
in your Adapter and return POSITION_NONE
. So when the ViewPager is recreated it will call getItemPosition()
and since you've returned POSITION_NONE
from here, it will call getItem()
. You should return the new fragments in from this getItem()
. Ref: http://developer.android.com/reference/android/support/v4/view/PagerAdapter.html#getItemPosition%28java.lang.Object%29
回答3:
Why use two different ids for your viewpager, when you can just remove Fragment B when your orientation changes?
You can retrieve your Fragments inside onCreateView() or onResume() like this (this example works inside a parent fragment, but is also usable inside a parent activity like in onResume() ):
@Override
public void onResume() {
super.onResume();
// ... initialize components, etc.
pager.setAdapter(pagerAdapter);
List<Fragment> children = getChildFragmentManager().getFragments();
if (children != null) {
pagerAdapter.restoreFragments(children, orientation);
}
}
Then inside your adapter:
@Override
public void restoreFragments(List<Fragment> fragments, int orientation) {
List<Fragment> fragmentsToAdd = new ArrayList<Fragment>();
Collections.fill(fragmentsToAdd, null);
if (Configuration.ORIENTATION_LANDSCAPE == orientation) {
for (Fragment f : fragments) {
if (!(f instanceof FragmentB)) {
fragmentsToAdd.add(f);
}
}
}
this.fragmentsInAdapter = Arrays.copyOf(temp.toArray(new Fragment[0]), this.fragments.length); // array of all your fragments in your adapter (always refresh them, when config changes, else you have old references in your array!
notifyDataSetChanged(); // notify, to remove FragmentB
}
That should work.
Btw. if you're using Support Library V13, you can't use FragmentManager.getFragments(), thus you'll need to get them by id or tag.
回答4:
Override OnConfigurationChanged() in your Activity (also add android:configChanges="orientation" to your activity in Manifest) this way you manage manually the the orientation change. Now "all" you have to do is to change the adapter in OnOrientaionChanged (also keep track of the current position). This way you use a single layout, a single ViewPager, and you don't have to worry about the fragment not getting recycled (well, you'll have plenty of work to make up for that). Good luck!
来源:https://stackoverflow.com/questions/23149850/viewpager-with-different-adapters-for-portrait-and-landscape