J'ai un fragment qui contient un ViewPager. ViewPager est associé à un adaptateur contenant un ensemble de fragments.
Lors du chargement du fragment parent, je rencontre une IllegalStateException
avec le message: Java.lang.IllegalStateException: Recursive entry to executePendingTransactions
.
Certaines recherches m'ont conduit à la conclusion que le système ne peut pas afficher les fragments dans un autre fragment. CEPENDANT, il semble y avoir une indication qu'il est possible de faire exactement cela avec l'utilisation d'un ViewPager ( Un bogue dans ViewPager qui l'utilise avec autre fragment ).
En fait, si j'ajoute un bouton à mon fragment parent qui appelle mPager.setAdapter(mAdapter)
sur mon ViewPager lorsqu'il est enfoncé, le ViewPager se charge correctement. Ce n'est pas idéal.
Le problème doit alors être lié au cycle de vie du fragment. Ma question est donc la suivante: Quelqu'un d’autre at-il trouvé une solution à ce problème et, dans l’affirmative, comment?
Existe-t-il un moyen de différer les paramètres de l'adaptateur sur le ViewPager après la transaction de fragment?
Voici mon code de fragment parent:
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
mView = inflater.inflate(R.layout.team_card_master, container, false);
mViewPager = (ViewPager)mView.findViewById(R.id.team_card_master_view_pager);
final Button button = (Button)mView.findViewById(R.id.load_viewpager_button);
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mViewPager.setAdapter(mAdapter);
button.setVisibility(View.GONE);
}
});
mAdapter = new ViewPagerAdapter(getFragmentManager());
// mViewPager.setAdapter(mAdapter);
return mView;
}
Et l'adaptateur:
public class ViewPagerAdapter extends FragmentPagerAdapter {
public ViewPagerAdapter(FragmentManager fm) {
super(fm);
}
@Override
public int getCount() {
if (mCursor == null) return 0;
else return mCursor.getCount();
}
@Override
public Fragment getItem(int position) {
Bundle b = createBundle(position, mCursor);
return TeamCardFragment.newInstance(b);
}
}
utilisez AsyncTask pour définir l'adaptateur pour viewPager. Ça marche pour moi. La tâche asynchrone consiste à faire en sorte que le fragment d'origine complète sa transition. et ensuite nous procédons avec des fragments de viewPager, essentiellement pour éviter la récursion.
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
mView = inflater.inflate(R.layout.team_card_master, container, false);
mViewPager = (ViewPager)mView.findViewById(R.id.team_card_master_view_pager);
final Button button = (Button)mView.findViewById(R.id.load_viewpager_button);
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mViewPager.setAdapter(mAdapter);
button.setVisibility(View.GONE);
}
});
mAdapter = new ViewPagerAdapter(getFragmentManager());
new setAdapterTask().execute();
return mView;
}
private class setAdapterTask extends AsyncTask<Void,Void,Void>{
protected Void doInBackground(Void... params) {
return null;
}
@Override
protected void onPostExecute(Void result) {
mViewPager.setAdapter(mAdapter);
}
}
Jords, je pense que vous pouvez utiliser FragmentStatePagerAdapter
au lieu de FragmentPageAdapter
. Cela enlèvera des fragments pour vous.
PS. Désolé d’utiliser réponse au lieu de commentaire mais ma réputation est trop basse.
Je viens de rencontrer ce même problème. J'avais une Fragment
qui devait héberger une ViewPager
de Fragments
. Quand j'empilais une autre Fragment
par-dessus ma ViewPagerFragment
, puis que je me rendais, j'obtenais une IllegalStateException
. Après avoir vérifié les journaux (en empilant une nouvelle Fragment
), j’ai trouvé que ma ViewPagerFragment
suivait ses méthodes de cycle de vie pour s’arrêter et se détruire, mais ses enfants Fragments
dans la ViewPager
resteraient dans onResume
. J'ai réalisé que les enfants Fragments
devraient être gérés par le FragmentManager
pour le ViewPagerFragment
, pas le FragmentManager
du Activity
.
Je crois comprendre qu'à l'époque, les réponses ci-dessus consistaient à contourner les limitations de Fragments
de ne pas pouvoir avoir d'enfants Fragments
. Cependant, maintenant que la dernière bibliothèque de support prend en charge l’emboîtement Fragment
, vous n’avez plus besoin de modifier la configuration de l’adaptateur pour votre ViewPager
et il suffit de passer getChildFragmentManager()
. Cela a fonctionné parfaitement pour moi jusqu'à présent.
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
mView = inflater.inflate(R.layout.team_card_master, container, false);
mViewPager = (ViewPager) mView.findViewById(R.id.team_card_master_view_pager);
mAdapter = new ViewPagerAdapter(getChildFragmentManager());
mViewPager.setAdapter(mAdapter);
return mView;
}
J'ai utilisé Handler.post()
pour définir l'adaptateur en dehors de la transaction de fragment d'origine:
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mHandler.post(new Runnable() {
@Override
public void run() {
mViewPager.setAdapter(mAdapter);
}
});
}
J'ai rencontré ce problème lors de l'initialisation de l'adaptateur viewPager sur TabIndicator dans ViewCreated. Après avoir lu à ce sujet en ligne, je me suis rendu compte que c'était peut-être juste parce que le fragment n'était pas encore ajouté à l'activité. J'ai donc déplacé le code d'initialisation sur onStart () du fragment, cela devrait résoudre votre problème. Mais pas pour moi, j'avais toujours le même problème.
Mais c’est parce que, dans mon code, j’essayais de contourner l’exécution asynchrone normale de la tâche en attente en appelant explicitement executePendingTransactions (), après avoir bloqué le fait que tout fonctionnait bien.
Selon la documentation: https://developer.Android.com/reference/Android/app/Fragment.html#getChildFragmentManager ()
Si vous souhaitez gérer le fragment imbriqué, vous devez utiliser getChildFragmentManager
au lieu de getFragmentManager
.
Lors du chargement du fragment parent, je rencontre une exception IllegalStateException avec le message: Java.lang.IllegalStateException: entrée récursive pour executePendingTransactions.
Certaines recherches m'ont conduit à la conclusion que le système ne peut pas afficher les fragments dans un autre fragment. CEPENDANT, il semble y avoir une indication qu'il est possible de faire exactement cela avec l'utilisation d'un ViewPager (un bogue dans ViewPager l'utilisant avec un autre fragment ).
définir explicitement Id pour ViewPager.
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
mView = inflater.inflate(R.layout.team_card_master, container, false);
mViewPager = (ViewPager)mView.findViewById(R.id.team_card_master_view_pager);
mViewPager.setId(100);
final Button button = (Button)mView.findViewById(R.id.load_viewpager_button);
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mViewPager.setAdapter(mAdapter);
button.setVisibility(View.GONE);
}
});
mAdapter = new ViewPagerAdapter(getFragmentManager());
mViewPager.setAdapter(mAdapter);
return mView;
}
Vous pouvez utiliser un identifiant dans vos ressources XML au lieu d’un nombre magique 100.
<resources>
<item name="Id_For_ViewPager" type="id"/>
</resources>
et setId (R.Id_For_ViewPager).
Voir FragmentManager.Java source pour plus de détails.
Ou voir cette page (en japonais). L'idée originale vient d'elle, en fait, pas de moi. http://y-anz-m.blogspot.jp/2012/04/androidfragment-viewpager-id.html
Au lieu de configurer l’adaptateur dans AsyncTask ou tout autre gestionnaire, écrivez simplement cette partie du code dans onResume () section
En faisant ce que vous voulez:
Le code de onResume () est le suivant dans votre cas:
@Override
public void onResume() {
super.onResume();
if(mViewPager.getAdapter()==null||mViewPager.getAdapter().getCount()==0)
mViewPager.setAdapter(mAdapter);
}
Également dans onCreateView Au lieu de
mAdapter = new ViewPagerAdapter(getFragmentManager());
Remplacer par
mAdapter = new ViewPagerAdapter(getChildFragmentManager());
En utilisant ce brillant post de Thomas Amssler: http://tamsler.blogspot.nl/2011/11/Android-viewpager-and-fragments-part-ii.html
J'ai créé un adaptateur qui peut être utilisé pour créer tous les types de fragments . L'adaptateur lui-même ressemble à ceci:
public abstract class EmbeddedAdapter extends FragmentStatePagerAdapter {
private SparseArray<WeakReference<Fragment>> mPageReferenceMap = new SparseArray<WeakReference<Fragment>>();
private ArrayList<String> mTabTitles;
public abstract Fragment initFragment(int position);
public EmbeddedAdapter(FragmentManager fm, ArrayList<String> tabTitles) {
super(fm);
mTabTitles = tabTitles;
}
@Override
public Object instantiateItem(ViewGroup viewGroup, int position) {
mPageReferenceMap.put(Integer.valueOf(position), new WeakReference<Fragment>(initFragment(position)));
return super.instantiateItem(viewGroup, position);
}
@Override
public int getCount() {
return mTabTitles.size();
}
@Override
public CharSequence getPageTitle(int position) {
return mTabTitles.get(position);
}
@Override
public Fragment getItem(int pos) {
return getFragment(pos);
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
super.destroyItem(container, position, object);
mPageReferenceMap.remove(Integer.valueOf(position));
}
public Fragment getFragment(int key) {
WeakReference<Fragment> weakReference = mPageReferenceMap.get(key);
if (null != weakReference) {
return (Fragment) weakReference.get();
} else {
return null;
}
}
@Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
}
Pour utiliser l'adaptateur, vous devrez procéder comme suit:
private void setAdapter() {
if(mAdapter == null) mAdapter = new EmbeddedAdapter(getActivity().getSupportFragmentManager(), mTabTitles) {
@Override
public Fragment initFragment(int position) {
Fragment fragment;
if(position == 0){
// A start fragment perhaps?
fragment = StartFragment.newInstance(mBlocks);
}else{
// Some other fragment
fragment = PageFragment.newInstance(mCategories.get((position)));
}
return fragment;
}
};
// Have to set the adapter in a handler
Handler handler = new Handler();
handler.post(new Runnable() {
@Override
public void run() {
mViewPager.setVisibility(View.VISIBLE);
mPagerTabStrip.setVisibility(View.VISIBLE);
mAdapter.notifyDataSetChanged();
}
});
}
Notez que tous les fragments utilisent le:
setRetainInstance(true);