web-dev-qa-db-fra.com

Récupérer un fragment d'un ViewPager

J'utilise une ViewPager avec une FragmentStatePagerAdapter pour héberger trois fragments différents:

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

Quand je veux obtenir Fragment1 de la ViewPager dans la FragmentActivity

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

227
Qun Qin

La réponse principale repose sur un nom généré par le framework. Si cela change jamais, alors cela ne fonctionnera plus.

Qu'en est-il de cette solution, annulant les fonctions 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 lorsqu'il s'agit de fragments disponibles. Les fragments qui n'ont pas encore été instanciés renverront null lors de l'appel de getRegisteredFragment. Mais je l’utilise surtout pour obtenir la Fragment actuelle de la ViewPager: adapater.getRegisteredFragment(viewPager.getCurrentItem()) et cela ne renverra pas null.

Je ne suis au courant d'aucun autre inconvénient de cette solution. S'il y en a, j'aimerais savoir.

492

Pour récupérer des fragments d’un ViewPager, il existe de nombreuses réponses ici et sur d’autres SO discussions/blogs connexes. Toutes les personnes que j'ai vues sont brisées et semblent généralement appartenir à l'un des deux types énumérés ci-dessous. Il existe d’autres solutions valables si vous voulez seulement récupérer le fragment actuel, comme this autre réponse sur ce fil de discussion.

Si vous utilisez FragmentPagerAdapter, voir ci-dessous. Si vous utilisez FragmentStatePagerAdapter, cela vaut la peine de regarder this . Saisir des index qui ne sont pas les index actuels dans FragmentStateAdapter n'est pas aussi utile que si elles étaient complètement détruites, elles étaient hors de vue/hors des limites offScreenLimit.} _

LES PISTES INHABPES

Wrong: conserve votre propre liste interne de fragments, ajoutée à l'appel de FragmentPagerAdapter.getItem()

  • Utilisant habituellement une SparseArray ou Map
  • Aucun des nombreux exemples que j'ai vus ne rend compte d'événements de cycle de vie, cette solution est donc fragile. Comme getItem n'est appelé que la première fois qu'une page est défilée (ou obtenue si votre ViewPager.setOffscreenPageLimit(x)> 0) dans la ViewPager, si la ActivityFragment H&OCIRC;TE EST SUPPRIM&EACUTE;E OU RED&EACUTE;MARR&EACUTE;E, LA SpaseArray INTERNE SERA EFFAC&EACUTE;E LORS DE LA RECR&EACUTE;ATION DE LA PROPRI&EACUTE;T&EACUTE; FRAGMENTPAGERACTIVITY , MAIS EN COULISSE, LES FRAGMENTS INTERNES DE VIEWPAGERS SERONT RECR&EACUTE;&EACUTE;S, ET getItem SERA APPEL&EACUTE; _/_ ET NON pour aucun des index, de sorte que la possibilité d’obtenir un fragment à partir d’index sera définitivement perdue. Vous pouvez en tenir compte en sauvegardant et en restaurant ces références de fragments via FragmentManager.getFragment() et putFragment, mais cela commence à devenir désordonné à mon humble avis.

Wrong: Construisez votre propre id de tag correspondant à ce qui est utilisé sous le capot dans FragmentPagerAdapter et utilisez-le pour récupérer les fragments de page à partir de la FragmentManager

  • C’est mieux dans la mesure où cela résout le problème des références de fragments perdants dans la première solution de type array interne, mais, comme cela a été souligné à juste titre dans les réponses ci-dessus et ailleurs sur le net - on a le sentiment qu’il s’agit d’une méthode private. internal to ViewPager qui peut changer à tout moment ou pour n’importe quelle version du système d'exploitation.

La méthode recréée pour cette solution est 

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

A HAPPY PATH: ViewPager.instantiateItem()

Une approche similaire à getItem() ci-dessus mais sans rupture de cycle de vie consiste à y accrocher instantiateItem() au lieu de getItem() car le précédent sera appelé à chaque fois que cet index est créé/utilisé. Voir cette réponse

A HAPPY PATH: Construisez votre propre FragmentViewPager

Construisez votre propre classe FragmentViewPager à partir de la source de la dernière version du support lib et modifiez la méthode utilisée en interne pour générer les balises fragment. Vous pouvez le remplacer par le suivant. Cela a pour avantage que vous savez que la création de balises ne changera jamais et que vous ne vous fiez pas à une méthode privée api /, ce 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;
}

