Mettre à jour ViewPager dynamiquement?

316

Je ne peux pas mettre à jour le contenu de ViewPager.

Quelle est l'utilisation correcte des méthodes instantiateItem () et getItem () dans la classe FragmentPagerAdapter?

J'utilisais uniquement getItem () pour instancier et renvoyer mes fragments:

@Override
public Fragment getItem(int position) {
    return new MyFragment(context, paramters);
}

Cela a bien fonctionné. Sauf que je ne peux pas changer le contenu.

J'ai donc trouvé ceci: ViewPager PagerAdapter ne met pas à jour la vue

"Mon approche consiste à utiliser la méthode setTag () pour toute vue instanciée dans la méthode instantiateItem ()"

Maintenant, je veux implémenter instantiateItem () pour le faire. Mais je ne sais pas ce que je dois retourner (le type est Object) et quelle est la relation avec getItem (int position)?

J'ai lu la référence :

  • public abstract Fragment getItem (int position)

    Renvoie le fragment associé à une position spécifiée.

  • public Object instantiateItem (conteneur ViewGroup, position int)

    Créez la page pour la position donnée. L'adaptateur est responsable de l'ajout de la vue au conteneur indiqué ici, mais il doit uniquement s'assurer que cela est fait au moment où il revient de finishUpdate (ViewGroup). Paramètres

    container La vue contenant dans laquelle la page sera affichée. position La position de la page à instancier.

    Retour

    Renvoie un objet représentant la nouvelle page. Cela n'a pas besoin d'être une vue, mais peut être un autre conteneur de la page.

mais je ne comprends toujours pas.

Voici mon code. J'utilise le package de support v4.

ViewPagerTest

public class ViewPagerTest extends FragmentActivity {
    private ViewPager pager;
    private MyFragmentAdapter adapter; 

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.pager1);

        pager = (ViewPager)findViewById(R.id.slider);

        String[] data = {"page1", "page2", "page3", "page4", "page5", "page6"};

        adapter = new MyFragmentAdapter(getSupportFragmentManager(), 6, this, data);
        pager.setAdapter(adapter);

        ((Button)findViewById(R.id.button)).setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                reload();
            }
        });
    }

    private void reload() {
        String[] data = {"changed1", "changed2", "changed3", "changed4", "changed5", "changed6"};
        //adapter = new MyFragmentAdapter(getSupportFragmentManager(), 6, this, data);
        adapter.setData(data);
        adapter.notifyDataSetChanged();
        pager.invalidate();

        //pager.setCurrentItem(0);
    }
}

MyFragmentAdapter

class MyFragmentAdapter extends FragmentPagerAdapter {
    private int slideCount;
    private Context context;
    private String[] data;

    public MyFragmentAdapter(FragmentManager fm, int slideCount, Context context, String[] data) {
        super(fm);
        this.slideCount = slideCount;
        this.context = context;
        this.data = data;
    }

    @Override
    public Fragment getItem(int position) {
        return new MyFragment(data[position], context);
    }

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

    public void setData(String[] data) {
        this.data = data;
    }

    @Override
    public int getItemPosition(Object object) {
        return POSITION_NONE;
    }
}

MyFragment

public final class MyFragment extends Fragment {
    private String text;

    public MyFragment(String text, Context context) {
        this.text = text;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.slide, null);
        ((TextView)view.findViewById(R.id.text)).setText(text);

        return view;
    }
}

Voici également quelqu'un avec un problème similaire, aucune réponse http://www.mail-archive.com/[email protected]/msg200477.html

Ixx
la source
Pour mieux comprendre les composants impliqués, il peut être utile de lire mon tutoriel sur un ViewPager à onglets avec un historique de navigation arrière séparé. Il est livré avec un exemple de travail sur GitHub et des ressources supplémentaires: medium.com/@nilan/…
marktani
obtenu un problème stackoverflow.com/questions/40149039/…
albert
Vous pouvez vérifier cet exemple dans GitHub . J'espère que cet exemple vous aidera.
Cabezas
Bonne solution simple: stackoverflow.com/a/35450575/2162226
Gene Bo
Google a récemment introduit ViewPager2 qui simplifie les mises à jour dynamiques de Fragment / View. Vous pouvez voir des exemples sur github.com/googlesamples/android-viewpager2 ou cette réponse sur stackoverflow.com/a/57393414/1333448
Yves Delerm

