Warum aktualisiert `PagerAdapter::notifyDataSetChanged` die Ansicht nicht?
Lesezeit: 9 Minuten
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
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
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
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
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:
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
10151300cookie-checkWarum aktualisiert `PagerAdapter::notifyDataSetChanged` die Ansicht nicht?yes
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