31

I'm working on android application that contains ViewPager with Fragments, like this:

MainActivity(MainActivityFragment(screenSlideViewPager(Fragments))), which means:

Activity contains Fragment contains ViewPager contains Fragments

Now my problem is when rotate device or change screen rotation all Fragments in ViewPager are disappear.

any ideas to solve this issue?

EDIT 1

MainActivity.java:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    if (getSupportFragmentManager().findFragmentByTag(TAG) == null) {
        final FragmentTransaction ft = getSupportFragmentManager()
                .beginTransaction();
        ft.add(android.R.id.content, new MainActivityFragment(), TAG);
        ft.commit();
    }
}

MainActivityFragment.java:

public class MainActivityFragment extends Fragment {
    private ViewPager mPager = null;
    private PagerAdapter mPageAdapter = null;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        final View reVal = inflater.inflate(R.layout.activity_main, container, false);
        mPager = (ViewPager) reVal.findViewById(R.id.pagerMainContent);
        mPageAdapter = new ScreenSlidePagerAdapter(getActivity().getFragmentManager());
        mPager.setAdapter(mPageAdapter);
        return reVal;
    }

    private MainGridViewFragment mainGridFragment;
    private AlphabetGridViewFragment alphabetGridFragment;

    private class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter {
        public ScreenSlidePagerAdapter(FragmentManager fm) {
            super(fm);
        }

        @Override
        public android.app.Fragment getItem(int position) {
            switch (position) {
                case 1:
                    mainGridFragment = new MainGridViewFragment();
                    return mainGridFragment;
                case 0:
                    alphabetGridFragment = new AlphabetGridViewFragment();
                    return alphabetGridFragment;
                default:
                    return null;
            }
        }

        @Override
        public int getCount() {
            return 2;
        }
    }
}

EDIT 2 The Activity After Screen Rotation

Obviously MainActivity and MainActivityFragment are loaded, and the proof is The ActionBar, notice also that ViewPager is loaded too, because you still can navigate between pages (the blue light means that the pager reached to last page) but you can't see its content.

Heysem Katibi
  • 1,808
  • 2
  • 14
  • 29
  • Good question, but we need more code to help you! – Saw Aug 06 '13 at 09:04
  • When there is a screen rotation in Android, your activity is destroyed and recreated. Therefore, you need to save the state of your variables and such so that you can "continue where you left off". This may help http://developer.android.com/training/basics/activity-lifecycle/recreating.html – Stu Whyte Aug 06 '13 at 09:07
  • 1
    I know, i have already saved activity state, but this is not destroy issue, Everything is disappearing even the views, i mean i am facing white screen when device rotation changed (without exceptions) – Heysem Katibi Aug 06 '13 at 09:13
  • Can you paste your code on the activity which this is happening with? – Stu Whyte Aug 06 '13 at 09:15
  • OK, i will update my question – Heysem Katibi Aug 06 '13 at 09:16
  • MainGridViewFragment, AlphabetGridViewFragment code is very simple(contains onCreateView() only) please tell me if you want to see it – Heysem Katibi Aug 06 '13 at 09:28
  • Does it get in the if statement after screen rotation on your MainAcytivity.java onCreate()? – Stu Whyte Aug 06 '13 at 09:40
  • No, it isn't. seems that it get in if statement first time only. – Heysem Katibi Aug 06 '13 at 09:43
  • shall i remove if statement from onCreate() Method?? – Heysem Katibi Aug 06 '13 at 09:45
  • I don't see where in your code you retain your state after your screen rotation. So if you remove your if, it looks like your activity will be "fresh" - but it will work. Perhaps create public void onRestoreInstanceState(Bundle savedInstanceState) method and do want you want to do after the screen rotation there. – Stu Whyte Aug 06 '13 at 09:48
  • No no, if you didn't restore instance state you will face (for example) empty `EditText` but the `EditText` View itself will not disappear, isn't that?? – Heysem Katibi Aug 06 '13 at 09:55
  • Yes, but as it is not getting in the if statement, your not setting your contentview so it doesn't load your views – Stu Whyte Aug 06 '13 at 10:01

8 Answers8

21

It looks like your main activity is using the getSupportFragmentManager() while your fragment is using getFragmentManager().

Not sure if this is worthy of an answer post but my rating is too low to reply any other way. :)