Réponses:

605

Lorsque vous utilisez FragmentPagerAdapter ou FragmentStatePagerAdapter, il est préférable de traiter uniquement getItem()et de ne pas toucher instantiateItem()du tout. L' interface instantiateItem()- destroyItem()- isViewFromObject()sur PagerAdapter est une interface de niveau inférieur que FragmentPagerAdapter utilise pour implémenter le plus simplegetItem() interface .

Avant d'entrer dans le détail, je dois préciser que

si vous souhaitez désactiver les fragments réels affichés, vous devez éviter FragmentPagerAdapter et utiliser FragmentStatePagerAdapter.

Une version antérieure de cette réponse a fait l'erreur d'utiliser FragmentPagerAdapter pour son exemple - cela ne fonctionnera pas car FragmentPagerAdapter ne détruit jamais un fragment après qu'il a été affiché la première fois.

Je ne recommande pas setTag()et findViewWithTag()solution de contournement fourni dans le poste que vous avez lié. Comme vous l'avez découvert, utiliser setTag()et findViewWithTag()ne fonctionne pas avec des fragments, donc ce n'est pas une bonne correspondance.

La bonne solution est de passer outre getItemPosition(). Lorsque notifyDataSetChanged()est appelé, ViewPager appelle getItemPosition()tous les éléments de son adaptateur pour voir s'ils doivent être déplacés vers une position différente ou supprimés.

Par défaut, getItemPosition()renvoie POSITION_UNCHANGED, ce qui signifie: "Cet objet est bien là où il se trouve, ne le détruisez pas ou ne le supprimez pas." Le retour POSITION_NONEcorrige le problème en disant à la place: "Cet objet n'est plus un élément que j'affiche, supprimez-le." Cela a donc pour effet de supprimer et de recréer chaque élément de votre adaptateur.

C'est une solution complètement légitime! Ce correctif permet à notifyDataSetChanged de se comporter comme un adaptateur standard sans recyclage de vue. Si vous implémentez ce correctif et que les performances sont satisfaisantes, vous partez pour les courses. Travail accompli.

Si vous avez besoin de meilleures performances, vous pouvez utiliser une getItemPosition()implémentation plus sophistiquée . Voici un exemple pour un pager créant des fragments à partir d'une liste de chaînes:

ViewPager pager = /* get my ViewPager */;
// assume this actually has stuff in it
final ArrayList<String> titles = new ArrayList<String>();

FragmentManager fm = getSupportFragmentManager();
pager.setAdapter(new FragmentStatePagerAdapter(fm) {
    public int getCount() {
        return titles.size();
    }

    public Fragment getItem(int position) {
        MyFragment fragment = new MyFragment();
        fragment.setTitle(titles.get(position));
        return fragment;
    }

    public int getItemPosition(Object item) {
        MyFragment fragment = (MyFragment)item;
        String title = fragment.getTitle();
        int position = titles.indexOf(title);

        if (position >= 0) {
            return position;
        } else {
            return POSITION_NONE;
        }
    }
});

Avec cette implémentation, seuls les fragments affichant de nouveaux titres seront affichés. Tous les fragments affichant des titres qui sont encore dans la liste seront à la place déplacés vers leur nouvelle position dans la liste, et les fragments avec des titres qui ne sont plus du tout dans la liste seront détruits.

Que faire si le fragment n'a pas été recréé, mais doit être mis à jour de toute façon? Les mises à jour d'un fragment vivant sont mieux gérées par le fragment lui-même. C'est l'avantage d'avoir un fragment, après tout - c'est son propre contrôleur. Un fragment peut ajouter un écouteur ou un observateur à un autre objet dans onCreate(), puis le supprimer onDestroy(), gérant ainsi les mises à jour lui-même. Vous n'avez pas besoin de mettre tout le code de mise à jour à l'intérieurgetItem() comme vous le faites dans un adaptateur pour un ListView ou d'autres types AdapterView.

Une dernière chose - ce n'est pas parce que FragmentPagerAdapter ne détruit pas un fragment que getItemPosition est complètement inutile dans un FragmentPagerAdapter. Vous pouvez toujours utiliser ce rappel pour réorganiser vos fragments dans le ViewPager. Cependant, il ne les supprimera jamais complètement du FragmentManager.

