web-dev-qa-db-fra.com

ViewPager ne redessine pas le contenu, reste/devient vide

Nous souffrons d'un problème très étrange avec ViewPager ici. Nous intégrons des listes sur chaque page ViewPager et déclenchons notifyDataSetChanged à la fois sur l'adaptateur de liste et sur l'adaptateur de pager View lors de la mise à jour des données de liste.

Nous observons que parfois, la page ne met pas à jour son arborescence de vues, c’est-à-dire qu'elle reste vide ou même disparaît lors de la pagination. Lorsque vous effectuez une pagination plusieurs fois, le contenu réapparaît soudainement. Il semble que Android manque une vue mise à jour ici. J'ai également remarqué que lors du débogage avec le visualiseur de hiérarchie, le choix d'une vue le fera toujours réapparaître, apparemment parce que le visualiseur de hiérarchie force la vue sélectionnée à se redessiner.

Je ne pouvais pas faire ce travail par programme cependant; l'invalidation de la vue liste, ou même de la vue entière, n'a eu aucun effet.

Ceci est avec la bibliothèque de compatibilité-v4_r7. J'ai également essayé d'utiliser la dernière version, car elle prétend résoudre de nombreux problèmes liés au visualiseur de vues, mais cela rendait la situation encore pire (par exemple, les gestes étaient brisés de sorte que cela ne me laissait plus parfois feuilleter toutes les pages.)

Est-ce que quelqu'un d'autre est également confronté à ces problèmes ou avez-vous une idée de ce qui pourrait provoquer cela?

83
Matthias

Nous avons finalement réussi à trouver une solution. Apparemment, notre implémentation souffrait de deux problèmes:

  1. notre adaptateur n'a pas supprimé la vue dans destroyItem().
  2. nous mettions en cache des vues de sorte que nous devions gonfler notre présentation une seule fois et, comme nous ne supprimions pas la vue dans destroyItem(), nous ne l’ajoutions pas dans instantiateItem() mais nous ne renvoyions que la vue en cache correspondant à la position actuelle.

Je n'ai pas trop étudié le code source de la ViewPager - et ce n'est pas tout à fait explicite que vous deviez le faire - mais la documentation dit:

destroyItem ()
Supprimer une page pour la position donnée. L'adaptateur est responsable de la suppression de la vue de son conteneur, même s'il doit uniquement s'assurer que cela est fait au moment où il retourne de finishUpdate (ViewGroup).

et: 

Un très simple PagerAdapter peut choisir d’utiliser la page elle-même en tant qu’objets clés, de les renvoyer à partir de instantiateItem (ViewGroup, int) après leur création et de les ajouter au ViewGroup parent. Une implémentation correspondante de destroyItem (ViewGroup, int, Object) supprimerait la vue de ViewGroup parent et isViewFromObject (View, Object) pourrait être implémenté en tant que return view == object ;.

Ma conclusion est donc que ViewPager s'appuie sur son adaptateur sous-jacent pour ajouter/supprimer explicitement ses enfants dans instantiateItem()/destroyItem(). Autrement dit, si votre adaptateur est une sous-classe de PagerAdapter, votre sous-classe doit implémenter cette logique.

Note latérale: soyez conscient de ceci si vous utilisez des listes dans ViewPager.

44
futtetennista

Si la ViewPager est définie dans un fragment avec une FragmentPagerAdapter, utilisez getChildFragmentManager() au lieu de getSupportFragmentManager() comme paramètre pour initialiser votre FragmentPagerAdapter.

mAdapter = new MyFragmentPagerAdapter(getChildFragmentManager());

Au lieu de

mAdapter = new MyFragmentPagerAdapter(getSupportFragmentManager());
50
eyeslave

J'ai eu exactement le même problème mais j'ai en fait détruit la vue dans destroyItem (je pensais). Le problème était cependant que je l'ai détruit en utilisant viewPager.removeViewAt(index); au lieu de viewPager.removeView((View) object);

Faux:

@Override
public void destroyItem(ViewGroup viewPager, int position, Object object) {
    viewPager.removeViewAt(position);
}

Droite:

@Override
public void destroyItem(ViewGroup viewPager, int position, Object object) {
    viewPager.removeView((View) object);
}
21
Ringen

ViewPager essaie de faire des choses intelligentes pour réutiliser des éléments, mais il vous oblige à renvoyer de nouvelles positions lorsque les choses ont changé. Essayez d’ajouter ceci à votre PagerAdapter:

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

