252 votes

Récupérer un Fragment d'un ViewPager

Je suis à l'aide d'un ViewPager avec un FragmentStatePagerAdapter d'hôte trois différents fragments:

  • [Fragment1]
  • [Fragment2]
  • [Fragment3]

Quand je veux m' Fragment1 de la ViewPager dans la FragmentActivity.

Quel est le problème et comment puis-je résoudre ce problème?

506voto

Streets Of Boston Points 3978

La principale réponse s'appuie sur un nom généré par le cadre. Si cela ne change jamais, alors il ne fonctionnera plus.

Ce à propos de cette solution, en substituant instantiateItem() et destroyItem() de votre Fragment(State)PagerAdapter:

public class MyPagerAdapter extends FragmentStatePagerAdapter {
    SparseArray<Fragment> registeredFragments = new SparseArray<Fragment>();

    public MyPagerAdapter(FragmentManager fm) {
        super(fm);
    }

    @Override
    public int getCount() {
        return ...;
    }

    @Override
    public Fragment getItem(int position) {
        return MyFragment.newInstance(...); 
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        Fragment fragment = (Fragment) super.instantiateItem(container, position);
        registeredFragments.put(position, fragment);
        return fragment;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        registeredFragments.remove(position);
        super.destroyItem(container, position, object);
    }

    public Fragment getRegisteredFragment(int position) {
        return registeredFragments.get(position);
    }
}

Cela semble fonctionner pour moi lorsque vous traitez avec des Fragments qui sont disponibles. Des Fragments qui n'ont pas encore été instancié, sera de retour null lors de l'appel d' getRegisteredFragment. Mais j'ai été en utilisant ce la plupart du temps pour obtenir le courant Fragment de la ViewPager: adapater.getRegisteredFragment(viewPager.getCurrentItem()) et ce ne sera pas de retour null.

Je ne suis pas au courant de tous les autres inconvénients de cette solution. Si il y en a, j'aimerais savoir.

85voto

Dori Points 4011

Pour extraire des fragments d'un ViewPager il y a beaucoup de réponses ici et sur d'autres, de SORTE threads / blogs. Tous ceux que j'ai vu est cassé, et ils ont généralement l'impression de tomber dans l'un des deux types énumérés ci-dessous. Il y a d'autres solutions valables si vous ne voulez attraper l'actuel fragment, comme cette autre réponse sur ce fil.

Si vous utilisez FragmentPagerAdapter voir ci-dessous. Si vous utilisez FragmentStatePagerAdapter sa peine de regarder cette. L'accaparement des indices qui ne sont pas actuellement dans une FragmentStateAdapter n'est pas aussi utile que par la nature de ces va être complètement démoli est allé hors de la vue / de offScreenLimit limites.

LES MAUVAISES FAÇONS

Faux: gérer votre propre liste interne des fragments, ajouté lors de l' FragmentPagerAdapter.getItem() est appelé

  • Généralement à l'aide d'un SparseArray ou Map
  • Pas l'un des nombreux exemples que j'ai vu des comptes pour les événements de cycle de vie, donc cette solution est fragile. En tant que getItem n'est appelée la première fois qu'une page est affichée (ou obtenu si votre ViewPager.setOffscreenPageLimit(x) > 0) dans l' ViewPager, si l'hébergement Activity / Fragment est tué ou redémarré puis l'interne SpaseArray seront effacées lorsque la coutume FragmentPagerActivity est recréé, mais derrière les coulisses, le ViewPagers interne fragments seront recréés, et getItem va PAS être appelé pour toute de l'index, de sorte que la possibilité d'obtenir un fragment de l'indice seront perdus à jamais. Vous pouvez tenir compte de cette par la sauvegarde et la restauration de ces références de fragments via FragmentManager.getFragment() et putFragment , mais cela commence à être très salissant à mon humble avis.
  • EDIT: un semblable, mais non du cycle de vie de la rupture approche consiste à crochet en ViewPager.instantiateItem() au lieu de getItem comme le premier sera appelé à chaque fois que l'index est créé / consulté. Voir cette réponse

Faux: Construire votre propre id de balise correspond à ce est utilisé sous le capot en FragmentPagerAdapter et de l'utiliser pour récupérer les Fragments de page de l' FragmentManager

  • C'est mieux de sorte qu'il s'adapte avec la perdre-fragment-références problème interne, solution de matrice, mais comme l'a justement souligné dans les réponses ci-dessus et ailleurs sur le net, il se sent hacky comme un privé méthode interne d' ViewPager qui pourrait changer à tout moment ou pour toute version de l'OS.

La méthode c'est recréé pour cette solution

private static String makeFragmentName(int viewId, long id) {
    return "android:switcher:" + viewId + ":" + id;
}

LA bonne FAÇON: Construire votre propre FragmentViewPager

Construire votre propre FragmentViewPager classe à partir de la source de la dernière support lib et de modifier la méthode utilisée en interne pour générer le fragment de balises. Vous pouvez le remplacer avec le ci-dessous. Ceci a l'avantage que vous connaissez le tag de la création ne changera jamais et de ne pas répondre à une api privée / méthode, qui est toujours dangereux.

/**
 * @param containerViewId the ViewPager this adapter is being supplied to
 * @param id pass in getItemId(position) as this is whats used internally in this class
 * @return the tag used for this pages fragment
 */
public static String makeFragmentName(int containerViewId, long id) {
    return "android:switcher:" + containerViewId + ":" + id;
}

Alors que le doc a dit, quand vous voulez prendre un fragment utilisé pour un indice de simplement appeler quelque chose comme cette méthode (que vous pouvez mettre dans la coutume FragmentPagerAdapter ou une sous-classe) d'être au courant le résultat peut être null si getItem n'a pas encore été appelé pour cette page, c'est à dire son pas encore été créé.

/**
 * @return may return null if the fragment has not been instantiated yet for that position - this depends on if the fragment has been viewed
 * yet OR is a sibling covered by {@link android.support.v4.view.ViewPager#setOffscreenPageLimit(int)}. Can use this to call methods on
 * the current positions fragment.
 */
public @Nullable Fragment getFragmentForPosition(int position)
{
    String tag = makeFragmentName(mViewPager.getId(), getItemId(position));
    Fragment fragment = getSupportFragmentManager().findFragmentByTag(tag);
    return fragment;
}

C'est une solution simple et résout les problèmes dans les deux autres solutions trouvées un peu partout sur le web

70voto

paiNie Points 2716

Ajouter à côté de méthodes pour votre FragmentPagerAdapter:

public Fragment getActiveFragment(ViewPager container, int position) {
String name = makeFragmentName(container.getId(), position);
return  mFragmentManager.findFragmentByTag(name);
}

private static String makeFragmentName(int viewId, int index) {
    return "android:switcher:" + viewId + ":" + index;
}

getActiveFragment(0) a pour travailler.

Voici la solution mise en œuvre dans ViewPager https://gist.github.com/jacek-marchwicki/d6320ba9a910c514424d. Si quelque chose échoue, vous verrez bien de crash.

38voto

Mindphaser Points 161

Une autre solution simple:

    public class MyPagerAdapter extends FragmentPagerAdapter {
        private Fragment mCurrentFragment;

        public Fragment getCurrentFragment() {
            return mCurrentFragment;
        }
//...    
        @Override
        public void setPrimaryItem(ViewGroup container, int position, Object object) {
            if (getCurrentFragment() != object) {
                mCurrentFragment = ((Fragment) object);
            }
            super.setPrimaryItem(container, position, object);
        }
    }

21voto

Steven Byle Points 4888

Je sais que cela a un peu de réponses, mais peut-être que cela aidera quelqu'un. J'ai utilisé une solution relativement simple quand j'en avais besoin pour obtenir un Fragment de mon ViewPager. Dans votre Activity ou Fragment tenue de l' ViewPager, vous pouvez utiliser ce code pour faire défiler chaque Fragment qu'il détient.

FragmentPagerAdapter fragmentPagerAdapter = (FragmentPagerAdapter) mViewPager.getAdapter();
for(int i = 0; i < fragmentPagerAdapter.getCount(); i++) {
    Fragment viewPagerFragment = fragmentPagerAdapter.getItem(i);
    if(viewPagerFragment != null) {
        // Do something with your Fragment
        // Check viewPagerFragment.isResumed() if you intend on interacting with any views.
    }
}
  • Si vous connaissez la position de votre Fragment dans la ViewPager, vous pouvez les appeler getItem(knownPosition).

  • Si vous ne connaissez pas la position de votre Fragment dans la ViewPager, vous pouvez avoir vos enfants Fragments implémenter une interface avec une méthode comme getUniqueId(), et l'utiliser pour les différencier. Ou vous pouvez faire défiler tous, Fragments et de vérifier le type de classe, comme if(viewPagerFragment instanceof FragmentClassYouWant)

!!! EDIT !!!

J'ai découvert que l' getItem seulement est appelée par un FragmentPagerAdapter lors de chaque Fragment doit être créé la première fois, après cela, il semble que la de la Fragments sont recyclés à l'aide de l' FragmentManager. De cette façon, de nombreuses implémentations de FragmentPagerAdapter créer de nouveaux Fragments dans getItem. En utilisant ma méthode ci-dessus, cela signifie que nous allons créer de nouveaux Fragments à chaque fois getItem est appelé à mesure que nous allons par le biais de tous les éléments de la FragmentPagerAdapter. Pour cette raison, j'ai trouvé une meilleure approche, à l'aide de l' FragmentManager pour obtenir chaque Fragment à la place (à l'aide de la accepté de répondre). C'est une solution plus complète, et a été fonctionne bien pour moi.

FragmentPagerAdapter fragmentPagerAdapter = (FragmentPagerAdapter) mViewPager.getAdapter();
for(int i = 0; i < fragmentPagerAdapter.getCount(); i++) {
    String name = makeFragmentName(mViewPager.getId(), i);
    Fragment viewPagerFragment = getChildFragmentManager().findFragmentByTag(name);
    // OR Fragment viewPagerFragment = getFragmentManager().findFragmentByTag(name);
    if(viewPagerFragment != null) {

        // Do something with your Fragment
        if (viewPagerFragment.isResumed()) {
            // Interact with any views/data that must be alive
        }
        else {
            // Flag something for update later, when this viewPagerFragment
            // returns to onResume
        }
    }
}

Et vous aurez besoin de cette méthode.

private static String makeFragmentName(int viewId, int position) {
    return "android:switcher:" + viewId + ":" + position;
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X