Bill Phillips
la source
4
Ne fonctionne pas, il ne met toujours rien à jour ... a posté mon code.
Ixx
64
D'accord, vous avez résolu le problème - vous devez utiliser FragmentStatePagerAdapter plutôt que FragmentPagerAdapter. FragmentPagerAdapter ne supprime jamais les fragments du FragmentManager, il les détache et les attache uniquement. Consultez la documentation pour plus d'informations - en général, vous ne souhaitez utiliser FragmentPagerAdapter que pour les fragments permanents. J'ai édité mon exemple avec la correction.
Bill Phillips
1
Je reverrai cela à un moment donné plus tard ... merci pour votre réponse. Et puis j'accepte votre réponse si cela fonctionne. Avant votre dernier commentaire, j'ai implémenté pour supprimer et ajouter un nouveau ViewPager à chaque fois et cela fonctionne. Pas une très bonne solution, mais je n'ai pas le temps maintenant de la changer.
Ixx
12
Vous avez raison, changer la classe de l'adaptateur en FragmentStatePagerAdapter a résolu tous mes problèmes. Merci!
Ixx
2
Même lorsque je retourne POSITION_NONE, pendant l'utilisation FragmentStatePagerAdapter, je peux voir qu'il supprime mon fragment et crée la nouvelle instance. Mais la nouvelle instance reçoit un BundleInstanceState enregistré avec l'état d'un ancien fragment. Comment puis-je détruire complètement le fragment pour que Bundle soit nul lors de sa création?
Chanté le
142

Au lieu de retourner à POSITION_NONEpartir getItemPosition()et causer des loisirs pleine vue, faites ceci:

//call this method to update fragments in ViewPager dynamically
public void update(UpdateData xyzData) {
    this.updateData = xyzData;
    notifyDataSetChanged();
}

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

Vos fragments doivent implémenter l' UpdateableFragmentinterface:

public class SomeFragment extends Fragment implements
    UpdateableFragment{

    @Override
    public void update(UpdateData xyzData) {
         // this method will be called for every fragment in viewpager
         // so check if update is for this fragment
         if(forMe(xyzData)) {
           // do whatever you want to update your UI
         }
    }
}

et l'interface:

public interface UpdateableFragment {
   public void update(UpdateData xyzData);
}

Votre classe de données:

public class UpdateData {
    //whatever you want here
}
M-WaJeEh
la source
1
Est-ce à dire que votre MainActivity doit également implémenter l'interface ??? S'il vous plaît, aidez-moi ici. Dans mon implémentation actuelle, j'utilise ma classe MainActivity comme communicateur entre les fragments, donc j'étais confus à propos de cette solution.
Rakeeb Rajbhandari
1
Ok, j'ai répondu à un autre endroit avec une implémentation complète où je mets à jour Fragmentdynamiquement, jetez un œil: stackoverflow.com/questions/19891828/…
M-WaJeEh
@ M-WaJeEh peut-être un exemple s'il vous plaît !!! j'ai une listview avec la liste des villes et après avoir sélectionné la ville ouvre le viewpager (3 pages) avec des informations sur cette ville. ça marche bien. mais après avoir sélectionné un autre viewpager de ville, seule la d page on refreshes the 1-st page only after swiping pages? thw 2page 3D est toujours vierge. merci
SERG
4
parmi toutes les choses que j'ai vues pendant les 2,5 derniers jours de ma vie professionnelle .. c'est la seule qui a fonctionné pour moi .. toutes sortes d'arigato, merci, spaciba, sukran .. n.
Orkun Ozen
2
Mother of Dragons !, cela fonctionne comme un charme, la seule solution a fonctionné pour moi ... merci beaucoup !!
Sebastián Guerrero
24

pour ceux qui sont toujours confrontés au même problème que j'ai rencontré auparavant quand j'ai un ViewPageravec 7 fragments. la valeur par défaut pour ces fragments pour charger le contenu anglais du APIservice, mais le problème ici que je veux changer la langue de l'activité des paramètres et après avoir terminé l'activité des paramètres, je veux que l' ViewPageractivité principale rafraîchisse les fragments pour correspondre à la sélection de la langue de l'utilisateur et charge le contenu arabe si l'utilisateur choisit l'arabe ici ce que j'ai fait pour travailler la première fois

1- Vous devez utiliser FragmentStatePagerAdapter comme mentionné ci-dessus.

