Warum aktualisiert `PagerAdapter::notifyDataSetChanged` die Ansicht nicht?

Lesezeit: 9 Minuten

Benutzer-Avatar
C0deAttack

Ich verwende den ViewPager aus der Kompatibilitätsbibliothek. Ich habe erfolgreich mehrere Ansichten angezeigt, die ich durchblättern kann.

Es fällt mir jedoch schwer, herauszufinden, wie ich den ViewPager mit einem neuen Satz von Ansichten aktualisieren kann.

Ich habe schon alles Mögliche ausprobiert, zum Beispiel anrufen mAdapter.notifyDataSetChanged(), mViewPager.invalidate() Ich erstelle sogar jedes Mal einen brandneuen Adapter, wenn ich eine neue Datenliste verwenden möchte.

Nichts hat geholfen, die Textansichten bleiben gegenüber den Originaldaten unverändert.

Aktualisieren:
Ich habe ein kleines Testprojekt gemacht und konnte die Ansichten fast aktualisieren. Ich füge die Klasse unten ein.

Was jedoch nicht aktualisiert zu werden scheint, ist die zweite Ansicht, das „B“ bleibt, es sollte „Y“ anzeigen, nachdem die Schaltfläche „Aktualisieren“ gedrückt wurde.

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) {
        }
    }
}

  • Unter stackoverflow.com/questions/12510404/… finden Sie einen potenziellen Fehler mit dem FragmentStatePagerAdapter.

    – samus

    21. Januar 2014 um 20:12 Uhr

  • Tut mir leid, das auszugraben. Deine Frage hat mir wirklich geholfen, danke! Das einzige, was ich nicht verstehe, ist, wie der Verweis auf Daten im PagerAdapter geändert wird, wenn Sie ihn in der Aktivität aktualisieren

    – Ewanw

    13. Juli 2015 um 22:54 Uhr

  • Lassen Sie Ihren Adapter BaseAdapter erweitern. Siehe: stackoverflow.com/questions/13664155/…

    Benutzer5219444

    12. August 2015 um 13:04 Uhr

  • Anscheinend, wenn Sie den Adapter wieder auf Ihren View-Pager einstellen, dann wird er zurückgesetzt.

    – EpicPandaForce

    11. Februar 2016 um 21:27 Uhr

  • Ich habe ein Problem mit dem Pager stackoverflow.com/questions/41217237/…

    – Chris

    21. Dezember 2016 um 12:27 Uhr

Benutzer-Avatar
Alvaro Luis Bustamante

Ich glaube nicht, dass da irgendein Bug drin ist PagerAdapter. Das Problem ist, dass es etwas komplex ist, zu verstehen, wie es funktioniert. Wenn man sich die hier erläuterten Lösungen ansieht, gibt es aus meiner Sicht ein Missverständnis und daher eine schlechte Verwendung von instanziierten Ansichten.

Die letzten Tage habe ich mit gearbeitet PagerAdapter und ViewPagerund ich habe folgendes gefunden:

Das notifyDataSetChanged() Methode auf der PagerAdapter wird nur die benachrichtigen ViewPager dass sich die zugrunde liegenden Seiten geändert haben. Wenn Sie beispielsweise Seiten dynamisch erstellt/gelöscht haben (Elemente zu Ihrer Liste hinzufügen oder daraus entfernen), wird die ViewPager sollte sich darum kümmern. In diesem Fall denke ich, dass die ViewPager bestimmt, ob eine neue Ansicht gelöscht oder mithilfe von instanziiert werden soll getItemPosition() und getCount() Methoden.

ich denke, dass ViewPagernach einer notifyDataSetChanged() call nimmt seine untergeordneten Ansichten auf und überprüft deren Position mit der getItemPosition(). Wenn diese Methode für eine untergeordnete Ansicht zurückkehrt POSITION_NONEdas ViewPager versteht, dass die Ansicht gelöscht wurde, ruft die auf destroyItem()und entfernen Sie diese Ansicht.

Auf diese Weise überschreiben getItemPosition() immer wiederzukommen POSITION_NONE ist völlig falsch, wenn man nur den Inhalt der Seiten aktualisieren möchte, da bei jedem Aufruf die zuvor erstellten Ansichten zerstört und neue erstellt werden notifyDatasetChanged(). Es mag scheinen, nur für wenige nicht so falsch zu sein TextViews, aber wenn Sie komplexe Ansichten haben, wie ListViews, die aus einer Datenbank gefüllt werden, kann dies ein echtes Problem und eine Verschwendung von Ressourcen sein.