Ensuite, comme le dit la doc, lorsque vous souhaitez récupérer un fragment utilisé pour un index, appelez simplement cette méthode (que vous pouvez mettre dans la variable personnalisée FragmentPagerAdapter ou une sous-classe), sachant que le résultat peut être null si getItem a pas encore été appelé pour cette page c'est-à-dire qu'il n'a 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 qui résout les problèmes des deux autres solutions trouvées partout sur le Web.

83
Dori

Ajoutez les méthodes suivantes à 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) doit fonctionner. 

Voici la solution implémentée dans ViewPager https://Gist.github.com/jacek-marchwicki/d6320ba9a910c514424d . Si quelque chose échoue, vous verrez un bon journal des incidents. 

70
kzotin

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);
        }
    }
38
Mindphaser

Je sais que cela a quelques réponses, mais peut-être que cela aidera quelqu'un. J'ai utilisé une solution relativement simple lorsque j'avais besoin d'obtenir un Fragment de mon ViewPager. Dans votre Activity ou Fragment tenant le ViewPager, vous pouvez utiliser ce code pour parcourir 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 ViewPager, vous pouvez simplement appeler getItem(knownPosition).

  • Si vous ne connaissez pas la position de votre Fragment dans ViewPager, vous pouvez demander à vos enfants Fragments d'implémenter une interface avec une méthode telle que getUniqueId() et de l'utiliser pour les différencier. Vous pouvez également parcourir toutes les variables Fragments et vérifier le type de classe, tel que if(viewPagerFragment instanceof FragmentClassYouWant).

!!! MODIFIER !!!

J'ai découvert que getItem n'est appelé par un FragmentPagerAdapter que lorsque chaque Fragment doit être créé la première fois , après cela, il semble que les Fragments sont recyclés à l'aide de FragmentManager. De cette façon, de nombreuses implémentations de FragmentPagerAdapter créent new Fragments dans getItem. En utilisant ma méthode ci-dessus, cela signifie que nous allons créer un nouveau Fragments chaque fois que getItem est appelé alors que nous parcourons tous les éléments de FragmentPagerAdapter. Pour cette raison, j'ai trouvé une meilleure approche, en utilisant FragmentManager pour obtenir chaque Fragment à la place (en utilisant la réponse acceptée). C'est une solution plus complète et qui 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;
}
21
Steven Byle

Pour obtenir le fragment visible actuel de ViewPager . J'utilise cette déclaration simple et ça fonctionne bien.

      public Fragment getFragmentFromViewpager()  
      {
         return ((Fragment) (mAdapter.instantiateItem(mViewPager, mViewPager.getCurrentItem())));
      }
6
Xar E Ahmer

Ceci est basé sur la réponse de Steven ci-dessus. Cela retournera l'instance réelle du fragment qui est déjà attaché à l'activité parent. 

FragmentPagerAdapter fragmentPagerAdapter = (FragmentPagerAdapter) mViewPager.getAdapter();
    for(int i = 0; i < fragmentPagerAdapter.getCount(); i++) {

        Fragment viewPagerFragment = (Fragment) mViewPager.getAdapter().instantiateItem(mViewPager, i);
        if(viewPagerFragment != null && viewPagerFragment.isAdded()) {

            if (viewPagerFragment instanceof FragmentOne){
                FragmentOne oneFragment = (FragmentOne) viewPagerFragment;
                if (oneFragment != null){
                    oneFragment.update(); // your custom method
                }
            } else if (viewPagerFragment instanceof FragmentTwo){
                FragmentTwo twoFragment = (FragmentTwo) viewPagerFragment;

                if (twoFragment != null){
                    twoFragment.update(); // your custom method
                }
            }
        }
    }
3
OrangeTree

Hey j'ai répondu à cette question ici . Fondamentalement, vous devez remplacer 

public Object instantiateItem (conteneur ViewGroup, int position)

méthode de FragmentStatePagerAdapter.

2
Utkarsh Saxena

Vous n'avez pas besoin d'appeler getItem() ou une autre méthode à un stade ultérieur pour obtenir la référence d'une Fragment hébergée dans ViewPager. Si vous souhaitez mettre à jour des données dans Fragment, utilisez cette approche: Mettre à jour ViewPager de manière dynamique?