2- sur mainActivity, j'ai remplacé onResume et j'ai fait ce qui suit

if (!(mPagerAdapter == null)) {
    mPagerAdapter.notifyDataSetChanged();
}

3-i a getItemPosition()remplacé le dans mPagerAdapter et le faire revenir POSITION_NONE.

@Override
public int getItemPosition(Object object) {
    return POSITION_NONE;
}

fonctionne comme un charme

Ziad Gholmish
la source
1
Fonctionne sauf dans une situtaion. Lorsque vous supprimez le dernier élément de viewPager. Ensuite, il ne supprime pas de la liste.
Vlado Pandžić
J'ajoutais des fragments à gauche et à droite du fragment actuel dans le viseur dynamiquement. Celui-ci a fonctionné! Merci.
h8pathak
15

J'ai rencontré ce problème et l'ai finalement résolu aujourd'hui, alors j'écris ce que j'ai appris et j'espère qu'il sera utile pour quelqu'un qui est nouveau sur Android ViewPageret le mettra à jour comme je le fais. J'utilise FragmentStatePagerAdapteren API niveau 17 et n'ai actuellement que 2 fragments. Je pense qu'il doit y avoir quelque chose de pas correct, veuillez me corriger, merci. entrez la description de l'image ici

  1. Les données sérialisées doivent être chargées en mémoire. Cela peut être fait en utilisant un CursorLoader/ AsyncTask/ Thread. Le chargement automatique dépend de votre code. Si vous utilisez un CursorLoader, il est automatiquement chargé car il existe un observateur de données enregistré.

  2. Après avoir appelé viewpager.setAdapter(pageradapter), l'adaptateur getCount()est constamment appelé pour créer des fragments. Donc, si les données sont en cours de chargement, getCount()peut retourner 0, vous n'avez donc pas besoin de créer de fragments factices pour aucune donnée affichée.

  3. Une fois les données chargées, l'adaptateur getCount()ne crée pas de fragments automatiquement car il est toujours égal à 0, nous pouvons donc définir le numéro de données réellement chargé à renvoyer getCount(), puis appeler l'adaptateur notifyDataSetChanged(). ViewPagercommencer à créer des fragments (juste les 2 premiers fragments) par des données en mémoire. C'est fait avant d' notifyDataSetChanged()être retourné. Ensuite, le ViewPagera les bons fragments dont vous avez besoin.

  4. Si les données dans la base de données et la mémoire sont toutes les deux mises à jour (écriture continue), ou seulement les données en mémoire sont mises à jour (réécriture), ou seules les données dans la base de données sont mises à jour. Dans les deux derniers cas, si les données ne sont pas automatiquement chargées de la base de données dans la mémoire (comme mentionné ci-dessus). L' ViewPageradaptateur pager et traite uniquement les données en mémoire.

  5. Ainsi, lorsque les données en mémoire sont mises à jour, il nous suffit d'appeler l'adaptateur notifyDataSetChanged(). Puisque le fragment est déjà créé, l'adaptateur onItemPosition()sera appelé avant les notifyDataSetChanged()retours. Rien à faire getItemPosition(). Ensuite, les données sont mises à jour.

oscarthecat
la source
j'ai trouvé le rien à faire après avoir mis à jour les données (en mémoire), il suffit d'appeler notifyDataSetChanged()puis de mettre à jour automatiquement, car le fragment (avec graphique) que j'utilise se rafraîchira. si ce n'est pas le cas, comme un fragment statique, je onItemPositon()POSITION_NONE
devrai
Quelqu'un a-t-il une idée de quel outil a été utilisé pour créer ce diagramme?
JuiCe
12

Essayez destroyDrawingCache()ViewPager après notifyDataSetChanged()dans votre code.

czaku
la source
3
Pareil ici. Fonctionne très bien merci. J'ai eu particulièrement du mal parce que je n'utilise pas Fragmentsmon téléavertisseur. Alors merci!
Seth
11

Après des heures de frustration en essayant toutes les solutions ci-dessus pour surmonter ce problème et en essayant également de nombreuses solutions sur d'autres questions similaires comme celle-ci , ceci et cela qui ont échoué avec moi pour résoudre ce problème et pour faire ViewPagerdétruire l'ancien Fragmentet remplir le pageravec le nouveau par Fragment. J'ai résolu le problème comme suit:

1) Faites en sorte que la ViewPagerclasse s'étende FragmentPagerAdaptercomme suit:

 public class myPagerAdapter extends FragmentPagerAdapter {

2) Créez un article pour le ViewPagerqui stocke le titleet le fragmentsuivant:

public class PagerItem {
private String mTitle;
private Fragment mFragment;


public PagerItem(String mTitle, Fragment mFragment) {
    this.mTitle = mTitle;
    this.mFragment = mFragment;
}
public String getTitle() {
    return mTitle;
}
public Fragment getFragment() {
    return mFragment;
}
public void setTitle(String mTitle) {
    this.mTitle = mTitle;
}

public void setFragment(Fragment mFragment) {
    this.mFragment = mFragment;
}

}

3) Faites en ViewPagersorte que le constructeur de la prise mon FragmentManagerinstance le stocke dans mon classcomme suit:

private FragmentManager mFragmentManager;
private ArrayList<PagerItem> mPagerItems;

public MyPagerAdapter(FragmentManager fragmentManager, ArrayList<PagerItem> pagerItems) {
    super(fragmentManager);
    mFragmentManager = fragmentManager;
    mPagerItems = pagerItems;
}

4) Créez une méthode pour réinitialiser les adapterdonnées avec les nouvelles données en supprimant directement toutes les précédentes fragmentde la fragmentManagermême afin adapterde définir à nouveau les nouvelles fragmentde la nouvelle liste comme suit:

public void setPagerItems(ArrayList<PagerItem> pagerItems) {
    if (mPagerItems != null)
        for (int i = 0; i < mPagerItems.size(); i++) {
            mFragmentManager.beginTransaction().remove(mPagerItems.get(i).getFragment()).commit();
        }
    mPagerItems = pagerItems;
}

5) À partir du conteneur Activityou Fragmentne réinitialisez pas l'adaptateur avec les nouvelles données. Définissez les nouvelles données via la méthode setPagerItemsavec les nouvelles données comme suit:

ArrayList<PagerItem> pagerItems = new ArrayList<PagerItem>();
    pagerItems.add(new PagerItem("Fragment1", new MyFragment1()));
    pagerItems.add(new PagerItem("Fragment2", new MyFragment2()));

    mPagerAdapter.setPagerItems(pagerItems);
    mPagerAdapter.notifyDataSetChanged();

J'espère que ça aide.

Sami Eltamawy
la source
pouvez-vous m'aider dans mon problème stackoverflow.com/questions/40149039/…
albert
pouvez-vous s'il vous plaît m'aider avec ce problème stackoverflow.com/questions/41547995/…
viper
10

Pour une raison quelconque, aucune des réponses n'a fonctionné pour moi, j'ai donc dû remplacer la méthode restoreState sans appeler super dans mon fragmentStatePagerAdapter. Code:

private class MyAdapter extends FragmentStatePagerAdapter {

    // [Rest of implementation]

    @Override
    public void restoreState(Parcelable state, ClassLoader loader) {}

}
hordurh
la source
1
Après avoir tout essayé dans cette réponse SO, c'est celle qui a fonctionné pour moi. Je termine () une activité qui revient à l'activité qui héberge le PagerAdapter en question. Je veux que tous mes fragments soient recréés dans ce cas car l'activité que je viens de terminer peut avoir changé l'état utilisé pour dessiner les fragments.
JohnnyLambada
OMG .. cela fonctionne .. cela fonctionne .. bravo: D Dans mon cas, je dois supprimer l'objet d'élément actuel du Viewpager. mais le fragment actuel n'est pas mis à jour après avoir essayé toutes les méthodes / étapes ci-dessus.
Rahul Khurana
3

J'ai légèrement modifié la solution proposée par Bill Phillips pour l'adapter à mes besoins

private class PagerAdapter extends FragmentStatePagerAdapter{
    Bundle oBundle;
    FragmentManager oFragmentManager;
    ArrayList<Fragment> oPooledFragments;

public PagerAdapter(FragmentManager fm) {
    super(fm);
    oFragmentManager=fm;

    }
@Override
public int getItemPosition(Object object) {

    Fragment oFragment=(Fragment)object;
    oPooledFragments=new ArrayList<>(oFragmentManager.getFragments());
    if(oPooledFragments.contains(oFragment))
        return POSITION_NONE;
    else
        return POSITION_UNCHANGED;
    } 
}

