624 votes

ViewPager PagerAdapter ne pas mettre à jour la vue

Je suis à l'aide de la ViewPager à partir de la bibliothèque de compatibilité. J'ai succussfully ai eu l'affichage de plusieurs vues qui je peux par le biais de la page.

Cependant, je vais avoir un moment difficile de déterminer comment mettre à jour le ViewPager avec un nouvel ensemble de points de Vue.

J'ai essayé toutes sortes de choses comme appelant mAdapter.notifyDataSetChanged(), mViewPager.invalidate() même d'en créer une nouvelle carte à chaque fois que je veux utiliser une nouvelle Liste de données.

Rien n'a aidé, le textviews demeurent inchangées par rapport aux données d'origine.

Merci pour votre temps.

Mise à jour: J'ai fait un petit projet de test et j'ai presque été en mesure de mettre à jour les points de vue. Je vais coller la classe ci-dessous.

Ce qui ne semble pas de mise à jour, cependant, est le 2ème point de vue, le " B "reste, il doit afficher" Y " après avoir appuyé sur le bouton de mise à jour.

public class ViewPagerBugActivity extends Activity {

    private ViewPager myViewPager;
    private List<String> data;

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

        data = new ArrayList<String>();
        data.add("A");
        data.add("B");
        data.add("C");

        myViewPager = (ViewPager) findViewById(R.id.my_view_pager);
        myViewPager.setAdapter(new MyViewPagerAdapter(this, data));

        Button updateButton = (Button) findViewById(R.id.update_button);
        updateButton.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                updateViewPager();
            }
        });
    }

    private void updateViewPager() {
        data.clear();
        data.add("X");
        data.add("Y");
        data.add("Z");
        myViewPager.getAdapter().notifyDataSetChanged();
    }

    private class MyViewPagerAdapter extends PagerAdapter {

        private List<String> data;
        private Context ctx;

        public MyViewPagerAdapter(Context ctx, List<String> data) {
            this.ctx = ctx;
            this.data = data;
        }

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

        @Override
        public Object instantiateItem(View collection, int position) {
            TextView view = new TextView(ctx);
            view.setText(data.get(position));
            ((ViewPager)collection).addView(view);
            return view;
        }

        @Override
        public void destroyItem(View collection, int position, Object view) {
             ((ViewPager) collection).removeView((View) view);
        }

        @Override
        public boolean isViewFromObject(View view, Object object) {
            return view == object;
        }

        @Override
        public Parcelable saveState() {
            return null;
        }

        @Override
        public void restoreState(Parcelable arg0, ClassLoader arg1) {
        }

        @Override
        public void startUpdate(View arg0) {
        }

        @Override
        public void finishUpdate(View arg0) {
        }
    }
}

883voto

rui.araujo Points 3906

Il y a plusieurs moyens d'y parvenir.

La première option est plus facile, mais peu plus inefficace.

Remplacer getItemPosition votre PagerAdapter comme ceci:

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

De cette façon, lorsque vous appelez notifyDataSetChanged(), le point de vue de radiomessagerie va supprimer tous les points de vue et de le recharger tous. Comme si le recharger effet est obtenu.

La deuxième option, proposée par alvarolb, est d' setTag() méthode de instantiateItem() lors de l'instanciation d'un nouveau point de vue. Alors au lieu d'utiliser notifyDataSetChanged(), vous pouvez utiliser findViewWithTag() de trouver la vue que vous souhaitez mettre à jour.

La deuxième approche est très flexible et de haute performance. Bravo à alvarolb pour la recherche originale.

488voto

Je ne pense pas qu'il y a un genre de bug dans l' PagerAdapter. Le problème est que de comprendre comment il fonctionne, c'est un peu complexe. En regardant les solutions expliqué ici, il y a un malentendu, et donc à une mauvaise utilisation des instancié vues de mon point de vue.

Depuis quelques jours, j'ai pu travailler avec des PagerAdapter et ViewPager, et j'ai trouvé le suivant:

L' notifyDataSetChanged() méthode sur l' PagerAdapter n'avertira l' ViewPager que les pages ont été modifiées. Par exemple, si vous avez créés/supprimés des pages dynamiquement (en ajoutant ou en supprimant des éléments de votre liste) ViewPager devrait prendre soin de cela. Dans ce cas, je pense que l' ViewPager détermine si un nouveau point de vue devrait être supprimé ou instancié à l'aide de l' getItemPosition() et getCount() méthodes.