Il indique en gros à ViewPager que tout a changé (et le force à tout ré-instancier). C’est la seule chose à laquelle je peux penser spontanément.

8
Chris Banes

J'ai rencontré ce même problème lors de l'utilisation d'un ViewPager et d'un FragmentStatePagerAdapter. J'ai essayé d'utiliser un gestionnaire avec un délai de 3 secondes pour appeler invalidate () et requestLayout () mais cela n'a pas fonctionné. Ce qui a bien fonctionné a été de réinitialiser la couleur d'arrière-plan de viewPager comme suit:

MyFragment.Java

    private Handler mHandler;
    private Runnable mBugUpdater;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        View rootView = new ViewPager(getActivity());
        //...Create your adapter and set it here...

        mHandler = new Handler();
        mBugUpdater = new Runnable(){
            @Override
            public void run() {
                mVp.setBackgroundColor(mItem.getBackgroundColor());
                mHandler = null;
                mBugUpdater = null;
            }           
        };
        mHandler.postDelayed(mBugUpdater,50);

        return rootView;
    }

    @Override
    public void onPause() {
        if(mHandler != null){
            //Remove the callback if it hasn't triggered yet
            mHandler.removeCallbacks(mBugUpdater);
            mHandler = null;
            mBugUpdater = null;
        }
        super.onPause();
     }
0
Chris Sprague

Je suis tombé dessus et j'avais des problèmes très similaires. J'ai même demandé sur débordement de pile. 

Pour moi, dans le parent du parent de mon point de vue, quelqu'un a sous-classé LinearLayout et a surchargé requestLayout() sans appeler super.requestLayout(). Cela a empêché l'appel de onMeasure et onLayout sur mon ViewPager (bien que hierarchyviewer les appelle manuellement). Sans être mesurés, ils apparaîtront comme vides dans ViewPager. 

Alors vérifiez vos vues contenant. Assurez-vous qu'ils sous-classent dans View et qu'ils ne remplacent pas aveuglément requestLayout ou quelque chose de similaire.

0
Tim O'Brien

J'ai eu un problème avec les mêmes symptômes, mais une cause différente qui s'est avérée être une erreur stupide de ma part. Je pensais l'ajouter ici au cas où cela pourrait aider quelqu'un.

J'avais un ViewPager utilisant FragmentStatePagerAdapter qui comportait auparavant deux fragments, mais j'en ai ensuite ajouté un troisième. Cependant, j’ai oublié que la limite par défaut des pages hors écran est de 1 - aussi, lorsque je passerais au nouveau troisième fragment, le premier serait détruit, puis recréé après le retour. Le problème était que mon activité était chargée de notifier ces fragments pour initialiser leur état d'interface utilisateur. Cela fonctionnait lorsque les cycles de vie d'activité et de fragment étaient identiques, mais pour le corriger, je devais modifier les fragments afin d'initialiser leur propre interface utilisateur au cours de leur cycle de vie de démarrage. En fin de compte, j'ai également fini par changer setOffscreenPageLimit à 2 afin que les trois fragments soient maintenus en vie à tout moment (sans danger dans ce cas car ils ne demandaient pas beaucoup de mémoire).

0
dfinn

J'ai essayé trop de solutions, mais de manière inattendue viewPager.post() a fonctionné

 mAdapter = new NewsVPAdapter(getContext(), articles);
    viewPager.post(new Runnable() {
        @Override
        public void run() {
            viewPager.setAdapter(mAdapter);
        }
    });
0
Zeero0

La bibliothèque de support Android a une activité de démonstration qui inclut un ViewPager avec un ListView sur chaque page. Vous devriez probablement jeter un coup d'œil et voir ce qu'il fait.

Dans Eclipse (avec Android Dev Tools r20):

  1. Sélectionnez New > Android Sample Project
  2. Sélectionnez votre niveau d'API cible (je suggère le plus récent disponible)
  3. Sélectionnez Support4Demos
  4. Cliquez avec le bouton droit sur le projet et sélectionnez Android Tools > Add Support Library
  5. Exécutez l'application et sélectionnez Fragment puis Pager

Le code pour cela est dans src/com.example.Android.supportv4.app/FragmentPagerSupport.Java. Bonne chance!

0
Sparky

Avait le même problème, qui est quelque chose à voir avec ListView (parce que ma vue vide montre bien si la liste est vide). Je viens d'appeler requestLayout() sur la _ListView problématique. Maintenant ça tire bien!

0
Oleg Vaskevich