de sorte que le getItemPosition()retourne POSITION_NONEuniquement pour les fragments qui sont actuellement dans le FragmentManagerquand getItemPositionest appelé. (Notez que cela FragmentStatePageret les éléments ViewPagerassociés sont contenus dans un fragment qui n'est pas dans une activité)

attendre
la source
2

J'ai eu un problème similaire, mais je ne veux pas me fier aux solutions existantes (noms de balises codées en dur, etc.) et je n'ai pas pu faire fonctionner la solution de M-WaJeEh pour moi. Voici ma solution:

Je garde des références aux fragments créés dans getItem dans un tableau. Cela fonctionne bien tant que l'activité n'est pas détruite en raison de configurationChange ou d'un manque de mémoire ou autre (-> en revenant à l'activité, les fragments reviennent à leur dernier état sans que 'getItem' soit appelé à nouveau et donc sans mettre à jour le tableau ).

Pour éviter ce problème, j'ai implémenté instantiateItem (ViewGroup, int) et y mettre à jour mon tableau, comme ceci:

        @Override
    public Object instantiateItem(ViewGroup container, int position) {
        Object o = super.instantiateItem(container, position);
        if(o instanceof FragmentX){
            myFragments[0] = (FragmentX)o;
        }else if(o instanceof FragmentY){
            myFragments[1] = (FragmentY)o;
        }else if(o instanceof FragmentZ){
            myFragments[2] = (FragmentZ)o;
        }
        return o;
    }

Donc, d'une part, je suis heureux d'avoir trouvé une solution qui fonctionne pour moi et que je voulais la partager avec vous, mais je voulais aussi demander si quelqu'un d'autre a essayé quelque chose de similaire et s'il y a une raison pour laquelle je ne devrais pas fais comme ça? Jusqu'à présent, cela fonctionne très bien pour moi ...

jpm
la source
J'ai fait quelque chose lié à stackoverflow.com/a/23427215/954643 , bien que vous devriez probablement supprimer l'élément de myFragmentsdans public void destroyItem(ViewGroup container, int position, Object object)si bien que vous ne prenez pas une référence fragement rassis.
qix
1

J'utilise EventBus bibliothèque pour mettre à jour le Fragmentcontenu en ViewPager. La logique est simple, tout comme le document d'EventBus comment faire . Il n'est pas nécessaire de contrôler l' FragmentPagerAdapterinstance. Le code est ici:

1: Définir les événements

Définissez le message à mettre à jour.

public class UpdateCountEvent {
    public final int count;

    public UpdateCountEvent(int count) {
        this.count = count;
    }
}

2. préparer les abonnés

Écrivez ci-dessous le code dans le fragment dont la mise à jour est nécessaire.

@Override
public void onStart() {
    super.onStart();
    EventBus.getDefault().register(this);
}

@Override
public void onStop() {
    EventBus.getDefault().unregister(this);  
    super.onStop();
}

public void onEvent(UpdateCountEvent event) {//get update message
    Toast.makeText(getActivity(), event.count, Toast.LENGTH_SHORT).show();
}

3.Post événements

Écrivez ci-dessous le code dans un autre Activityou un autre Fragmentqui doit mettre à jour le paramètre

//input update message
EventBus.getDefault().post(new UpdateCountEvent(count));
Fantaisie Fang
la source
1

Si vous souhaitez utiliser FragmentStatePagerAdapter, veuillez consulter https://code.google.com/p/android/issues/detail?can=2&start=0&num=100&q=&colspec=ID%20Type%20Status%20Owner%20Summary% 20Stars & groupby = & sort = & id = 37990 . Il existe des problèmes avec FragmentStatePagerAdapter qui peuvent ou non perturber votre cas d'utilisation.

De plus, link a aussi peu de solutions ... peu peuvent convenir à vos besoins.

cgr
la source
À partir de cette URL, j'ai trouvé une solution. J'ai utilisé le FragmentStatePagerAdapter modifié de ce gist gist.github.com/ypresto/8c13cb88a0973d071a64 dans mon code et il a commencé à fonctionner comme supposé l'être.
Rafael
Cool. Oui. Link a aussi peu de solutions.
cgr
1