Je pense qu' ViewPager, après une notifyDataSetChanged() appel prend de l'enfant vues et vérifie leur position avec l' getItemPosition(). Si, pour un enfant de voir cette méthode retourne POSITION_NONE, ViewPager comprend que le point de vue a été supprimé, l'appel de la destroyItem(), et la suppression de ce point de vue.

De cette façon, en remplaçant getItemPosition() toujours y revenir POSITION_NONE est complètement faux si vous souhaitez uniquement mettre à jour le contenu des pages, parce que le point de vue sera détruit et les nouveaux seront créés chaque fois que vous appelez notifyDatasetChanged(). Il peut sembler être pas si mal pour un petit nombre TextViews, mais quand vous avez des vues complexes, comme les ListViews rempli à partir d'une base de données, cela peut être un vrai problème et un gaspillage de ressources.

Il ya donc plusieurs approches pour modifier efficacement le contenu d'un point de vue, sans avoir à supprimer et instancier les afficher de nouveau. Cela dépend du problème que vous souhaitez résoudre. Mon approche est d'utiliser l' setTag() méthode pour n'importe quel autre vue dans l' instantiateItem() méthode. Ainsi, lorsque vous voulez modifier les données ou d'invalider l'affichage que vous avez besoin, vous pouvez contacter l' findViewWithTag() méthode sur l' ViewPager pour récupérer le déjà instancié afficher et modifier/utiliser comme vous le souhaitez sans avoir à supprimer/créer un nouveau point de vue chaque fois que vous souhaitez mettre à jour une certaine valeur.

Imaginez par exemple que vous disposez de 100 pages avec 100 TextViews et vous souhaitez uniquement mettre à jour une valeur périodiquement. Avec les approches expliqué précédemment, cela signifie que vous sont en retrait et l'instanciation de 100 TextViews sur chaque mise à jour. Il ne fait pas de sens...

88voto

Kit Points 31

Changer le FragmentPagerAdapter à FragmentStatePagerAdapter.

Substituez la méthode getItemPosition() et POSITION_NONE de retour.

Finalement, il va écouter le notifyDataSetChanged() le pagineur de la vue.

47voto

Grimmace Points 1872

La réponse donnée par alvarolb est certainement la meilleure façon de le faire. Bâtiment lors de sa réponse, un moyen facile à mettre en œuvre c'est simplement pour stocker les vues actives par position:

SparseArray<View> views = new SparseArray<View>();

@Override
public Object instantiateItem(View container, int position) {
    View root = <build your view here>;
    ((ViewPager) container).addView(root);
    views.put(position, root);
    return root;
}

@Override
public void destroyItem(View collection, int position, Object o) {
    View view = (View)o;
    ((ViewPager) collection).removeView(view);
    views.remove(position);
    view = null;
}

Puis, une fois en substituant l' notifyDataSetChanged méthode, vous pouvez actualiser les points de vue...

@Override
public void notifyDataSetChanged() {
    int key = 0;
    for(int i = 0; i < views.size(); i++) {
       key = views.keyAt(i);
       View view = views.get(key);
       <refresh view with new data>
    }
    super.notifyDataSetChanged();
}

Vous pouvez en fait utiliser un code similaire dans instantiateItem et notifyDataSetChanged d'actualiser votre vue. Dans mon code j'utilise exactement la même méthode.

20voto

Tamawy Points 1447

Après des heures de frustration en essayant toutes les solutions ci-dessus afin de surmonter ce problème et tente également de nombreuses solutions sur d'autres questions similaires comme ceci, ceci et cela , qui ont tous ÉCHOUÉ avec moi à résoudre ce problème et de faire de l' ViewPager à détruire le vieux - Fragment et de remplir l' pager avec le nouveau Fragments. J'ai résolu le problème comme suit:

1) Faire de l' ViewPager classe étend FragmentPagerAdapter comme suit:

 public class myPagerAdapter extends FragmentPagerAdapter {

2) Créer un Article pour l' ViewPager qui stockent l' title et de la 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) Faire le constructeur de l' ViewPager prendre mon FragmentManager exemple le ranger 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éer une méthode de re-définir le adapter données avec les nouvelles données par la suppression de toutes les précédentes, fragment de la fragmentManager lui-même directement à faire de l' adapter pour définir le nouveau fragment de la nouvelle liste de nouveau de la manière suivante:

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 Activity ou Fragment ne pas ré-initialiser la carte avec les nouvelles données. Définir les nouvelles données par le biais de la méthode de setPagerItems avec les nouvelles données de la manière suivante:

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 aide.

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