ViewPager zeichnet keine Inhalte neu, bleibt / bleibt leer

86

Wir haben hier ein sehr seltsames Problem mit ViewPager. Wir binden Listen auf jeder ViewPager-Seite ein und lösen beim Aktualisieren von Listendaten notifyDataSetChanged sowohl auf dem Listenadapter als auch auf dem View Pager-Adapter aus.

Was wir beobachten, ist, dass die Seite manchmal ihren Ansichtsbaum nicht aktualisiert, dh leer bleibt oder manchmal sogar verschwindet, wenn sie darauf blättert. Wenn Sie ein paar Mal hin und her blättern, wird der Inhalt plötzlich wieder angezeigt. Es scheint, als ob Android hier ein Ansichtsupdate fehlt. Mir ist auch aufgefallen, dass beim Debuggen mit dem Hierarchie-Viewer die Auswahl einer Ansicht immer wieder angezeigt wird, anscheinend weil der Hierarchie-Viewer die ausgewählte Ansicht zwingt, sich selbst neu zu zeichnen.

Ich konnte diese Arbeit jedoch nicht programmgesteuert ausführen. Das Ungültigmachen der Listenansicht oder sogar des gesamten Ansichtspagers hatte keine Auswirkung.

Dies ist mit der kompatiblen v4_r7-Bibliothek der Fall. Ich habe auch versucht, die neueste Version zu verwenden, da sie behauptet, viele Probleme im Zusammenhang mit dem Anzeigen von Pager zu beheben, aber es hat die Sache noch schlimmer gemacht (zum Beispiel wurden Gesten gebrochen, so dass ich manchmal nicht mehr alle Seiten durchblättern konnte).

Stößt auch jemand anderes auf diese Probleme oder haben Sie eine Vorstellung davon, was dies verursachen könnte?

Matthias
quelle

Antworten:

44

Wir haben es endlich geschafft, eine Lösung zu finden. Anscheinend hatte unsere Implementierung zwei Probleme:

  1. Unser Adapter hat die Ansicht in nicht entfernt destroyItem().
  2. Wir haben Ansichten zwischengespeichert, sodass wir unser Layout nur einmal aufblasen mussten, und da wir die Ansicht nicht entfernt haben destroyItem(), haben wir sie nicht hinzugefügt, instantiateItem()sondern nur die zwischengespeicherte Ansicht zurückgegeben, die der aktuellen Position entspricht.

Ich habe nicht zu tief in den Quellcode von geschaut ViewPager- und es ist nicht genau explizit, dass Sie das tun müssen -, aber die Dokumentation sagt:

destroyItem ()
Entferne eine Seite für die angegebene Position. Der Adapter ist dafür verantwortlich, die Ansicht aus seinem Container zu entfernen, muss jedoch nur sicherstellen, dass dies zum Zeitpunkt der Rückkehr von finishUpdate (ViewGroup) erfolgt.

und:

Ein sehr einfacher PagerAdapter verwendet möglicherweise die Seitenansichten selbst als Schlüsselobjekte, gibt sie nach der Erstellung von instantiateItem (ViewGroup, int) zurück und fügt sie der übergeordneten ViewGroup hinzu. Eine übereinstimmende Implementierung von destroyItem (ViewGroup, int, Object) würde die Ansicht aus der übergeordneten ViewGroup entfernen, und isViewFromObject (View, Object) könnte als return view == object; implementiert werden.

Mein Fazit ist also, dass ViewPagerder zugrunde liegende Adapter zum expliziten Hinzufügen / Entfernen seiner untergeordneten Elemente in instantiateItem()/ verwendet wird destroyItem(). Das heißt, wenn Ihr Adapter eine Unterklasse von ist PagerAdapter, muss Ihre Unterklasse diese Logik implementieren.

Randnotiz: Beachten Sie dies, wenn Sie darin Listen verwenden ViewPager.

futtetennista
quelle
2
Ich habe das gleiche Problem, aber mit FragmentPagerAdapter, der onDestroy für mich behandelt. Auch hier funktioniert keine der anderen Lösungen.
Greg Ennis
14
Was auch das Problem sein könnte, ist, dass jemand getFragmentManger anstelle von GetChildFragmentManager
Boy
@Boy was ist GetChildFragmentManager?
Feuer im Loch
1
@ Boy Wooooow .... Ich hatte mir zwei Tage lang die Haare ausgezogen, um herauszufinden, warum ich nichts zum Aktualisieren bekommen konnte. DANKE! (Sie sollten wirklich, wirklich einen Hinweis in die Google-Dokumentation einfügen, um den childfragmentmanager zu verwenden. Das bedeutet nicht, dass Sie einen anderen Manager benötigen.)
user0721090601
@futtetennista Ich mache dasselbe .. vor diesem Problem .. können Sie überprüfen .. stackoverflow.com/questions/61727835/…
AskQ
55

Wenn das ViewPagerin einem Fragment mit a festgelegt ist FragmentPagerAdapter, verwenden Sie getChildFragmentManager()anstelle von getSupportFragmentManager()als Parameter, um Ihr zu initialisieren FragmentPagerAdapter.