J'ai vécu le même problème et j'ai cherché trop de fois. Toute réponse donnée dans stackoverflow ou via google n'était pas une solution à mon problème. Mon problème était facile. J'ai une liste, je montre cette liste avec viewpager. Lorsque j'ajoute un nouvel élément en tête de la liste et que j'actualise le viseur, rien n'a changé. Ma solution finale était très simple à utiliser. Lorsqu'un nouvel élément est ajouté à la liste et que vous souhaitez actualiser la liste. Définissez d'abord l'adaptateur de viewpager sur null, puis recréez l'adaptateur et définissez i sur viewpager.

myVPager.setAdapter(null);
myFragmentAdapter = new MyFragmentAdapter(getSupportFragmentManager(),newList);
myVPager.setAdapter(myFragmentAdapter);

Assurez-vous que votre adaptateur doit étendre FragmentStatePagerAdapter

nebyan
la source
1

Voici mon implémentation qui intègre les informations de @Bill Phillips One obtient la mise en cache des fragments la plupart du temps, sauf lorsque les données ont changé. Simple et semble bien fonctionner.

MyFragmentStatePagerAdapter.java

private boolean mIsUpdating = false;
public void setIsUpdating(boolean mIsUpdating) {
        this.mIsUpdating = mIsUpdating;
    }


@Override
public int getItemPosition(@NonNull Object object) {

    if (mIsUpdating) {
        return POSITION_NONE;
    }
    else {
        return super.getItemPosition(object);
    }
}

MyActivity.java

mAdapter.setIsUpdating(true);
mAdapter.notifyDataSetChanged();
mAdapter.setIsUpdating(false);
voam
la source
0

J'avais essayé tellement d'approches différentes, aucune n'a vraiment soulevé mon problème. Voici comment je le résous avec un mélange de solutions fournies par vous tous. Merci tout le monde.

class PagerAdapter extends FragmentPagerAdapter {

    public boolean flag_refresh=false;

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

    @Override
    public Fragment getItem(int page) {
        FragmentsMain f;
        f=new FragmentsMain();
        f.page=page;
        return f;
    }

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

    @Override
    public int getItemPosition(Object item) {
        int page= ((FragmentsMain)item).page;

        if (page == 0 && flag_refresh) {
            flag_refresh=false;
            return POSITION_NONE;
        } else {
            return super.getItemPosition(item);
        }
    }

    @Override
    public void destroyItem(View container, int position, Object object) {

        ((ViewPager) container).removeView((View) object);
    }
}

Je veux seulement rafraîchir la page 0 après onResume ().

 adapter=new PagerAdapter(getSupportFragmentManager());
 pager.setAdapter(adapter);

@Override
protected void onResume() {
    super.onResume();

    if (adapter!=null) {
        adapter.flag_refresh=true;
        adapter.notifyDataSetChanged();
    }
}

Dans mon FragmentsMain, il y a une "page" d'entier public, qui peut me dire si c'est la page que je veux rafraîchir.

public class FragmentsMain extends Fragment {

private Cursor cursor;
private static Context context;
public int page=-1;
Ted Hsieh
la source
0

Je sais que je suis en retard pour le Parti. J'ai résolu le problème en appelant TabLayout#setupWithViewPager(myViewPager);juste aprèsFragmentPagerAdapter#notifyDataSetChanged();

theapache64
la source
0

Cette solution ne fonctionnera pas pour tout le monde, mais dans mon cas, chaque fragment de mon ViewPager est une classe différente, et un seul d'entre eux existe à la fois.

Avec cette contrainte, cette solution est sûre et doit être sûre à utiliser en production.

private void updateFragment(Item item) {

    List<Fragment> fragments = getSupportFragmentManager().getFragments();
    for (Fragment fragment : fragments) {

        if (fragment instanceof MyItemFragment && fragment.isVisible()) {
            ((MyItemFragment) fragment).update(item);
        }

    }
}

Si vous disposez de plusieurs versions du même fragment, vous pouvez utiliser cette même stratégie pour appeler des méthodes sur ces fragments afin de déterminer s'il s'agit du fragment que vous souhaitez mettre à jour.

Kevin Grant
la source
0

Cela pourrait être utile à quelqu'un - dans mon cas, lors de l'insertion d'une nouvelle page, le pager de vue demandait la position d'un fragment existant deux fois, mais ne demandait pas la position du nouvel élément, provoquant un comportement incorrect et des données ne s'affichant pas.

  1. Copiez la source de FragmentStatePagerAdapter (semble ne pas avoir été mis à jour depuis longtemps).