Es gibt also mehrere Ansätze, um den Inhalt einer Ansicht effizient zu ändern, ohne die Ansicht entfernen und erneut instanziieren zu müssen. Es hängt von dem Problem ab, das Sie lösen möchten. Mein Ansatz ist die Verwendung von setTag() -Methode für jede instanziierte Ansicht in der instantiateItem() Methode. Wenn Sie also die Daten ändern oder die benötigte Ansicht ungültig machen möchten, können Sie die aufrufen findViewWithTag() Methode auf der ViewPager um die zuvor instanziierte Ansicht abzurufen und sie nach Belieben zu ändern/zu verwenden, ohne jedes Mal, wenn Sie einen Wert aktualisieren möchten, eine neue Ansicht löschen/erstellen zu müssen.

Stellen Sie sich zum Beispiel vor, Sie haben 100 Seiten mit 100 TextViews und Sie nur einen Wert regelmäßig aktualisieren möchten. Mit den zuvor erläuterten Ansätzen bedeutet dies, dass Sie 100 entfernen und instanziieren TextViews bei jedem Update. Es ergibt keinen Sinn…

  • +1 – Ihre Lösung funktioniert nicht nur wie ein Zauber Ich bin genau auf das Problem gestoßen, das Sie hier beschrieben haben, als ich Seiten aktualisieren musste, die Registerkarten mit Listenansichten, Textansichten und Bildern enthielten. (OutOfErrorExceptions und so …) Danke!

    – Schlegel

    15. Dezember 2011 um 16:23 Uhr

  • @alvarolb: Hallo, ich weiß nicht wirklich, wie du das meinst setTag in Methode onInstantiateItem, Möchten Sie diese Antwort mit etwas Codierung aktualisieren? Vielen Dank.

    – Huy-Turm

    11. April 2014 um 9:50 Uhr

  • Es wäre perfekt, wenn Sie ein Beispiel schreiben würden.

    – Sami Eltamawy

    19. Mai 2015 um 11:31 Uhr

  • Wie jemand sagt: Ein Beispiel wäre willkommen!

    – Jey10

    21. Januar 2016 um 14:25 Uhr

  • 6 Jahre jetzt, niemand kann ein Beispiel liefern.

    – Oussaki

    21. August 2017 um 18:18 Uhr

  • Ich denke, Sie sollten die gesamte getItemPosition richtig implementieren UND POSITION_NONE zurückgeben, wenn das Fragment nicht gefunden wird.

    – tkhduracell

    17. September 2018 um 16:43 Uhr

  • Du hast mich gerettet. Vielen Dank . Für mich bist du Gott. Ich kann meine Tränen nicht aufhalten

    – sajjad Josefi

    3. Juli 2020 um 12:30 Uhr

  • Das hat mich davor bewahrt, zu viel Theorie zu lesen, für eine faule Person wie mich funktioniert das gut

    – Harscha

    14. April 2021 um 18:58 Uhr

  • FragmentStatePagerAdapter veraltet.

    – Prantik Mondal

    27. September 2021 um 11:37 Uhr

Benutzer-Avatar
Grimasse

Die Antwort von alvarolb ist definitiv der beste Weg, dies zu tun. Aufbauend auf seiner Antwort besteht eine einfache Möglichkeit, dies zu implementieren, darin, die aktiven Ansichten einfach nach Position zu speichern:

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

Dann einmal durch Überschreiben der notifyDataSetChanged Methode können Sie die Ansichten aktualisieren …

@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();
}