La clé consiste à définir de nouvelles données dans Adaper et à appeler notifyDataSetChanged(), qui à son tour appelle getItemPosition(), vous transmettant une référence de votre Fragment et vous permettant de la mettre à jour. Tous les autres moyens exigent que vous gardiez une référence à vous-même ou à un autre hack qui n’est pas une bonne solution.

@Override
public int getItemPosition(Object object) {
    if (object instanceof UpdateableFragment) {
        ((UpdateableFragment) object).update(xyzData);
    }
    //don't return POSITION_NONE, avoid fragment recreation. 
    return super.getItemPosition(object);
}
2
M-WaJeEh

Je l'ai manipulé en faisant d'abord une liste de tous les fragments (List<Fragment> fragments;) que j'allais utiliser, puis je les ai ajoutés au pageur, ce qui facilite la gestion du fragment actuellement visualisé.

Alors:

@Override
onCreate(){
    //initialise the list of fragments
    fragments = new Vector<Fragment>();

    //fill up the list with out fragments
    fragments.add(Fragment.instantiate(this, MainFragment.class.getName()));
    fragments.add(Fragment.instantiate(this, MenuFragment.class.getName()));
    fragments.add(Fragment.instantiate(this, StoresFragment.class.getName()));
    fragments.add(Fragment.instantiate(this, AboutFragment.class.getName()));
    fragments.add(Fragment.instantiate(this, ContactFragment.class.getName()));


    //Set up the pager
    pager = (ViewPager)findViewById(R.id.pager);
    pager.setAdapter(new MyFragmentPagerAdapter(getSupportFragmentManager(), fragments));
    pager.setOffscreenPageLimit(4);
}

alors cela peut être appelé:

public Fragment getFragment(ViewPager pager){   
    Fragment theFragment = fragments.get(pager.getCurrentItem());
    return theFragment;
}

alors je pourrais jeter dans une déclaration si qui ne fonctionnerait que si elle était sur le bon fragment

Fragment tempFragment = getFragment();
if(tempFragment == MyFragmentNo2.class){
    MyFragmentNo2 theFrag = (MyFragmentNo2) tempFragment;
    //then you can do whatever with the fragment
    theFrag.costomFunction();
}

mais c’est juste mon approche de hack et slash mais cela a fonctionné pour moi, je l’utilise pour effectuer des changements pertinents sur le fragment actuellement affiché lorsque le bouton de retour est enfoncé.

2
Russell Cargill

Je ne pouvais pas trouver un moyen simple et propre de faire cela. Cependant, le widget ViewPager n'est qu'un autre ViewGroup, qui héberge vos fragments. ViewPager a ces fragments comme enfants immédiats. Vous pouvez donc simplement les parcourir (en utilisant .getChildCount () et .getChildAt ()) et voir si l’instance de fragment que vous recherchez est actuellement chargée dans le ViewPager et y obtenir une référence. Par exemple. vous pouvez utiliser un champ d'identifiant unique statique pour distinguer les fragments.

Notez que le ViewPager n'a peut-être pas chargé le fragment recherché, car il s'agit d'un conteneur de virtualisation tel que ListView.

2

Doit étendre FragmentPagerAdapter à votre classe d’adaptateur ViewPager.
Si vous utilisez FragmentStatePagerAdapter, vous ne pourrez pas trouver votre Fragment par son ID

public static String makeFragmentName(int viewPagerId, int index) {
  return "Android:switcher:" + viewPagerId + ":" + index;
}

Comment utiliser cette méthode: -

Fragment mFragment = ((FragmentActivity) getContext()).getSupportFragmentManager().findFragmentByTag(
       AppMethodUtils.makeFragmentName(mViewPager.getId(), i)
);
InterestViewFragment newFragment = (InterestViewFragment) mFragment;
2
i_m_mahii

FragmentPagerAdapter est la fabrique des fragments. Pour trouver un fragment basé sur sa position s'il est encore en mémoire, utilisez ceci:

public Fragment findFragmentByPosition(int position) {
    FragmentPagerAdapter fragmentPagerAdapter = getFragmentPagerAdapter();
    return getSupportFragmentManager().findFragmentByTag(
            "Android:switcher:" + getViewPager().getId() + ":"
                    + fragmentPagerAdapter.getItemId(position));
}

Exemple de code pour l'API de support v4.

2
Daniel De León

La meilleure solution consiste à utiliser l’extension créée à l’emplacement CodePath appelée SmartFragmentStatePagerAdapter . Suivre ce guide facilite grandement la récupération des fragments et du fragment actuellement sélectionné à partir d'un ViewPager. Cela permet également de mieux gérer la mémoire des fragments incorporés dans l'adaptateur.

1
Nathan

J'ai implémenté cela facilement avec une approche un peu différente.

Ma méthode personnalisée FragmentAdapter.getItem n'a pas renvoyé MyFragment (), mais l'instance de MyFragment créée dans le constructeur FragmentAdapter. 

Dans mon activité, j'ai ensuite obtenu le fragment de l'adaptateur, vérifie s'il est instanceOf required Fragment, puis transforme et utilise les méthodes nécessaires.

0
lxknvlk

dans TabLayout, il y a plusieurs onglets pour Fragment. vous pouvez trouver le fragment par Tag en utilisant l'index du fragment.

Par ex. L'index pour Fragment1 est 0. Par conséquent, dans la méthode findFragmentByTag(), transmettez la balise pour le Viewpager. Après avoir utilisé fragmentTransaction, vous pouvez ajouter et remplacer le fragment.

String tag = "Android:switcher:" + R.id.viewPager + ":" + 0; Fragment1 f = (Fragment1) getSupportFragmentManager().findFragmentByTag(tag);

0
SUMIT MONAPARA

Le moyen le plus simple et le plus concis. Si tous vos fragments dans ViewPager appartiennent à des classes différentes, vous pouvez les récupérer et les distinguer comme suit:

public class MyActivity extends Activity
{

    @Override
    public void onAttachFragment(Fragment fragment) {
        super.onAttachFragment(fragment);
        if (fragment.getClass() == MyFragment.class) {
            mMyFragment = (MyFragment) fragment;
        }
    }

}
0
riwnodennyk

Dans le fragment

public int getArgument(){
   return mPage;
{
public void update(){

}

En FragmentActivité

List<Fragment> fragments = getSupportFragmentManager().getFragments();
for(Fragment f:fragments){
    if((f instanceof PageFragment)&&(!f.isDetached())){
          PageFragment pf = (PageFragment)f;   
          if(pf.getArgument()==pager.getCurrentItem())pf.update();
    }
}
0
Mark Zhuravlyov

Créer un identifiant de ressource entier dans /values/integers.xml

<integer name="page1">1</integer>
<integer name="page2">2</integer>
<integer name="page3">3</integer>

Ensuite, dans la fonction getItem de PagerAdapter:

public Fragment getItem(int position) {

        Fragment fragment = null;

        if (position == 0) {
            fragment = FragmentOne.newInstance();
            mViewPager.setTag(R.integer.page1,fragment);

        }
        else if (position == 1) {

            fragment = FragmentTwo.newInstance();
            mViewPager.setTag(R.integer.page2,fragment);

        } else if (position == 2) {

            fragment = FragmentThree.newInstance();
            mViewPager.setTag(R.integer.page3,fragment);

        }

        return fragment;
        }

Ensuite, en activité, écrivez cette fonction pour obtenir la référence de fragment:

private Fragment getFragmentByPosition(int position) {
    Fragment fragment = null;

    switch (position) {
        case 0:
            fragment = (Fragment) mViewPager.getTag(R.integer.page1);
            break;

        case 1:

            fragment = (Fragment) mViewPager.getTag(R.integer.page2);
            break;

        case 2:
            fragment = (Fragment) mViewPager.getTag(R.integer.page3);
            break;
            }

            return fragment;
    }

Obtenez la référence au fragment en appelant la fonction ci-dessus, puis convertissez-la dans votre fragment personnalisé:

Fragment fragment = getFragmentByPosition(position);

        if (fragment != null) {
                    FragmentOne fragmentOne = (FragmentOne) fragment;
                    }
0
user2574090

Un moyen facile d'itérer des fragments dans le gestionnaire de fragments. Trouver le viewpager, qui a un argument de position de section, placé dans public statique PlaceholderFragment newInstance (int sectionNumber) .

public PlaceholderFragment getFragmentByPosition(Integer pos){
    for(Fragment f:getChildFragmentManager().getFragments()){
        if(f.getId()==R.id.viewpager && f.getArguments().getInt("SECTNUM") - 1 == pos) {
            return (PlaceholderFragment) f;
        }
    }
    return null;
}
0
Evgeny