  2. Remplacez notifyDataSetChanged ()

    @Override
    public void notifyDataSetChanged() {
        mFragments.clear();
        super.notifyDataSetChanged();
    }
  3. Ajoutez une vérification d'intégrité à destroyItem () pour éviter les plantages:

    if (position < mFragments.size()) {
        mFragments.set(position, null);
    }
Méchant homme
la source
0

J'ai parcouru toutes les réponses ci-dessus et un certain nombre d'autres messages, mais je n'ai toujours pas trouvé quelque chose qui fonctionnait pour moi (avec différents types de fragments ainsi que l'ajout et la suppression dynamiques d'onglets). L'approche FWIW suivante est ce qui a fonctionné pour moi (au cas où quelqu'un d'autre aurait les mêmes problèmes).

public class MyFragmentStatePageAdapter extends FragmentStatePagerAdapter {
    private static final String TAB1_TITLE = "Tab 1";
    private static final String TAB2_TITLE = "Tab 2";
    private static final String TAB3_TITLE = "Tab 3";

    private ArrayList<String> titles = new ArrayList<>();
    private Map<Fragment, Integer> fragmentPositions = new HashMap<>();


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


    public void update(boolean showTab1, boolean showTab2, boolean showTab3) {
        titles.clear();

        if (showTab1) {
            titles.add(TAB1_TITLE);
        }
        if (showTab2) {
            titles.add(TAB2_TITLE);
        }
        if (showTab3) {
            titles.add(TAB3_TITLE);
        }
        notifyDataSetChanged();
    }


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

    @Override
    public Fragment getItem(int position) {
        Fragment fragment = null;

        String tabName = titles.get(position);
        if (tabName.equals(TAB1_TITLE)) { 
            fragment =  Tab1Fragment.newInstance();
        } else if (tabName.equals(TAB2_TITLE)) {
            fragment =  Tab2Fragment.newInstance();
        } else if (tabName.equals(TAB3_TITLE)) {
            fragment =  Tab3Fragmen.newInstance();
        } 

        ((BaseFragment)fragment).setTitle(tabName);
        fragmentPositions.put(fragment, position);
        return fragment;
    }

    @Override
    public CharSequence getPageTitle(int position) {
        return titles.get(position);
    }

    @Override
    public int getItemPosition(Object item) {
        BaseFragment fragment = (BaseFragment)item;
        String title = fragment.getTitle();
        int position = titles.indexOf(title);

        Integer fragmentPosition = fragmentPositions.get(item);
        if (fragmentPosition != null && position == fragmentPosition) {
            return POSITION_UNCHANGED;
        } else {
            return POSITION_NONE;
        }
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        super.destroyItem(container, position, object);
        fragmentPositions.remove(object);
    }
}
John O'Reilly
la source
0

Utilisez FragmentStatePagerAdapter au lieu de FragmentPagerAdapter si vous souhaitez recréer ou recharger le fragment sur la base de l'index Par exemple, si vous souhaitez recharger un fragment autre que FirstFragment, vous pouvez vérifier l'instance et renvoyer la position comme ceci

 public int getItemPosition(Object item) {
    if(item instanceof FirstFragment){
       return 0;
    }
    return POSITION_NONE;
 }
HemangNirmal
la source
0

Vous devez modifier l'élément mFragments d'instantiateItem getItemPosition.

    if (mFragments.size() > position) {
        Fragment f = mFragments.get(position);

        if (f != null) {
            int newPosition = getItemPosition(f);
            if (newPosition == POSITION_UNCHANGED) {
                return f;
            } else if (newPosition == POSITION_NONE) {
                mFragments.set(position, null);
            } else {
                mFragments.set(newPosition, f);
            }
        }
    }

Basé sur AndroidX FragmentStatePagerAdapter.java, car mFragments la position des éléments de ne change pas lors de l'appel à notifyDataSetChanged ().

La source: https://github.com/cuichanghao/infivt/blob/master/library/src/main/java/cc/cuichanghao/library/FragmentStatePagerChangeableAdapter.java

Exemple: https://github.com/cuichanghao/infivt/blob/master/app/src/main/java/cc/cuichanghao/infivt/MainActivityChangeablePager.kt

Vous pouvez exécuter ce projet pour confirmer le fonctionnement. https://github.com/cuichanghao/infivt

changhao cui
la source