332 votes

Mettre à jour le ViewPager de façon dynamique ?

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

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

J'utilisais seulement getItem() pour instancier et retourner 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.

Alors j'ai trouvé ça : ViewPager PagerAdapter ne met pas à jour la vue

"Mon approche est d'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 le référence :

  • public abstract Fragment getItem (int position)

    Retourne le Fragment associé à une position spécifiée.

  • public Object instantiateItem (ViewGroup container, int position)

    Crée la page pour la position donnée. L'adaptateur est responsable de l'ajout de la vue au conteneur donné ici, bien qu'il doive seulement s'assurer que cela est fait au moment où il retourne de finishUpdate(ViewGroup). Paramètres

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

    Renvoie à

    Renvoie un objet représentant la nouvelle page. Il ne s'agit pas nécessairement d'une vue, mais peut être un autre conteneur de la page.

mais je ne comprends toujours pas.

Voici mon code. J'utilise le paquet 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);
    }
}

MonFragmentAdapter

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;
    }
}

MonFragment

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 qui a un problème similaire, sans réponse. http://www.mail-archive.com/Android-developers@googlegroups.com/msg200477.html

0 votes

Pour mieux comprendre les composants impliqués, il peut être utile de lire mon tutoriel sur un ViewPager à onglets avec historique séparé de la navigation arrière. Il est accompagné d'un exemple fonctionnel sur GitHub et de ressources supplémentaires : medium.com/@nilan/

0 votes

0 votes

Vous pouvez vérifier ceci exemple dans GitHub . J'espère que cet exemple vous aidera.

622voto

Bill Phillips Points 3264

Lors de l'utilisation de FragmentPagerAdapter ou de FragmentStatePagerAdapter, il est préférable de s'occuper exclusivement de getItem() et ne pas toucher instantiateItem() du tout. Le site instantiateItem() - destroyItem() - isViewFromObject() sur PagerAdapter est une interface de niveau inférieur que FragmentPagerAdapter utilise pour mettre en œuvre l'interface beaucoup plus simple getItem() interface.

Avant d'entrer dans le vif du sujet, je dois préciser que

si vous voulez changer les fragments réels qui sont affichés, vous devez éviter FragmentPagerAdapter et utiliser FragmentStatePagerAdapter.

Une version antérieure de cette réponse commettait l'erreur d'utiliser FragmentPagerAdapter pour son exemple - cela ne fonctionnera pas car FragmentPagerAdapter ne détruit jamais un fragment après son premier affichage.

Je ne recommande pas le setTag() y findViewWithTag() La solution de contournement fournie dans l'article dont vous avez donné le lien. Comme vous l'avez découvert, l'utilisation de setTag() y findViewWithTag() ne fonctionne pas avec les fragments, donc ce n'est pas une bonne combinaison.

La bonne solution consiste à remplacer getItemPosition() . Quand notifyDataSetChanged() est appelé, ViewPager appelle getItemPosition() sur tous les éléments de son adaptateur pour voir s'ils doivent être déplacés vers une autre position ou retirés.

Par défaut, getItemPosition() renvoie à POSITION_UNCHANGED ce qui signifie : "Cet objet est bien là où il est, ne le détruisez pas et ne l'enlevez pas". Retour à POSITION_NONE résout le problème en disant plutôt : "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.

Il s'agit d'un correctif tout à fait légitime ! Grâce à cette correction, notifyDataSetChanged se comporte comme un adaptateur normal sans recyclage de vue. Si vous implémentez ce correctif et que les performances sont satisfaisantes, vous êtes sur la bonne voie. Le travail est terminé.

Si vous avez besoin d'une meilleure performance, vous pouvez utiliser une getItemPosition() implémentation. Voici un exemple de pager créant des fragments à partir d'une liste de chaînes de caractères :

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 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 - il est son propre contrôleur. Un fragment peut ajouter un auditeur ou un observateur à un autre objet dans la section onCreate() puis le retirer dans onDestroy() et gère ainsi lui-même les mises à jour. Il n'est pas nécessaire de mettre tout le code de mise à jour à l'intérieur du programme. getItem() comme vous le faites dans un adaptateur pour un ListView ou d'autres types de AdapterView.

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

4 votes

Ça ne marche pas, ça ne met toujours rien à jour... j'ai posté mon code.

68 votes

Ok, j'ai trouvé le problème - vous devez utiliser FragmentStatePagerAdapter plutôt que FragmentPagerAdapter. Le FragmentPagerAdapter ne supprime jamais les fragments du FragmentManager, il ne fait que les détacher et les attacher. Consultez la documentation pour plus d'informations - en général, vous ne voulez utiliser FragmentPagerAdapter que pour les fragments qui sont permanents. J'ai modifié mon exemple avec la correction.

1 votes

Je reverrai cela à un moment donné plus tard... merci pour votre réponse. Et puis j'accepte votre réponse si elle fonctionne. Avant votre dernier commentaire, j'ai implémenté de supprimer et ajouter un nouveau ViewPager à chaque fois et cela fonctionne. Ce n'est pas une très bonne solution, mais je n'ai pas le temps maintenant de la changer.

149voto

M-WaJeEh Points 5957

