Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ViewPager + FragmentStatePagerAdapter + orientation change

I have a little Problem: i have a ViewPager with some Pages and i use the FragmentStatePagerAdapter to handle the data. In portrait i have for example 20 pages for the ViewPager and in landscape i have just 10 pages for the ViewPager. So on a orientation change i create a new Adapter with different Data.

Here a little explanation why: I show one image in portrait and two in landscape but always all in count. If i have 10 pictures to show, i have 10 in portrait and 5 in landscape (always two).

But now i have a strange bug: When i am in Landscape on index 5 and turn the device, the ViewPager's current page is set to 10. If i turn it back again i am on page 5. If i swipe the ViewPage now to page 10 the methode getItem of the adapter is never called and the ViewPage shows me the one portrait picture and not the two for landscape. How can this happen? Is there a cache in the adapter or ViewPager? In the onCreate of the Activity everything is new created, the adapter and also the data (just strings with path) for the ViewPager. So any idea how to fix this really scary "feature"?

Here are some code:

onCreate:

    mViewPagerAdapter = new ReaderPageViewAdapter(getSupportFragmentManager(), getBaseContext(), mCurrentDocument.mPages, getResources()
                .getConfiguration().orientation);
    mPageReader = (ReaderViewPager) findViewById(R.id.pager);
    mPageReader.setAdapter(mViewPagerAdapter);

adapter getItem:

    public Fragment getItem(final int index) {
        final PageInfo pageInfo = mPages.get(index);
        final PageFragment pageFragment = (PageFragment) PageFragment
                .instantiate(mContext, pageInfo.mClss.getName(), pageInfo.mArgs);
        return pageFragment;
    }

If you need something more just tell me. Thanks

like image 597
Informatic0re Avatar asked Dec 17 '12 08:12

Informatic0re


2 Answers

You can override the FragmentActivity onSaveInstanceState() and not call the super.onSaveInstanceState() in the method.

@Override
protected void onSaveInstanceState(final Bundle outState) {
    // super.onSaveInstanceState(outState);
}

Do not use mViewPager.setSaveEnabled(false);

it ended up with a big memoryLeak. After every change of the orientation it puts the Fragments into an array of FragmentManager and never cleaned it. So it goes up to over 100mb memory usage after change the orientation many times. Using the onSaveInstanceState method is a better way I think.

like image 106
Informatic0re Avatar answered Nov 16 '22 19:11

Informatic0re


I also occurred this problem and I found the reason.

Ever screen rotated, The FragmentManager will retain all your activity hosted fragments, It wou't remove fragments If using FragmentStatePagerAdapter, You can use FragmentManager.getFragments() to check the number of fragments.

Example: The activity has 4 fragments, After 4 times orientation change, The list FragmentManager.getFragments() size will be 20!

I found some solutions:

  1. Do not call the super.onSaveInstanceState() of you Activity, But It's seems not a good solution, Fragments can't save state and restore.

  2. Use FragmentPagerAdapter instead of FragmentStatePagerAdapter. But after orientation changed, The fragments which ViewPager hosted was blanked(Does anyone else has same problem?). But I can remove all fragments in Activity.onCreate to fix it. like this:

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        FragmentManager fm = getSupportFragmentManager();
        List<Fragment> fragments = fm.getFragments();
        if (fragments != null) {
            FragmentTransaction transaction = fm.beginTransaction();
            for (Fragment fragment : fragments) {
                transaction.detach(fragment).remove(fragment);
            }
            transaction.commitNowAllowingStateLoss();
        }
        ...
    

    But the Fragment.onCreateView() called twice.

I'm still looking for the perfect solution :D

Edit:
I finally solved the problem, It's cause by my ViewPager is add after onStart. When run to onStart, The FragmentManager will try to restore fragments state. I traced into FragmentManager, Have these code:

com/android/support/support-fragment/24.2.1/support-fragment-24.2.1-sources.jar!/android/support/v4/app/FragmentManager.java

Check the highlight line, The f.mContainerId actually is my ViewPager id. Then it will try to add fragments view to ViewPager, In my case, The ViewPager is not add yet, So it will be blanked, But fragments state are ATTACHED. And in FragmentPagerAdapter.instantiateItem(), If the fragment was added, It just do attach the fragment, But the fragment is already attached, So its do nothing :(

I add ViewPager to activity layout XML, its fixed.

Happy coding :D

like image 2
TJT Avatar answered Nov 16 '22 20:11

TJT