FragmentStatePagerAdapter, childFragmentManager, and direction change are not a good combination?
Here’s my problem, I have an Activity that includes a Fragment using ViewPager (FragmentStatePagerAdapter), everything works fine when the activity is first loaded, but when setRetainInstance(true) is set to the parent fragment (the one with the pager), and the activity The direction changes, which causes
java.lang.IllegalStateException: No activity
When trying to add a saved fragment, the code is as follows:
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: I have to save the fragment instance on
onCreate because when the code arrives at onStart, the reference to that fragment is empty (this issue is not important because it has to do with NavigationDrawer), so I need to save the fragment instance manually.
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 DetailPagerFragment, a custom pager fragment that only wraps the public code of the pager (e.g. View bloat, using the inflateView method), which is why the pager is added on the fragment rather than 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();
}
}
}
Solution
I’m having a similar subfragment problem in ViewPager.
The parent fragment creates a new instance of the child fragment
to use with ViewPager, while the adapter retains an instance of the old child fragment that was used before the configuration change.
I ended up cleaning up 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….