Au lieu de renvoyer POSITION_NONE de getItemPosition() et provoquer une récréation en pleine vue, faites ça :

//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 mettre en œuvre UpdateableFragment interface :

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
}

1 votes

Cela signifie-t-il que votre activité principale doit également mettre en œuvre l'interface ? Veuillez m'aider. Dans mon implémentation actuelle, j'utilise ma classe MainActivity comme un communicateur entre les fragments, donc je suis confus à propos de cette solution.

1 votes

Ok, j'ai répondu à un autre endroit avec une mise en œuvre complète où je suis en train de mettre à jour. Fragment dynamiquement, jetez-y un coup d'œil : stackoverflow.com/questions/19891828/

0 votes

@M-WaJeEh peut-être un exemple s'il vous plaît !!! j'ai un listview avec une liste de villes et après avoir sélectionné une ville, le viewpager s'ouvre (3 pages) avec des informations sur cette ville. cela fonctionne bien. mais après avoir sélectionné une autre ville, le viewpager affiche seulement les 3 pages. d page on refreshes the 1-st page only after swiping pages? thw 2 la page d est toujours vide. merci

16voto

oscarthecat Points 153

J'ai rencontré ce problème et je l'ai finalement résolu aujourd'hui. J'écris donc ce que j'ai appris et j'espère que cela sera utile à quelqu'un qui est nouveau dans le monde d'Android. ViewPager et mettre à jour comme je le fais. J'utilise FragmentStatePagerAdapter au niveau 17 de l'API et n'a actuellement que 2 fragments. Je pense qu'il doit y avoir quelque chose qui n'est pas correct, s'il vous plaît corrigez-moi, merci. enter image description here

  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 fait qu'il soit automatiquement chargé dépend de votre code. Si vous utilisez un CursorLoader il est chargé automatiquement puisqu'il y a un observateur de données enregistré.

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

  3. Après le chargement des données, l'adaptateur ne construira pas automatiquement les fragments car getCount() est toujours 0, donc nous pouvons définir le numéro de données réellement chargées à retourner par getCount() puis appelle la fonction notifyDataSetChanged() . ViewPager commencer à créer des fragments (juste les 2 premiers fragments) par des données en mémoire. C'est fait avant notifyDataSetChanged() est retourné. Ensuite, le ViewPager a les bons fragments dont vous avez besoin.

  4. Si les données de la base de données et de la mémoire sont toutes deux mises à jour (write through), ou si seules les données de la mémoire sont mises à jour (write back), ou encore si seules les données de 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 vers la mémoire (comme mentionné ci-dessus). Le site ViewPager et l'adaptateur de téléavertisseur ne traitent que des données en mémoire.

  5. Ainsi, lorsque les données en mémoire sont mises à jour, il suffit d'appeler l'adaptateur notifyDataSetChanged() . Étant donné que le fragment est déjà créé, l'adaptateur onItemPosition() sera appelé avant notifyDataSetChanged( retours. Rien ne doit être fait dans getItemPosition() . Ensuite, les données sont mises à jour.

0 votes

J'ai trouvé qu'il n'y a rien à faire après avoir mis à jour les données (en mémoire), il suffit d'appeler notifyDataSetChanged() puis mis à jour automatiquement, parce que le fragment (avec chartview) que j'utilise se rafraîchira lui-même. S'il ne l'est pas, comme un fragment statique, je devrai appeler onItemPositon() et retourne POSITION_NONE Désolé pour ça.

0 votes

Quelqu'un a-t-il une idée de l'outil utilisé pour réaliser ce diagramme ?

13voto

czaku Points 385

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

3 votes

Même chose ici. Ça marche très bien, merci. J'ai surtout eu du mal parce que je n'utilise pas de Fragments dans mon afficheur. Je vous remercie donc !

11voto

Tamawy Points 1447

Après des heures de frustration en essayant toutes les solutions ci-dessus pour surmonter ce problème et aussi en essayant de nombreuses solutions sur d'autres questions similaires comme ce , ce y ce qui ont tous ÉCHOUÉ avec moi pour résoudre ce problème et faire le ViewPager pour détruire l'ancien Fragment et remplir le pager avec le nouveau Fragment s. J'ai résolu le problème de la manière suivante :

1) Faites le ViewPager pour étendre la classe FragmentPagerAdapter comme suit :

 public class myPagerAdapter extends FragmentPagerAdapter {

2) Créer un élément pour le ViewPager qui stockent les title y el fragment comme suit :

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 sorte que le constructeur de l'objet ViewPager prendre mon FragmentManager pour la stocker dans mon class comme 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 le adapter avec les nouvelles données en supprimant toutes les données précédentes. fragment de la fragmentManager directement pour rendre le adapter pour définir la nouvelle fragment de 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) Du conteneur Activity o Fragment ne pas réinitialiser l'adaptateur avec les nouvelles données. Définissez les nouvelles données à l'aide de la méthode setPagerItems avec 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 cela vous aidera.

0 votes

Pouvez-vous m'aider à résoudre mon problème stackoverflow.com/questions/40149039/

0 votes

Pouvez-vous m'aider à résoudre ce problème ? stackoverflow.com/questions/41547995/

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