ViewPager with various adapters for portrait and landscape orientation

In portrait mode, my ViewPager has 3 fragments A, B, C, but in landscape mode it has only 2 fragments A and C. Therefore, I create 2 FragmentStatePagerAdapter for each mode. The problem is changing the orientation of the screen, ViewPager restores and uses the previous fragments of the old orientation. For example, when changing the orientation from portrait to landscape, the ViewPager now shows 2 fragments A, B instead of A and C. I know why this happens, but cannot find a good solution for this.

My current workaround is to use different identifiers for the ViewPager (for example: id / viewpager_portrait for the portrait and id / viewpager_landscape for the landscape layout) to prevent reusing fragments, but this causes me a memory leak because the old fragment will not be destroyed and that's it still stored in memory.

I tried some workaround, for example, call super.onCreate (null) in onCreate activity, or delete ViewPager fragments in the ViewPager action, but all of them cause my application to crash.

So my question is how to avoid reusing one or more fragments in the FragmentStatePagerAdapter when changing orientation?

Any help would be appreciated. Thanks in advance.

+6
source share
4 answers

The problem probably is that the built-in PagerAdapter implementations of the Fragment provided by Android assume that the elements will remain constant, so keep and reuse index-based links for all the Fragment that are added to the ViewPager . These links are maintained by the FragmentManager even after the Activity (and Fragment s) is recreated due to changes to the configuration or process that was killed.

What you need to do is write your own implementation of the PagerAdapter , which associates a custom tag with each Fragment and saves the Fragment in a tag-based format (instead of an index). You can get a general implementation of this from one of the existing ones after adding an abstract method to provide an index-based tag along with the getItem() method. Of course, you will need to remove the lost / unused Fragment added in the previous configuration from the ViewPager (while ideally holding its state).

If you do not want to implement the entire solution yourself, then the ArrayPagerAdapter in the CWAC-Pager library can be used to provide a reasonable implementation of this with minimal effort. After initialization, you can disconnect the corresponding Fragment based on the provided tag and remove / add it also from the adapter.

+3
source

Cancel getItemPosition() in your adapter and return POSITION_NONE . Therefore, when the ViewPager is recreated, it will call getItemPosition() , and since you returned POSITION_NONE from here, it will call getItem() . You should return new fragments from this getItem() . Link: http://developer.android.com/reference/android/support/v4/view/PagerAdapter.html#getItemPosition%28java.lang.Object%29

+1
source

Why use two different identifiers for your viewpager, when you can just delete fragment B when changing orientation?

You can get your fragments inside onCreateView () or onResume () like this (this example works inside the parent fragment, but can also be used inside the parent action, for example inResume ()):

 @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 use the V13 support library, you cannot use FragmentManager.getFragments (), so you will need to get them by id or tag.

+1
source

Override OnConfigurationChanged () in your activity (also add android: configChanges = "orientation" to your activity in the manifest) so you manually control the orientation change. Now "all" you need to change the adapter in OnOrientaionChanged (also track the current position). This way you use a single layout, one ViewPager, and you don’t have to worry about the fragment being not processed (well, you will have a lot of work to compensate for this). Good luck

+1
source

All Articles