Edit: I believe you may also need to extend a FragmentActivity with the support library.

See: https://stackoverflow.com/a/10609839/2640693

Edit 2:

public class MainActivityFragment extends FragmentActivity {
    private ViewPager mPager = null;
    private PagerAdapter mPageAdapter = null;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        final View reVal = inflater.inflate(R.layout.activity_main, container, false);
        mPager = (ViewPager) reVal.findViewById(R.id.pagerMainContent);
        mPageAdapter = new ScreenSlidePagerAdapter(getChildFragmentManager());
        mPager.setAdapter(mPageAdapter);
        return reVal;
    }

    private MainGridViewFragment mainGridFragment;
    private AlphabetGridViewFragment alphabetGridFragment;

    private class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter {
        public ScreenSlidePagerAdapter(FragmentManager fm) {
            super(fm);
        }

        @Override
        public Fragment getItem(int position) {
            switch (position) {
                case 1:
                    mainGridFragment = new MainGridViewFragment();
                    return (Fragment)mainGridFragment;
                case 0:
                    alphabetGridFragment = new AlphabetGridViewFragment();
                    return (Fragment)alphabetGridFragment;
                default:
                    return null;
            }
        }

        @Override
        public int getCount() {
            return 2;
        }
    }
}
Community
  • 1
  • 1
Jon
  • 1,398
  • 9
  • 14
  • your link is useful, but it explain another issue – Heysem Katibi Aug 06 '13 at 10:00
  • Is it normal to use both Fragment Managers? I was under the impression you would get two separate objects doing so. Therefore your first fragment would be using the support one and the second non support manager would be lost when you created a new activity. – Jon Aug 06 '13 at 10:04
  • I'am using `getFragmentManager()` because is returns `android.app.FragmentManager`, and `getSupportFragmentManager()` because it returns `android.support.v4.app.FragmentManager` that means thay don't return the same object. which one shall i use?? – Heysem Katibi Aug 06 '13 at 10:25
  • If you're app is used on pre-honeycomb devices you'll need to use the support library. It's almost completely the same except for a few things like the `getSupportFragmentManager`. The easiest way I've found is to just ensure there are no imports from `android.app.Fragment` `android.app.FragmentManager` `android.app.FragmentTransaction` etc. They all should be from the support library. – Jon Aug 06 '13 at 12:32
  • i am using ICS but `ViewPager` is exist only in support library. – Heysem Katibi Aug 24 '13 at 09:37
  • I changed your code above to use the library. You may need adjust the rest of your project to match and clean/rebuild your project. – Jon Aug 24 '13 at 17:15
  • 6
    look like getChildFragmentManager() helps to solve my similar problem – Truong Nguyen Jul 22 '14 at 10:16
  • Thanks! You save me a lot of time :) – Jacob Dam Dec 05 '14 at 13:21
  • I was using FragmentPagerAdapter rather of FragmentStatePagerAdapter as you using thank you very much! import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentPagerAdapter; – Ivor Dec 17 '15 at 22:27
9

I tried many solution but get success in any one. At last I fixed as below:

  • Use getActivity().getSupportFragmentManager() rather than getChildFragmentManager()

  • Override getItemId(int position) method in your ViewPagerAdapter class like below:

    @Override
    public long getItemId(int position) {
        return System.currentTimeMillis();
    }
    

For me it's working like charm, please also comment here your view.

Kalpesh
  • 1,767
  • 19
  • 29
  • 2
    Overriding getItemId helped me immensely. I never would have figured that out. I'm guessing the ViewPager must cache the fragments based on ID somehow. Either way, thank you. – steve Aug 13 '17 at 17:33
  • if anyone can't override `getItemId()` use `FragmentPagerAdapter` instead of `FragmentStatePagerAdapter` – NJY404 Nov 24 '19 at 16:49
5

For the ones that are having problems with this you can try this:

private void InitializeUI(){
mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());
mViewPager = (ViewPager) findViewById(R.id.container);
mViewPager.setAdapter(mSectionsPagerAdapter);

}

@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
setContentView(R.layout.activity_exame2);
InitializeUI();
}

i spent 2 days around this problem this was the only thing that worked for me, remeber to add android:configChanges="screenSize|orientation" to your activity in manifest

Tiago Oliveira
  • 1,582
  • 1
  • 16
  • 31
3

Avoid creating the viewpager again. Put the code of onCreateView in onCreate(). And make your fragment use onRetainInstance(true). Hence this shall not recreate the fragment and again because the view is in the onCreate(), it will not be called again.

Check in the onCreateView() whether the view exists, if so then remove it. Snippets:

//creates the view
private View createView(Bundle savedInstanceState) {
    View view = getActivity().getLayoutInflater().inflate(your_layout, null, false);

    //viewpager

    return view;
}

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    //retains fragment instance across Activity re-creation
    setRetainInstance(true);

    viewRoot = createView(savedInstanceState);
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {

    if (viewRoot != null && viewRoot.getParent() != null) {
        ViewGroup parent = (ViewGroup) viewRoot.getParent();
        parent.removeView(viewRoot);

        Log.i("Test", "**************view.getParent(): " + viewRoot.getParent() + "    " + viewRoot);
    }

    return viewRoot;
}
android developer
  • 1,253
  • 2
  • 13
  • 43
3

I had this exact same problem. It's tremendously confusing, but this thread helped me out except that the answers didn't really explain why it works.

The fix to this problem is to use Fragment.getChildFragmentManager(). What's so confusing is the android API has Activity.getFragmentManager(), but if you are trying to support older devices you need to use Activity.getSupportFragmentManager(). However, this particular problem isn't about supporting older APIs.

The problem is that you have a Activity which has a Fragment which owns the ViewPager which owns several Fragments. You have Fragments within Fragments! The original design of Fragments didn't really handle this use case and later was updated to handle it. When you have Fragments within Fragments you need to use:

Fragment.getChildFragmentManager()

From within a Fragment you have the choice of Fragment.getFragmentManager() which returns the FragmentManager of the Activity vs Fragment.getChildFragmentManager() which returns the FragmentManager for this Fragment which properly scopes the Fragments within your Fragment.

chubbsondubs
  • 37,646
  • 24
  • 106
  • 138
1

You should rather use a getChildFragmentManager() instead of the getFragmentManager(). You used

mPageAdapter = new ScreenSlidePagerAdapter(getActivity().getFragmentManager());

it should have been

mPageAdapter = new ScreenSlidePagerAdapter(getActivity().getChildFragmentManager());

in your fragment

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
  • 1
    This is actually correct except that getAcitivity().getChildFragmentManager() does not exist. It shouldn't have been down voted. – chubbsondubs Jun 25 '16 at 13:03
  • You should use it like this: `pagerAdapter = new PagerAdapter(getChildFragmentManager());` – AlexS Nov 09 '18 at 13:04
1

In my case there is activity A which has fragment F1, which has viewpager V1, which has fragment F2, which has viewpager V2, which has fragment F3. :)

This last fragment F3, when rotated, either gives blank or error.

My solution was to:

  1. Remove PageChangeListener, so basicly not use fragment transactions when changing fragments, instead leave as it is default.
  2. Use FragmentStatePagerAdapter instead of FragmentPagerAdapter
  3. Override getItemPosition() in adapter and return POSITION_NONE
  4. Create a field with List of Fragments in adapter and set it in constructor
  5. In getItem() in adapter just return fragmentList.get(position)
  6. In getCount() in adapter just return fragmentList.size()
  7. NOT OVERRIDE any other methods in adapter like destroy, instantiate etc
  8. Create and instantiate V2 adapter in onCreateView() of F2

EDIT

When for example need to replace V1 in F1 with V2 in F2 then:

  1. Enclose V1 inside say relative layout with F1_container id
  2. Use getSupportFragmentManager() and begin transaction
  3. Remove F1
  4. Replace F1_container with F2
  5. Add to backstack
  6. Commit transaction

Later when need to comeback from F2 to F1 call popBackStack() on valid A reference.

And think thats it, took more than a day of changing each of those and restarting app and see result. Hope this helps.

10101101
  • 193
  • 1
  • 13
0

I have been using databinding while having fragment A hosting the viewPager, so far rotation wasn't an issue but navigating to fragment B and coming back, and then rotating was the issue. It turned out I was assigning parentFragmentManager instead of childFragmentManager. This is such basic fundamentals of Android development, and I am embarrassed to have forgotten this :)