71 votes

réutilisation des fragments dans un adaptateur de fragmentation

J'ai un viewpager qui pagine à travers des fragments. Mon site FragmentPagerAdapter crée un nouveau fragment dans le getItem ce qui semble être un gaspillage. Existe-t-il une FragmentPagerAdapter équivalent à l convertView dans le listAdapter qui me permettra de réutiliser des fragments déjà créés ? Mon code est le suivant.

public class ProfilePagerAdapter extends FragmentPagerAdapter {

    ArrayList<Profile> mProfiles = new ArrayList<Profile>();

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

    /**
     * Adding a new profile object created a new page in the pager this adapter is set to.
     * @param profile
     */
    public void addProfile(Profile profile){
        mProfiles.add(profile);
    }

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

    @Override
    public Fragment getItem(int position) {
        return new ProfileFragment(mProfiles.get(position));
    }
}

102voto

Geoff Points 1332

El FragmentPagerAdapter met déjà en cache le Fragments pour vous. Une balise est attribuée à chaque fragment, puis la balise FragmentPagerAdapter essaie d'appeler findFragmentByTag . Il appelle seulement getItem si le résultat de findFragmentByTag est null . Vous ne devriez donc pas avoir à mettre les fragments en cache vous-même.

4 votes

Bat, je pense que ce n'est pas une solution. J'ai un problème similaire. J'ai 10 écrans dans ViewPager qui contiennent tous le même fragment mais avec des arguments différents. Je n'ai pas besoin de 10 instances. 3 suffisent. Lorsque l'utilisateur fait défiler vers la droite, la plupart des fragments de gauche peuvent être réutilisés.

0 votes

@ATom - lorsque vous faites défiler, les fragments situés au-delà de +/- 1 index sont détruits et recréés lorsque vous y accédez de toute façon. Essayez de mettre une sortie de journal dans le fichier onCreateView pour voir quand chaque fragment est instancié.

2 votes

@MattTaylor Si vous utilisez un ViewPager, vous pouvez utiliser setOffscreenPageLimit pour conserver les mêmes instances.

67voto

petrnohejl Points 2483

Appendice pour le poste de Geoff :

Vous pouvez obtenir une référence à votre Fragment sur FragmentPagerAdapter en utilisant findFragmentByTag() . Le nom de la balise est généré de cette façon :

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

où viewId est l'identifiant du ViewPager

Regardez ce lien : http://code.google.com/p/openintents/source/browse/trunk/compatibility/AndroidSupportV2/src/Android/support/v2/app/FragmentPagerAdapter.java#104

19 votes

Note pour les gens comme moi : viewId est l'identifiant du ViewPager.

0 votes

@Kopfgeldjaeger J'ai vu votre code. getFragmentByPosition . Quelle est la position du fragment ?

0 votes

Seconding @GregEnnis ne fonctionne pas avec le pager d'État, des idées ?

24voto

Turbo Points 2177

Il semble qu'un grand nombre de personnes qui consultent cette question cherchent un moyen de faire référence à l'interface de l'entreprise. Fragments créé par FragmentPagerAdapter / FragmentStatePagerAdapter . Je voudrais proposer ma solution à ce problème sans m'appuyer sur la en interne créé tags que les autres réponses ici utilisent.

En prime, cette méthode devrait également fonctionner avec FragmentStatePagerAdapter . Voir les notes ci-dessous pour plus de détails.


Problème des solutions actuelles : dépendance à l'égard du code interne

Un grand nombre des solutions que j'ai vues pour cette question et d'autres questions similaires reposent sur l'obtention d'une référence à la base de données existante de l'entreprise. Fragment en appelant FragmentManager.findFragmentByTag() et en imitant le balise créée en interne : "android:switcher:" + viewId + ":" + id . Le problème, c'est que vous vous appuyez sur le code source interne, qui, comme nous le savons tous, n'est pas garanti de rester le même pour toujours. Les ingénieurs Android de Google pourraient facilement décider de modifier le code source de l'application. tag ce qui casserait votre code en vous laissant dans l'incapacité de trouver une référence à la structure existante de la Fragments .

Solution alternative sans recours à l'interne tag

Voici un exemple simple de la façon d'obtenir une référence à l'objet Fragments retourné par FragmentPagerAdapter qui ne s'appuie pas sur le système interne tags sur le Fragments . La clé est de passer outre instantiateItem() et y enregistrer les références au lieu de de dans getItem() .