Sie können tatsächlich ähnlichen Code in verwenden instantiateItem und notifyDataSetChanged um Ihre Ansicht aufzufrischen. In meinem Code verwende ich genau die gleiche Methode.

  • Können Sie ein vollständiges Adapterbeispiel bereitstellen? wird dies mit Fragmenten gemacht oder nicht?

    Benutzer1545072

    30. Januar 2013 um 16:22 Uhr

  • ?? Was soll das heißen, entweder müssen wir ihn hier hinzufügen oder nichts?

    – Akarsch M

    22. Juli 2013 um 15:39 Uhr

  • Diese Methode aktualisiert alle Ansichten. Wenn Sie nur eine Ansicht aktualisieren möchten, können Sie Ihre eigene Methode “notifyDataItemChanged” wie folgt schreiben: public void NotifyDataItemChanged(int pos) { TextView tv = (TextView) views.get(pos).findViewById(R.id.tv); tv.setText (list.get (pos)); super.notifyDataSetChanged(); }

    – Kai Wang

    8. März 2016 um 20:57 Uhr


  • Dieser Code stürzt ab, wenn die neue Pagergröße kleiner als die alte Größe ist

    – Anton Duzenko

    24. März 2016 um 15:41 Uhr

  • @AntonDuzenko richtig, das Löschen von Ansichten ist ziemlich schwierig.

    – MLProgrammer-CiM

    5. Juli 2016 um 18:34 Uhr

Benutzer-Avatar
Tonys

Hatte das gleiche Problem. Für mich hat es funktioniert, FragmentStatePagerAdapter zu erweitern und die folgenden Methoden zu überschreiben:

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

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

}

  • Können Sie ein vollständiges Adapterbeispiel bereitstellen? wird dies mit Fragmenten gemacht oder nicht?

    Benutzer1545072

    30. Januar 2013 um 16:22 Uhr

  • ?? Was soll das heißen, entweder müssen wir ihn hier hinzufügen oder nichts?

    – Akarsch M

    22. Juli 2013 um 15:39 Uhr

  • Diese Methode aktualisiert alle Ansichten. Wenn Sie nur eine Ansicht aktualisieren möchten, können Sie Ihre eigene Methode “notifyDataItemChanged” wie folgt schreiben: public void NotifyDataItemChanged(int pos) { TextView tv = (TextView) views.get(pos).findViewById(R.id.tv); tv.setText (list.get (pos)); super.notifyDataSetChanged(); }

    – Kai Wang

    8. März 2016 um 20:57 Uhr


  • Dieser Code stürzt ab, wenn die neue Pagergröße kleiner als die alte Größe ist

    – Anton Duzenko

    24. März 2016 um 15:41 Uhr

  • @AntonDuzenko richtig, das Löschen von Ansichten ist ziemlich schwierig.

    – MLProgrammer-CiM

    5. Juli 2016 um 18:34 Uhr

Benutzer-Avatar
Gemeinschaft

Nach Stunden der Frustration beim Ausprobieren aller oben genannten Lösungen zur Überwindung dieses Problems und auch beim Ausprobieren vieler Lösungen zu anderen ähnlichen Fragen wie dieser, dieser und dieser, die alle bei mir fehlschlugen, dieses Problem zu lösen und das zu machen ViewPager das Alte zu zerstören Fragment und fülle die pager mit dem neuen Fragments. Ich habe das Problem wie folgt gelöst:

1) Mach das ViewPager Klasse zu erweitern FragmentPagerAdapter wie folgt:

 public class myPagerAdapter extends FragmentPagerAdapter {

2) Erstellen Sie ein Element für die ViewPager die speichern title und der fragment wie folgt:

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) Machen Sie den Konstruktor der ViewPager Nimm mein FragmentManager Beispiel, um es in my zu speichern class wie folgt:

private FragmentManager mFragmentManager;
private ArrayList<PagerItem> mPagerItems;

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

4) Erstellen Sie eine Methode zum Zurücksetzen der adapter Daten mit den neuen Daten durch Löschen aller vorherigen fragment von dem fragmentManager selbst direkt zu machen adapter das neue einzustellen fragment aus der neuen Liste wieder wie folgt:

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) Aus dem Behälter Activity oder Fragment initialisieren Sie den Adapter nicht mit den neuen Daten neu. Stellen Sie die neuen Daten durch die Methode ein setPagerItems mit den neuen Daten wie folgt:

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();

Ich hoffe, es hilft.

  • @ Tomer, was war das Problem, mit dem Sie konfrontiert waren?

    – Sami Eltamawy

    12. Februar 2018 um 13:50 Uhr

  • stackoverflow.com/questions/48747734/…

    – Tomer

    13. Februar 2018 um 5:41 Uhr

1015130cookie-checkWarum aktualisiert `PagerAdapter::notifyDataSetChanged` die Ansicht nicht?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy