FragmentStatePagerAdapter, childFragmentManager and orientation change a bad combination?

android, android-fragments, android-viewpager, fragmentstatepageradapter, java

Solution

I had a similar problem with child fragments in a ViewPager.

The parent fragment created new instances of the child fragments to be used with the ViewPager while the adapter kept instances of the old child fragments used before the configuration change.

I ended up cleaning the ChildFragmentManager before initializing the adapter with it:

@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    mPager = (ViewPager) view.findViewById(R.id.pager);

    cleanChildFragments(getChildFragmentManager());

    mPagerAdapter = new MyPagerAdapter(getChildFragmentManager(), getTabFragments());
    mPager.setAdapter(mPagerAdapter);
}

/**
 * This is necessary to have a clean ChildFragmentManager, old fragments might be called otherwise
 * @param childFragmentManager
 */
private void cleanChildFragments(FragmentManager childFragmentManager) {
    List<Fragment> childFragments = childFragmentManager.getFragments();
    if (childFragments != null && !childFragments.isEmpty()) {
        FragmentTransaction ft = childFragmentManager.beginTransaction();
        for (Fragment fragment : childFragments) {
            ft.remove(fragment);
        }
        ft.commit();
    }
}

Maybe this helps someone...

Problem

Here is my the problem, I have an activity, which includes a Fragment that has a ViewPager using (FragmentStatePagerAdapter), all works perfect when the Activity loads for the first time, but when setting setRetainInstance(true) to the parent fragment (the one with the pager), and orientation changes on the activity, it causes java.lang.IllegalStateException: No activity When trying to add the saved fragment, here is the code: Activity: ``` public class DetailActivity{ ... @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.frame_layout_with_progress_container); ... // However, if we're being restored from a previous state, // then we don't need to do anything and should return or else // we could end up with overlapping fragments. FragmentManager fm = getSupportFragmentManager(); if (savedInstanceState != null) { Fragment f = fm.findFragmentById(R.id.container); if(f instanceof DetailPagerFragment){ detailPagerFragment = (DetailPagerFragment) f; } } } @Override protected void onPostResume() { super.onPostResume(); //If fragment is null, create a new instance if(detailPagerFragment==null){ detailPagerFragment = DetailContainerFragment.newInstance(details, initialPosition); } FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); ft.replace(R.id.container, detailPagerFragment); ft.commit(); } ``` Note: that i have to save the fragment instance on onCreate because when the code reaches onStart the reference for this fragment was null (this issue is not important for the time it has something to do with the NavigationDrawer), so i need to manually save the instance of the fragment. Activity layout: ``` <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <FrameLayout android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/container"/> </RelativeLayout> ``` ViewPager Fragmentm this class extends for DetailPagerFragment which is a custom pager fragment it only wraps common code for pagers (for example view inflation, uses inflateView method), this is why the pager is added on a fragment instead of directly to the activity: ``` public class DetailContainerFragment extends DetailPagerFragment { List<Detail> details; public static DetailContainerFragment newInstance(List<Detail> details,int selectedPosition) { DetailContainerFragment df = new DetailContainerFragment(); df.setSelectedPosition(selectedPosition); df.setDetails(details); return df; } public void setDetails(List<Detail> details) { this.details = details; } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setRetainInstance(true); } /** * Inflates the view to be used by this fragment * * @param inflater * Inflater to use * @return Inflated view */ @Override public View inflateView(LayoutInflater inflater) { return inflater.inflate(R.layout.detail_pager_fragment, null); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { DetailStatePagerAdapter detailStatePagerAdapter = new DetailStatePagerAdapter(getChildFragmentManager()); setPagerAdapter(detailStatePagerAdapter); return super.onCreateView(inflater, container, savedInstanceState); } private class DetailStatePagerAdapter extends FragmentStatePagerAdapter { public DetailStatePagerAdapter(FragmentManager fm) { super(fm); } @Override public Fragment getItem(int position) { return DetailFragment.newInstance(details.get(position)); } @Override public CharSequence getPageTitle(int position) { return details.get(position).getTitle(); } @Override public int getCount() { return details.size(); } } } ```

Original source