web-dev-qa-db-fra.com

Affichage d'un fragment de vue dans un fragment

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);
    }
}
54
howettl

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);
        }
}
60
Yashwanth Kumar

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.

37
Leszek

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;
}
24
Steven Byle

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);     
        }
    });        
}
19
inazaruk

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.

0
Sanket

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.

0
Salvatore Cozzubo

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.

https://github.com/Android/platform_frameworks_support/blob/master/v4/Java/Android/support/v4/app/FragmentManager.Java#L900

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

0
Kugyo

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:

  • Laissez la transition du fragment principal à compléter.
  • Affichez le fragment enfant sans utiliser de tâche en arrière-plan.

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());
0
Karan

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);
0
Slickelito