public class SomeActivity extends Activity {
    private FragmentA m1stFragment;
    private FragmentB m2ndFragment;

    // other code in your Activity...

    private class CustomPagerAdapter extends FragmentPagerAdapter {
        // other code in your custom FragmentPagerAdapter...

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

        @Override
        public Fragment getItem(int position) {
            // Do NOT try to save references to the Fragments in getItem(),
            // because getItem() is not always called. If the Fragment
            // was already created then it will be retrieved from the FragmentManger
            // and not here (i.e. getItem() won't be called again).
            switch (position) {
                case 0:
                    return new FragmentA();
                case 1:
                    return new FragmentB();
                default:
                    // This should never happen. Always account for each position above
                    return null;
            }
        }

        // Here we can finally safely save a reference to the created
        // Fragment, no matter where it came from (either getItem() or
        // FragmentManger). Simply save the returned Fragment from
        // super.instantiateItem() into an appropriate reference depending
        // on the ViewPager position.
        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            Fragment createdFragment = (Fragment) super.instantiateItem(container, position);
            // save the appropriate reference depending on position
            switch (position) {
                case 0:
                    m1stFragment = (FragmentA) createdFragment;
                    break;
                case 1:
                    m2ndFragment = (FragmentB) createdFragment;
                    break;
            }
            return createdFragment;
        }
    }

    public void someMethod() {
        // do work on the referenced Fragments, but first check if they
        // even exist yet, otherwise you'll get an NPE.

        if (m1stFragment != null) {
            // m1stFragment.doWork();
        }

        if (m2ndFragment != null) {
            // m2ndFragment.doSomeWorkToo();
        }
    }
}

o si vous préférez travailler avec tags au lieu des variables membres de la classe/références à la Fragments vous pouvez également saisir le tags fixé par FragmentPagerAdapter de la même manière : NOTE : ceci ne s'applique pas à FragmentStatePagerAdapter puisqu'il ne définit pas tags lors de la création de son Fragments .

@Override
public Object instantiateItem(ViewGroup container, int position) {
    Fragment createdFragment = (Fragment) super.instantiateItem(container, position);
    // get the tags set by FragmentPagerAdapter
    switch (position) {
        case 0:
            String firstTag = createdFragment.getTag();
            break;
        case 1:
            String secondTag = createdFragment.getTag();
            break;
    }
    // ... save the tags somewhere so you can reference them later
    return createdFragment;
}

Notez que cette méthode ne repose PAS sur l'imitation de l'interface interne de l'entreprise. tag fixé par FragmentPagerAdapter et utilise plutôt des API appropriées pour les récupérer. De cette façon, même si le tag des changements dans les futures versions de l SupportLibrary vous serez toujours en sécurité.


N'oubliez pas qu'en fonction de la conception de votre Activity le Fragments sur lequel vous essayez de travailler peut ou peut ne pas exister encore, donc vous devez en tenir compte en faisant null vérifications avant d'utiliser vos références.

De même, si au lieu de vous travaillez avec FragmentStatePagerAdapter alors vous ne voulez pas garder des références dures à vos Fragments parce que vous pourriez en avoir beaucoup et que les références dures les garderaient inutilement en mémoire. Au lieu de cela, enregistrez les Fragment références en WeakReference au lieu des variables standard. Comme ceci :

WeakReference<Fragment> m1stFragment = new WeakReference<Fragment>(createdFragment);
// ...and access them like so
Fragment firstFragment = m1stFragment.get();
if (firstFragment != null) {
    // reference hasn't been cleared yet; do work...
}

17voto

Daniel De León Points 2842

Si le fragment est toujours en mémoire, vous pouvez le retrouver avec cette fonction.

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.

0 votes

Premièrement, le code dans cette réponse dépend de la compatibilité avec le nommage interne : si ce mécanisme change, ce code cessera de fonctionner. Deuxièmement, il n'y a pas vraiment de raison d'obtenir les références aux fragments par balise puisque vous pouvez simplement les stocker lorsque vous appelez instantiateItem dans votre onCreate comme décrit ici : stackoverflow.com/questions/14035090/

1voto

user158 Points 592

Pour les futurs lecteurs !

Si vous envisagez de réutiliser des fragments avec viewpager, la meilleure solution est d'utiliser ViewPager 2 puisque La vue Pager 2 fait appel à RecyclerView.

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