mAdapter = new MyFragmentPagerAdapter(getChildFragmentManager());

Anstatt

mAdapter = new MyFragmentPagerAdapter(getSupportFragmentManager());
Augensklave
quelle
2
Guter Fang, es scheint mein Problem zu lösen, wenn ein FragmentPagerAdapter
Tobliug
1
Danke - das hat funktioniert. Ich habe versucht zu zwingen , getItem()auf ein , FragmentPagerAdapterdie in einem neu Fragment verschachtelt wurde.
Kosiara - Bartosz Kosarzycki
wow diese Arbeit wie Charme. Ich denke, das Problem ist auf das Aufblasen des Viewpagers in einem Fragment zurückzuführen
Thecarisma
Es sind Zeiten wie diese. Ich wünschte, wir hätten Klatschen wie Mittel, damit ich Ihnen mehr als 1000 Klatschen dafür geben könnte.
Gute
21

Ich hatte genau das gleiche Problem, aber ich habe die Ansicht in destroyItem tatsächlich zerstört (dachte ich). Das Problem war jedoch, dass ich es mit viewPager.removeViewAt(index);insted of zerstört habeviewPager.removeView((View) object);

Falsch:

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

Richtig:

@Override
public void destroyItem(ViewGroup viewPager, int position, Object object) {
    viewPager.removeView((View) object);
}
Ringen
quelle
Danke für den Tipp. Ich habe mich schon seit einiger Zeit mit dem Thema befasst.
Moritz
2
Das hat bei mir funktioniert, aber wissen Sie, warum der erste ein Problem ist?
HannahMitt
@HannahMitt removeViewAt entfernt jede Ansicht, die sich an dieser bestimmten Position in der aktuellen Liste der untergeordneten Elemente des ViewPager befindet, und Seiten können dem ViewPager in beliebiger Reihenfolge hinzugefügt werden (Seite 0 befindet sich also möglicherweise im ViewPager bei Index 1 oder was auch immer). removeView durchläuft die Liste der untergeordneten Objekte und entfernt genau das angegebene Objekt.
2
Funktioniert nicht für mich: Kann die Ansicht nicht auf Fragment umwandeln. Objekt hier ist ein Fragment.
FRK
Viewpager muss statisch sein?
Kanagalingam
10

ViewPager versucht, clevere Dinge zu tun, um Gegenstände wiederzuverwenden, aber Sie müssen neue Gegenstandspositionen zurückgeben, wenn sich die Dinge geändert haben. Versuchen Sie, dies Ihrem PagerAdapter hinzuzufügen:

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

Im Grunde sagt es ViewPager, dass sich alles geändert hat (und zwingt es, alles neu zu instanziieren). Das ist das Einzige, woran ich auf Anhieb denken kann.

Chris Banes
quelle
1
Ja, ich habe in diesem Thread über diese Option gelesen: stackoverflow.com/questions/7263291/… - dies scheint jedoch der Vorschlaghammer-Ansatz zu sein. Sicherlich muss es einen eleganteren Weg geben? Ich frage mich, ob es mit der Verwendung von Listen als Pager-Seiten zusammenhängt.
Matthias
Es ist ein bisschen ein Vorschlaghammer-Ansatz, aber Sie können davon zurückarbeiten. dh einen korrekten Wert von der Methode zurückgeben.
Chris Banes
@ChrisBanes Wie Sie sagten, anrufen return POSITION_NONE;, forces it to re-instantiate everythingaber in meinem Fall möchte ich nicht alles neu instanziieren. Wäre es also möglich, es zu entfernen, viewohne das vorhandene Material zu löschen ? Bitte lassen Sie es mich wissen
Ritesh Adulkar
Sie sind der Lebensgenuss
mask8
2

Versuchte zu viele Lösungen, viewPager.post()funktionierte aber unerwartet

 mAdapter = new NewsVPAdapter(getContext(), articles);
    viewPager.post(new Runnable() {
        @Override
        public void run() {
            viewPager.setAdapter(mAdapter);
        }
    });
Zeero0
quelle
1
mein Gott. warum hat das niemand positiv bewertet? Ich habe ein seltsames Verhalten, wenn ich ein Fragment erneut eingebe und der Viewpager darin sich nicht selbst rendert. Wenn ich es ein bisschen verzögere, ist das Problem tatsächlich gelöst!
Fugogugo
0

Die Android Support Library verfügt über eine Demo-Aktivität, die auf jeder Seite einen ViewPager mit einer ListView enthält. Sie sollten wahrscheinlich einen Blick darauf werfen und sehen, was es tut.

In Eclipse (mit Android Dev Tools r20):

  1. Wählen New > Android Sample Project
  2. Wählen Sie Ihre Ziel-API-Ebene (ich schlage die neueste verfügbare vor)
  3. Wählen Support4Demos
  4. Klicken Sie mit der rechten Maustaste auf das Projekt und wählen Sie Android Tools > Add Support Library
  5. Führen Sie die App aus und wählen Sie Fragmentund dannPager

