Fragment dans ViewPager non restauré après popBackStack

Problème

Un fragment n’est pas rattaché à son hébergeur ViewPager après son retour d’un autre fragment.

Situation

Une activité hébergeant un fragment dont la disposition contient un ViewPager ( PageListFragment dans l’exemple ci-dessous). Le ViewPager est rempli par un FragmentStateViewPagerAdapter. Les fragments uniques hébergés dans le pageur ( PageFragment dans l’exemple ci-dessous) peuvent ouvrir des listes de sous-pages contenant un nouvel ensemble de pages.

Comportement

Tout fonctionne bien tant que le bouton retour n’est pas enfoncé. Dès que l’utilisateur ferme l’une des sous-listes, la liste précédente est recréée, mais sans la page affichée précédemment. Passer en revue les autres pages de la PageList parente fonctionne toujours.

Code

Un exemple d’application peut être trouvé sur github :

Activité

 public class MainActivity extends FragmentActivity { private static final Ssortingng CURRENT_FRAGMENT = MainActivity.class.getCanonicalName() + ".CURRENT_FRAGMENT"; public static final Ssortingng ARG_PARENTS = "Parents"; public void goInto(Ssortingng mHostingLevel, Ssortingng mPosition) { Fragment hostingFragment = newHostingFragment(mHostingLevel, mPosition); addFragment(hostingFragment); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); addBaseFragment(); } private void addBaseFragment() { Fragment hostingFragment = newHostingFragment("", ""); addFragment(hostingFragment); } private Fragment newHostingFragment(Ssortingng mHostingLevel, Ssortingng oldPosition) { Fragment hostingFragment = new PageListFragment(); Bundle args = new Bundle(); args.putSsortingng(ARG_PARENTS, mHostingLevel + oldPosition +" > "); hostingFragment.setArguments(args); return hostingFragment; } private void addFragment(Fragment hostingFragment) { FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); transaction.replace(R.id.fragmentSpace, hostingFragment, CURRENT_FRAGMENT); transaction.addToBackStack(null); transaction.commit(); } } 

PageListFragment

 public class PageListFragment extends Fragment { private Ssortingng mParentSsortingng; public PageListFragment() { // Required empty public constructor } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Inflate the layout for this fragment return inflater.inflate(R.layout.fragment_hosting, container, false); } @Override public void onResume() { mParentSsortingng = getArguments().getSsortingng(MainActivity.ARG_PARENTS); ViewPager viewPager = (ViewPager) getView().findViewById(R.id.viewPager); viewPager.setAdapter(new SimpleFragmentStatePagerAdapter(getFragmentManager(),mParentSsortingng)); super.onResume(); } private static class SimpleFragmentStatePagerAdapter extends FragmentStatePagerAdapter { private Ssortingng mHostingLevel; public SimpleFragmentStatePagerAdapter(FragmentManager fm, Ssortingng hostingLevel) { super(fm); this.mHostingLevel = hostingLevel; } @Override public android.support.v4.app.Fragment getItem(int position) { PageFragment pageFragment = new PageFragment(); Bundle args = new Bundle(); args.putSsortingng(MainActivity.ARG_PARENTS, mHostingLevel); args.putInt(PageFragment.ARG_POSITION, position); pageFragment.setArguments(args); return pageFragment; } @Override public int getCount() { return 5; } } } 

PageFragment

 public class PageFragment extends Fragment { public static final Ssortingng ARG_POSITION = "Position"; private Ssortingng mHostingLevel; private int mPosition; public PageFragment() { // Required empty public constructor } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View contentView = inflater.inflate(R.layout.fragment_page, container, false); setupTextView(contentView); setupButton(contentView); return contentView; } private void setupTextView(View contentView) { mPosition = getArguments().getInt(ARG_POSITION); mHostingLevel = getArguments().getSsortingng(MainActivity.ARG_PARENTS); TextView text = (TextView) contentView.findViewById(R.id.textView); text.setText("Parent Fragments " + mHostingLevel + " \n\nCurrent Fragment "+ mPosition); } private void setupButton(View contentView) { Button button = (Button) contentView.findViewById(R.id.button); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { openNewLevel(); } }); } protected void openNewLevel() { MainActivity activity = (MainActivity) getActivity(); activity.goInto(mHostingLevel, Integer.toSsortingng(mPosition)); } } 

Après une longue enquête, le gestionnaire de fragment pose problème.

Lorsque vous utilisez une construction comme celle située au-dessus de la transaction de fragment pour rattacher le fragment à la liste de pages, celle-ci est supprimée silencieusement. C’est fondamentalement le même problème qui provoque une

 java.lang.IllegalStateException: Recursive entry to executePendingTransactions 

en essayant de modifier les fragments à l’intérieur du FragmentPager.

La même solution que pour les problèmes liés à cette erreur est également applicable ici. Lors de la construction de FragmentStatePagerAdapter, indiquez le gestionnaire de fragment enfant correct.

Au lieu de

  viewPager.setAdapter(new SimpleFragmentStatePagerAdapter(getFragmentManager(),mParentSsortingng)); 

faire

  viewPager.setAdapter(new SimpleFragmentStatePagerAdapter(getChildFragmentManager(),mParentSsortingng)); 

Voir aussi: github

Ce que Paul a omis de mentionner, c’est que si vous utilisez getChildFragmentManager , vous subirez alors le problème de «l’écran vide sur lequel vous avez appuyé».