Der Code dafür ist in src/com.example.android.supportv4.app/FragmentPagerSupport.java. Viel Glück!

Sparky
quelle
danke - das schaue ich mir an! Vielleicht sehe ich etwas, was wir falsch machen.
Matthias
0

Ich bin darauf gestoßen und hatte sehr ähnliche Probleme. Ich habe es sogar beim Stapelüberlauf gefragt .

Für mich hat im Elternteil des Elternteils meiner Ansicht jemand LinearLayoutuntergeordnet und überschrieben, requestLayout()ohne anzurufen super.requestLayout(). Dies verhindert onMeasureund onLayoutaus auf meinem ViewPager genannt wird (obwohl hierarchyviewer diese manuell aufruft). Ohne gemessen zu werden, werden sie in ViewPager als leer angezeigt.

Überprüfen Sie also Ihre enthaltenen Ansichten. Stellen Sie sicher, dass sie in View eine Unterklasse haben und requestLayout oder ähnliches nicht blind überschreiben.

Tim O'Brien
quelle
0

Hatte das gleiche Problem, was etwas damit zu tun hat ListView(weil meine leere Ansicht gut angezeigt wird, wenn die Liste leer ist). Ich habe gerade requestLayout()das Problem angesprochen ListView. Jetzt zieht es gut!

Oleg Vaskevich
quelle
0

Bei der Verwendung von ViewPager und FragmentStatePagerAdapter trat dasselbe Problem auf. Ich habe versucht, mit einem Handler mit einer Verzögerung von 3 Sekunden invalidate () und requestLayout () aufzurufen, aber es hat nicht funktioniert. Was funktioniert hat, war das Zurücksetzen der Hintergrundfarbe des viewPager wie folgt:

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();
     }
Chris Sprague
quelle
0

Ich hatte ein Problem mit den gleichen Symptomen, aber eine andere Ursache, die sich als dummer Fehler herausstellte. Ich dachte, ich würde es hier hinzufügen, falls es jemandem hilft.

Ich hatte einen ViewPager mit FragmentStatePagerAdapter, der früher zwei Fragmente hatte, aber später fügte ich ein drittes hinzu. Ich habe jedoch vergessen, dass das Standardlimit für Seiten außerhalb des Bildschirms 1 ist. Wenn ich also zum neuen dritten Fragment wechsle, wird das erste zerstört und nach dem Zurückschalten neu erstellt. Das Problem war, dass meine Aktivität dafür verantwortlich war, diese Fragmente zu benachrichtigen, um ihren UI-Status zu initialisieren. Dies funktionierte zufällig, wenn die Aktivität und der Fragmentlebenszyklus gleich waren, aber um dies zu beheben, musste ich die Fragmente ändern, um ihre eigene Benutzeroberfläche während ihres Startlebenszyklus zu initialisieren. Am Ende habe ich auch setOffscreenPageLimit auf 2 geändert, damit alle drei Fragmente jederzeit am Leben bleiben (in diesem Fall sicher, da sie nicht sehr speicherintensiv waren).

dfinn
quelle
-1

Ich hatte ein ähnliches Problem. Ich speichere Ansichten zwischen, da ich nur 3 Ansichten benötige ViewPager. Wenn ich vorwärts rutsche, ist alles in Ordnung, aber wenn ich anfange rückwärts zu rutschen, tritt ein Fehler auf, der besagt, dass "meine Ansicht bereits ein übergeordnetes Element hat". Die Lösung besteht darin, nicht benötigte Elemente manuell zu löschen.

@Override
    public Object instantiateItem(ViewGroup container, int position) {
        int localPos = position % SIZE;
        TouchImageView view;
        if (touchImageViews[localPos] != null) {
            view = touchImageViews[localPos];
        } else {
            view = new TouchImageView(container.getContext());
            view.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
            touchImageViews[localPos] = view;
        }
        view.setImageDrawable(mDataModel.getPhoto(position));
        Log.i(IRViewPagerAdpt.class.toString(), "Add view " + view.toString() + " at pos: " + position + " " + localPos);
        if (view.getParent() == null) {
        ((ViewPager) container).addView(view);
    }
        return view;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object view) {
        //      ((ViewPager) container).removeView((View) view);
        Log.i(IRViewPagerAdpt.class.toString(), "remove view " + view.toString() + " at pos: " + position);
    }

..................

private static final int SIZE = 3;
private TouchImageView[] touchImageViews = new TouchImageView[SIZE];
Roman Nazarevych
quelle
-1

Für mich kam das Problem auf die Aktivität zurück, nachdem der App-Prozess beendet wurde. Ich verwende einen benutzerdefinierten View-Pager-Adapter, der von den Android-Quellen geändert wurde. Der View-Pager ist direkt in die Aktivität eingebettet.

Berufung viewPager.setCurrentItem(position, true);

(mit Animation) nach dem Setzen der Daten und notifyDataSetChanged () scheint zu funktionieren, aber wenn der Parameter auf false gesetzt ist, funktioniert dies nicht und das Fragment ist leer. Dies ist ein Randfall, der jemandem helfen kann.

Gemeiner Mann
quelle