ViewPager PagerAdapter aktualisiert die Ansicht nicht

597

Ich verwende den ViewPager aus der Kompatibilitätsbibliothek. Ich habe es erfolgreich dazu gebracht, mehrere Ansichten anzuzeigen, durch die ich blättern kann.

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

Ich habe alle möglichen Dinge versucht , wie der Aufruf mAdapter.notifyDataSetChanged(), mViewPager.invalidate()auch eine brandneue Adapter jedes Mal zu schaffen ich eine neue Liste der Daten verwendet werden soll.

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

Update: Ich habe ein kleines Testprojekt erstellt und konnte die Ansichten fast aktualisieren. Ich werde die Klasse unten einfügen.

Was jedoch nicht zu aktualisieren scheint, ist die 2. Ansicht. Das 'B' bleibt erhalten. Nach dem Drücken der Update-Taste sollte 'Y' angezeigt werden.

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) {
        }
    }
}
C0deAttack
quelle
1
Unter stackoverflow.com/questions/12510404/… finden Sie einen möglichen Fehler mit dem FragmentStatePagerAdapter.
Samis
2
Tut mir leid, das auszubaggern. Ihre Frage hat mir wirklich geholfen, also danke! Das einzige, was ich nicht verstehe, ist, wie sich der Verweis auf Daten im PagerAdapter ändert, wenn Sie ihn in der Aktivität aktualisieren
Ewanw
Erweitern Sie Ihren Adapter. BaseAdapter Siehe dies: stackoverflow.com/questions/13664155/…
1
Wenn Sie den Adapter erneut auf Ihren Ansichtspager einstellen, wird er anscheinend zurückgesetzt.
EpicPandaForce
Ich habe Problem in Pager stackoverflow.com/questions/41217237/...
chris

Antworten:

856

Es gibt verschiedene Möglichkeiten, dies zu erreichen.

Die erste Option ist einfacher, aber etwas ineffizienter.

Überschreiben Sie getItemPositionin Ihrem PagerAdapterwie folgt :

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

Auf diese Weise entfernt notifyDataSetChanged()der Ansichtspager beim Aufrufen alle Ansichten und lädt sie alle neu. Dadurch wird der Nachladeeffekt erhalten.

Die zweite Option, die von Alvaro Luis Bustamante (zuvor alvarolb) vorgeschlagen wurde , besteht darin setTag(), instantiateItem()beim Instanziieren einer neuen Ansicht eine Methode zu verwenden . Anstatt zu verwenden notifyDataSetChanged(), können Sie dann die findViewWithTag()Ansicht suchen, die Sie aktualisieren möchten.

Der zweite Ansatz ist sehr flexibel und leistungsstark. Ein großes Lob an Alvarolb für die ursprüngliche Forschung.

rui.araujo
quelle
4
Besser als meine Arbeit, scheint keine Nebenwirkungen zu haben, danke.
C0deAttack
3
Ich habe ein ähnliches Problem. Mein viewPager zeigt die neuen Suchergebnisse an, kehrt jedoch nicht standardmäßig zum ersten Eintrag im Cursor zurück. Weiß jemand, wie man die Position beim Aktualisieren des Datensatzes zurücksetzt?
mAndroid
1
@mAndroid Sie sollten eine separate Frage mit mehr Details einreichen.
Rui.araujo
1
Die Rückgabe von POSITION_NONE führt anscheinend zu einer erheblichen Ineffizienz der gesamten Anwendung, da Android dadurch die mit der abgefragten Position verknüpfte Ansicht löscht. Die Ansicht muss dann vor der Anzeige erneut erstellt werden. Behebt das Problem, dass Ansichten nicht aktualisiert werden, aber wahrscheinlich nicht die beste Lösung für komplexe Layouts.
Wufoo
11
Bessere Antwort (ohne alle Elemente zu entfernen) -> stackoverflow.com/a/10852046/898056
yhpark
484

Ich glaube nicht, dass es irgendeinen Fehler in der gibt PagerAdapter. Das Problem ist, dass das Verständnis, wie es funktioniert, etwas komplex ist. Wenn ich mir die hier erläuterten Lösungen anschaue, gibt es aus meiner Sicht ein Missverständnis und daher eine schlechte Verwendung von instanziierten Ansichten.

In den letzten Tagen habe ich mit PagerAdapterund gearbeitet ViewPagerund Folgendes gefunden:

Die notifyDataSetChanged()Methode auf dem PagerAdapterwird nur benachrichtigen, ViewPagerdass sich die zugrunde liegenden Seiten geändert haben. Wenn Sie beispielsweise Seiten dynamisch erstellt / gelöscht haben (Hinzufügen oder Entfernen von Elementen zu Ihrer Liste), ViewPagersollte dies erledigt werden. In diesem Fall denke ich, dass das ViewPagerbestimmt, ob eine neue Ansicht mit den Methoden getItemPosition()und gelöscht oder instanziiert werden soll getCount().

Ich denke, dass ViewPagernach einem notifyDataSetChanged()Anruf die untergeordneten Ansichten aufgenommen und ihre Position mit dem überprüft werden getItemPosition(). Wenn diese Methode für eine untergeordnete Ansicht zurückgegeben wird POSITION_NONE, ViewPagerversteht das , dass die Ansicht gelöscht wurde, ruft die auf destroyItem()und entfernt diese Ansicht.

Auf diese Weise ist das Überschreiben getItemPosition(), immer zurückzukehren, POSITION_NONEvöllig falsch, wenn Sie nur den Inhalt der Seiten aktualisieren möchten, da die zuvor erstellten Ansichten zerstört werden und bei jedem Aufruf neue erstellt werden notifyDatasetChanged(). Es scheint nicht nur für ein paar TextViewSekunden so falsch zu sein , aber wenn Sie komplexe Ansichten haben, wie ListViews, die aus einer Datenbank ausgefüllt werden, kann dies ein echtes Problem und eine Verschwendung von Ressourcen sein.

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

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

Alvaro Luis Bustamante
quelle
11
+1 - Ihre Lösung funktioniert nicht nur wie ein Zauber, den ich in genau dem Problem ausgeführt habe, das Sie hier beschrieben haben, als ich Seiten aktualisieren musste, die Registerkarten mit Listenansichten, Textansichten und Bildern enthielten. (OutOfErrorExceptions und so ...) Danke!
Schlingel
3
@alvarolb: Hallo, ich weiß nicht wirklich, wie du es mit der setTagMethode meinst. Möchtest onInstantiateItemdu diese Antwort mit etwas Codierung aktualisieren? Vielen Dank.
Huy Tower
35
Es wäre perfekt, wenn Sie ein Beispiel schreiben würden.
Sami Eltamawy
13
Wie jemand sagt: Ein Beispiel wird geschätzt!
Jey10
15
Seit 6 Jahren kann niemand mehr ein Beispiel geben.
Oussaki
80

Ändern Sie das FragmentPagerAdapterinFragmentStatePagerAdapter .

getItemPosition()Methode überschreiben und zurückgebenPOSITION_NONE .

Schließlich wird der notifyDataSetChanged()On-View-Pager abgehört.

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

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

Wenn notifyDataSetChangedSie dann einmal die Methode überschreiben , 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 ähnlichen Code in instantiateItemund verwenden notifyDataSetChanged, um Ihre Ansicht zu aktualisieren. In meinem Code verwende ich genau die gleiche Methode.

Grimmace
quelle
8
Können Sie ein vollständiges Adapterbeispiel bereitstellen? Wird dies mit Fragmenten gemacht oder nicht?
9
<Ansicht mit neuen Daten aktualisieren> ?? Was ist der Mittelwert davon, entweder müssen wir hinzufügen, dass er hier sieht oder nichts?
Akarsh M
Diese Methode aktualisiert alle Ansichten. Wenn Sie nur eine Ansicht aktualisieren möchten, können Sie Ihre eigene notifyDataItemChanged-Methode 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
Dieser Code stürzt ab, wenn die neue
Pagergröße
@AntonDuzenko richtig, das Löschen von Ansichten ist ziemlich schwierig.
MLProgrammer-CiM
28

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

}
Mario Mosca
quelle
Dies ist die einzige Lösung, die auch für mich funktioniert hat. In meinem Fall wird das Fragment nicht aktualisiert, sondern Fragmente werden dynamisch entfernt oder hinzugefügt. Ohne FragmentStatePagerAdapter zu erweitern, gibt ViewPager zwischengespeicherte Fragmente zurück, was eine falsche Position ist. Vielen Dank!
Sira Lam
Funktioniert gut in der Support-Bibliothek 28.0.0
Oleksii K.
Überschreiben, um was zu tun? Wie erstelle ich ein Statusobjekt, das einem Adapter zugeordnet ist?
Phani Rithvij
Danke Kumpel. Diese Lösung funktioniert nur
Parthi
20

Nach stundenlanger Frustration beim Ausprobieren aller oben genannten Lösungen, um dieses Problem zu überwinden, und auch beim Ausprobieren vieler Lösungen für andere ähnliche Fragen wie diese , dies und das, die alle mit mir gescheitert sind, um dieses Problem zu lösen und ViewPagerdas Alte zu zerstören Fragmentund das pagermit zu füllen die neuen Fragments. Ich habe das Problem wie folgt gelöst:

1) Stellen Sie sicher, dass die ViewPagerKlasse FragmentPagerAdapterwie folgt erweitert wird:

 public class myPagerAdapter extends FragmentPagerAdapter {

2) Erstellen Sie ein Element für das ViewPager, in dem das titleund das fragmentfolgende gespeichert sind :

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) Lassen Sie den Konstruktor von ViewPagertake my FragmentManagerinstance classwie folgt in my speichern :

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 zur Wieder stellen Sie die adapterDaten mit den neuen Daten durch alle vorherigen Löschen fragmentaus der fragmentManagersich direkt um die adapterdie neue zu setzen fragmentaus der neuen Liste wieder , wie folgend:

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 Container Activityoder Fragmentinitialisieren Sie den Adapter nicht neu mit den neuen Daten. Stellen Sie die neuen Daten durch die Methode setPagerItemsmit den neuen Daten wie folgt ein:

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.

Sami Eltamawy
quelle
@Tomer was war das Problem, mit dem Sie konfrontiert waren?
Sami Eltamawy
9

Ich hatte das gleiche Problem und meine Lösung verwendet das FragmentPagerAdapterÜberschreiben von FragmentPagerAdapter#getItemId(int position):

@Override
public long getItemId(int position) {
    return mPages.get(position).getId();
}

Standardmäßig gibt diese Methode die Position des Elements zurück. Ich nehme an, dass ViewPagerüberprüft wird, ob itemIdgeändert wurde, und Seite nur neu erstellt, wenn es war. Die nicht überschriebene Version gibt jedoch dieselbe Position zurück wieitemId auch wenn die Seite tatsächlich unterschiedlich ist, und ViewPager definiert nicht, dass die Seite ersetzt wird und neu erstellt werden muss.

Um dies zu nutzen, long id wird für jede Seite benötigt. Normalerweise wird erwartet, dass es eindeutig ist, aber ich schlage für diesen Fall vor, dass es sich nur vom vorherigen Wert für dieselbe Seite unterscheiden sollte. Daher ist es hier möglich, einen kontinuierlichen Zähler in Adapter- oder Zufallszahlen (mit breiter Verteilung) zu verwenden.

Ich denke, dass es konsequenter ist, Tags der Ansicht zu verwenden, die in diesem Thema als Lösung erwähnt werden. Aber wahrscheinlich nicht für alle Fälle.

Atlascoder
quelle
1
Es ist. Ich habe es ausprobiert: Nur POSITION_NONE ist nicht genug. Vielen Dank für Ihre wundervolle Antwort! :)
Slava
Diese Funktion ist in nicht einmal vorhanden PagerAdapter. Welche Klasse ist das?
Saket
@ Saket, Sie haben Recht, die Klasse istFragmentPagerAdapter
Atlascoder
6

Ich fand eine sehr interessante Entscheidung für dieses Problem. Anstelle von FragmentPagerAdapter , der alle Fragmente im Speicher behält, können wir FragmentStatePagerAdapter ( android.support.v4.app.FragmentStatePagerAdapter) verwenden ) verwenden, der das Fragment jedes Mal neu , wenn wir es auswählen.

Die Realisierungen beider Adapter sind identisch. Wir müssen also nur " FragmentPagerAdapter erweitern " in " FragmentStatePagerAdapter erweitern " ändern.

Рома Богдан
quelle
Guter Punkt, es wird anderen helfen, Speicherprobleme von viewPager im Zusammenhang mit Fragment zu lösen
Ashu Kumar
Tolle Antwort, das hat mir sehr geholfen.
Sergio Marani
5

Nachdem ich viel nach diesem Problem gesucht hatte, fand ich eine wirklich gute Lösung, die meiner Meinung nach der richtige Weg ist, dies zu tun. Im Wesentlichen wird instantiateItem nur aufgerufen, wenn die Ansicht instanziiert wird, und nie wieder, es sei denn, die Ansicht wird zerstört (dies geschieht, wenn Sie die Funktion getItemPosition überschreiben, um POSITION_NONE zurückzugeben). Stattdessen möchten Sie speichern die erstellten Ansichten und entweder im Adapter aktualisieren, eine Get-Funktion generieren, damit jemand anderes sie aktualisieren kann, oder eine Set-Funktion, die den Adapter aktualisiert (mein Favorit).

Fügen Sie in Ihrem MyViewPagerAdapter eine Variable wie die folgende hinzu:

private View updatableView;

ein in Ihrem InstantiateItem:

 public Object instantiateItem(View collection, int position) {
        updatableView = new TextView(ctx); //My change is here
        view.setText(data.get(position));
        ((ViewPager)collection).addView(view);
        return view;
    }

Auf diese Weise können Sie eine Funktion erstellen, die Ihre Ansicht aktualisiert:

public updateText(String txt)
{
    ((TextView)updatableView).setText(txt);
}

Hoffe das hilft!

Deutsche
quelle
Mir wurde klar, dass ich die Daten nicht eingestellt habe instantiateItem, deshalb wurden meine Ansichten nicht aktualisiert. Aus deiner Antwort habe ich es erkannt. +1
Phani Rithvij
5

Zweieinhalb Jahre nachdem das OP seine Frage gestellt hat, ist dieses Thema immer noch ein Thema. Es ist offensichtlich, dass Googles Priorität in dieser Hinsicht nicht besonders hoch ist. Anstatt eine Lösung zu finden, habe ich eine Problemumgehung gefunden. Der große Durchbruch für mich war herauszufinden, was die wahre Ursache des Problems war (siehe die akzeptierte Antwort in diesem Beitrag ). Als sich herausstellte, dass das Problem darin bestand, dass aktive Seiten nicht ordnungsgemäß aktualisiert wurden, war meine Problemumgehung offensichtlich:

In meinem Fragment (den Seiten):

  • Ich habe den gesamten Code, der das Formular aus onCreateView ausfüllt, in eine Funktion namens PopulateForm eingefügt, die von überall und nicht vom Framework aufgerufen werden kann. Diese Funktion versucht, die aktuelle Ansicht mit getView abzurufen. Wenn dies null ist, wird nur zurückgegeben. Es ist wichtig, dass PopulateForm nur den angezeigten Code enthält - der gesamte andere Code, der FocusChange-Listener und dergleichen erstellt, befindet sich noch in OnCreate
  • Erstellen Sie einen Booleschen Wert, der als Flag verwendet werden kann und angibt, dass das Formular neu geladen werden muss. Meins ist mbReloadForm
  • Überschreiben Sie OnResume (), um PopulateForm () aufzurufen, wenn mbReloadForm festgelegt ist.

In meiner Aktivität, in der ich die Seiten lade:

  • Gehen Sie zu Seite 0, bevor Sie etwas ändern. Ich verwende FragmentStatePagerAdapter, daher weiß ich, dass höchstens zwei oder drei Seiten betroffen sind. Durch den Wechsel zu Seite 0 wird sichergestellt, dass ich das Problem immer nur auf den Seiten 0, 1 und 2 habe.
  • Bevor Sie die alte Liste löschen, nehmen Sie ihre Größe (). Auf diese Weise wissen Sie, wie viele Seiten von dem Fehler betroffen sind. Wenn> 3, reduzieren Sie es auf 3 - wenn Sie einen anderen PagerAdapter verwenden, müssen Sie sehen, wie viele Seiten Sie bearbeiten müssen (vielleicht alle?)
  • Laden Sie die Daten neu und rufen Sie pageAdapter.notifyDataSetChanged () auf.
  • Überprüfen Sie nun für jede der betroffenen Seiten, ob die Seite mithilfe von pager.getChildAt (i) aktiv ist. Hier erfahren Sie, ob Sie eine Ansicht haben. Wenn ja, rufen Sie pager.PopulateView () auf. Wenn nicht, setzen Sie das ReloadForm-Flag.

Wenn Sie danach einen zweiten Satz von Seiten neu laden, führt der Fehler immer noch dazu, dass einige die alten Daten anzeigen. Sie werden jetzt jedoch aktualisiert und Sie sehen die neuen Daten. Ihre Benutzer wissen nicht, dass die Seite jemals falsch war, da diese Aktualisierung erfolgt, bevor sie die Seite sehen.

Hoffe das hilft jemandem!

RichardP
quelle
5

All diese Lösungen haben mir nicht geholfen. So fand ich eine funktionierende Lösung: Sie können setAdapterjedes Mal, aber es ist nicht genug. Sie sollten dies tun, bevor Sie den Adapter wechseln:

FragmentManager fragmentManager = slideShowPagerAdapter.getFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
List<Fragment> fragments = fragmentManager.getFragments();
for (Fragment f : fragments) {
    transaction.remove(f);
}
transaction.commit();

und danach:

viewPager.setAdapter(adapter);
Mahdi Perfekt
quelle
Vielen Dank! In meinem Fall habe ich ViewPagerInnenfragment verwendet, also ersetzt slideShowPagerAdapter.getFragmentManager()durch getChildFragmentManager(). Vielleicht getFragmentManager()hilft in Ihrem Fall. Ich habe FragmentPagerAdapternicht benutzt FragmentStatePagerAdapter. Siehe auch stackoverflow.com/a/25994654/2914140 für einen hackigen Weg.
CoolMind
5

Ein viel einfacherer Weg: Verwenden Sie a FragmentPagerAdapterund wickeln Sie Ihre Seitenansichten in Fragmente ein. Sie werden aktualisiert

alfongj
quelle
5

Vielen Dank an rui.araujo und Alvaro Luis Bustamante. Zuerst versuche ich, den Weg von rui.araujo zu benutzen, weil es einfach ist. Es funktioniert, aber wenn sich die Daten ändern, wird die Seite offensichtlich neu gezeichnet. Es ist schlecht, also versuche ich, Alvaro Luis Bustamantes Weg zu benutzen. Es ist perfekt. Hier ist der Code:

@Override
protected void onStart() {
    super.onStart();
}

private class TabPagerAdapter extends PagerAdapter {
    @Override
    public int getCount() {
        return 4;
    }

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

    @Override
    public void destroyItem(final View container, final int position, final Object object) {
        ((ViewPager) container).removeView((View) object);
    }

    @Override
    public Object instantiateItem(final ViewGroup container, final int position) {
        final View view = LayoutInflater.from(
                getBaseContext()).inflate(R.layout.activity_approval, null, false);
        container.addView(view);
        ListView listView = (ListView) view.findViewById(R.id.list_view);
        view.setTag(position);
        new ShowContentListTask(listView, position).execute();
        return view;
    }
}

Und wenn sich Daten ändern:

for (int i = 0; i < 4; i++) {
    View view = contentViewPager.findViewWithTag(i);
    if (view != null) {
        ListView listView = (ListView) view.findViewById(R.id.list_view);
        new ShowContentListTask(listView, i).execute();
    }
}
evan.z
quelle
4

Nur für den Fall, dass jemand FragmentStatePagerAdapter verwendet basierten Adapter verwendet (mit dem ViewPager die für die Anzeige erforderlichen Mindestseiten erstellen kann, in meinem Fall höchstens 2), verursacht die Antwort von @ rui.araujo, getItemPosition in Ihrem Adapter zu überschreiben, keine erhebliche Verschwendung, aber dennoch Kann verbessert werden.

Im Pseudocode:

public int getItemPosition(Object object) {
    YourFragment f = (YourFragment) object;
    YourData d = f.data;
    logger.info("validate item position on page index: " + d.pageNo);

    int dataObjIdx = this.dataPages.indexOf(d);

    if (dataObjIdx < 0 || dataObjIdx != d.pageNo) {
        logger.info("data changed, discard this fragment.");
        return POSITION_NONE;
    }

    return POSITION_UNCHANGED;
}
Jerry Tian
quelle
In ViewPager war es recht einfach, die Position des Elements zu ermitteln. Die perfekteste Implementierung besteht darin, die neue Position zurückzugeben, getItemPosition()wenn sich das Dataset geändert hat, und ViewPager erkennt, dass sie geändert wurden oder nicht. ViewPager hat es leider nicht getan.
VinceStyling
3

Ich hatte ein ähnliches Problem, bei dem ich vier Seiten hatte und eine der Seiten die Ansichten der anderen drei Seiten aktualisierte. Ich konnte die Widgets (SeekBars, TextViews usw.) auf der Seite neben der aktuellen Seite aktualisieren. Die letzten beiden Seiten hätten beim Aufrufen nicht initialisierte WidgetsmTabsAdapter.getItem(position) .

Um mein Problem zu lösen, habe ich setSelectedPage(index)vor dem Anruf verwendetgetItem(position) . Dies würde die Seite instanziieren und es mir ermöglichen, Werte und Widgets auf jeder Seite zu ändern.

Nach all dem Update würde ich setSelectedPage(position)gefolgt von verwenden notifyDataSetChanged().

Sie können ein leichtes Flackern in der ListView auf der Hauptaktualisierungsseite sehen, aber nichts Auffälliges. Ich habe es nicht gründlich getestet, aber es löst mein unmittelbares Problem.

enKoder
quelle
Hallo, ich interessiere mich für Ihren Code. Ich habe ein ähnliches Problem. Ich kann benachbarte Fragmente aktualisieren, aber das aktuelle sichtbare Fragment wird nicht aktualisiert. Ich bin sehr verwirrt mit allen Antworten hier. Wie kann ich Ansichten im aktuellen Fragment aktualisieren, das vom VIewPager-Adapter angezeigt wird
Pankaj Nimgade
3

Ich poste diese Antwort nur für den Fall, dass jemand anderes sie nützlich findet. Um genau das Gleiche zu tun, habe ich einfach den Quellcode von ViewPager und PagerAdapter aus der Kompatibilitätsbibliothek genommen und in meinem Code kompiliert (Sie müssen alle Fehler und Importe selbst aussortieren, aber es kann definitiv durchgeführt werden).

Erstellen Sie dann im CustomViewPager eine Methode namens updateViewAt (int position). Die Ansicht selbst kann von ArrayList-Elementen abgerufen werden, die in der ViewPager-Klasse definiert sind (Sie müssen eine ID für die Ansichten beim Instanziieren des Elements festlegen und diese ID mit der Position in der updateViewAt () -Methode vergleichen). Anschließend können Sie die Ansicht nach Bedarf aktualisieren.

Naveen LP
quelle
2

Ich denke, ich habe die Logik von ViewPager.

Wenn ich eine Reihe von Seiten aktualisieren und basierend auf einem neuen Dataset anzeigen muss, rufe ich notifyDataSetChanged () auf . Anschließend ruft ViewPager getItemPosition () mehrfach auf und übergibt dort Fragment als Objekt. Dieses Fragment kann entweder aus einem alten Datensatz (den ich verwerfen möchte) oder aus einem neuen Datensatz (den ich anzeigen möchte) stammen. Also überschreibe ich getItemPosition () und muss dort irgendwie feststellen, ob mein Fragment aus dem alten oder aus dem neuen Datensatz stammt.

In meinem Fall habe ich ein Layout mit zwei Fenstern mit einer Liste der wichtigsten Elemente im linken Bereich und einer Wischansicht (ViewPager) im rechten Bereich. Daher speichere ich einen Link zu meinem aktuellen Top-Element in meinem PagerAdapter und auch in jedem instanziierten Seitenfragment. Wenn sich das ausgewählte oberste Element in der Liste ändert, speichere ich das neue oberste Element in PagerAdapter und rufe notifyDataSetChanged () auf . Und in der überschriebenen getItemPosition () vergleiche ich das oberste Element aus meinem Adapter mit dem obersten Element aus meinem Fragment. Und nur wenn sie nicht gleich sind, gebe ich POSITION_NONE zurück. Anschließend stellt PagerAdapter alle Fragmente wieder her, die POSITION_NONE zurückgegeben haben.

HINWEIS. Das Speichern der obersten Element-ID anstelle einer Referenz ist möglicherweise eine bessere Idee.

Das folgende Code-Snippet ist etwas schematisch, aber ich habe es aus dem tatsächlich funktionierenden Code angepasst.

public class SomeFragment extends Fragment {
  private TopItem topItem;
}

public class SomePagerAdapter extends FragmentStatePagerAdapter {
  private TopItem topItem;

  public void changeTopItem(TopItem newTopItem) {
    topItem = newTopItem;
    notifyDataSetChanged();
  }

  @Override
  public int getItemPosition(Object object) {
    if (((SomeFragment) object).getTopItemId() != topItem.getId()) {
      return POSITION_NONE;
    }
    return super.getItemPosition(object);
  }
}

Vielen Dank für alle bisherigen Forscher!

Stanislav Karakhanov
quelle
2

Der folgende Code hat bei mir funktioniert.

Erstellen Sie eine Klasse, die die FragmentPagerAdapter-Klasse wie folgt erweitert.

public class Adapter extends FragmentPagerAdapter {

private int tabCount;
private Activity mActivity;
private Map<Integer, String> mFragmentTags;
private FragmentManager mFragmentManager;
private int container_id;
private ViewGroup container;
private List<Object> object;

public Adapter(FragmentManager fm) {
    super(fm);
}

public Adapter(FragmentManager fm, int numberOfTabs , Activity mA) {
    super(fm);
    mActivity = mA;
    mFragmentManager = fm;
    object = new ArrayList<>();
    mFragmentTags = new HashMap<Integer, String>();
    this.tabCount = numberOfTabs;
}

@Override
public Fragment getItem(int position) {
    switch (position) {
        case 0:
            return Fragment0.newInstance(mActivity);
        case 1:
            return Fragment1.newInstance(mActivity);
        case 2:
            return Fragment2.newInstance(mActivity);
        default:
            return null;
    }}


@Override
public Object instantiateItem(ViewGroup container, int position) {
    Object object = super.instantiateItem(container, position);
    if (object instanceof Fragment) {
        Log.e("Already defined","Yes");
        Fragment fragment = (Fragment) object;
        String tag = fragment.getTag();
        Log.e("Fragment Tag","" + position + ", " + tag);
        mFragmentTags.put(position, tag);
    }else{
        Log.e("Already defined","No");
    }
    container_id = container.getId();
    this.container = container;
    if(position == 0){
        this.object.add(0,object);
    }else if(position == 1){
        this.object.add(1,object);
    }else if(position == 2){
        this.object.add(2,object);
    }
    return object;
}

@Override
public void destroyItem(ViewGroup container, int position, Object object) {
    super.destroyItem(container, position, object);
    if (object instanceof Fragment) {
        Log.e("Removed" , String.valueOf(position));
    }
}

@Override
public int getItemPosition (Object object)
{   int index = 0;
    if(this.object.get(0) == object){
        index = 0;
    }else if(this.object.get(1) == object){
        index = 1;
    }else if(this.object.get(2) == object){
        index = 2;
    }else{
        index = -1;
    }
    Log.e("Index" , "..................." + String.valueOf(index));
    if (index == -1)
        return POSITION_NONE;
    else
        return index;
}

public String getFragmentTag(int pos){
    return "android:switcher:"+R.id.pager+":"+pos;
}

public void NotifyDataChange(){
    this.notifyDataSetChanged();
}

public int getcontainerId(){
    return container_id;
}

public ViewGroup getContainer(){
    return this.container;
}

public List<Object> getObject(){
    return this.object;
}

@Override
public int getCount() {
    return tabCount;
}}

Erstellen Sie dann in jedem von Ihnen erstellten Fragment eine updateFragment-Methode. Bei dieser Methode ändern Sie die Dinge, die Sie im Fragment ändern müssen. In meinem Fall enthielt Fragment0 beispielsweise eine GLSurfaceView, die ein 3D-Objekt basierend auf einem Pfad zu einer .ply-Datei anzeigt. Daher ändere ich in meiner updateFragment-Methode den Pfad zu dieser Ply-Datei.

Erstellen Sie dann eine ViewPager-Instanz.

viewPager = (ViewPager) findViewById(R.id.pager);

und eine Adpater-Instanz,

adapter = new Adapter(getSupportFragmentManager(), 3, this);

dann mach das,

viewPager.setAdapter(adapter);
viewPager.setOffscreenPageLimit(1);

Dann haben Sie innerhalb der Klasse die Adapterklasse oben initialisiert und einen viewPager erstellt. Verwenden Sie jedes Mal, wenn Sie eines Ihrer Fragmente (in unserem Fall Fragment0) aktualisieren möchten, Folgendes:

adapter.NotifyDataChange();

adapter.destroyItem(adapter.getContainer(), 0, adapter.getObject().get(0)); // destroys page 0 in the viewPager.

fragment0 = (Fragment0) getSupportFragmentManager().findFragmentByTag(adapter.getFragmentTag(0)); // Gets fragment instance used on page 0.

fragment0.updateFragment() method which include the updates on this fragment

adapter.instantiateItem(adapter.getContainer(), 0); // re-initialize page 0.

Diese Lösung basierte auf der von Alvaro Luis Bustamante vorgeschlagenen Technik.

Brainnovo
quelle
1

1. Zuerst müssen Sie die getItemposition-Methode in Ihrer Pageradapter-Klasse festlegen. 2. Sie müssen die genaue Position Ihres View Pager lesen. 3. Dann senden Sie diese Position als Datenspeicherort Ihres neuen Pagers. 4. Schreiben Sie die Aktualisierungsschaltfläche, indem Sie auf den Listener in setonPageChange klicken Hörer

Dieser Programmcode wurde ein wenig geändert, um nur das bestimmte Positionselement festzulegen

public class MyActivity extends Activity {

private ViewPager myViewPager;
private List<String> data;
public int location=0;
public Button updateButton;
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

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

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

      updateButton = (Button) findViewById(R.id.update);

    myViewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
        @Override
        public void onPageScrolled(int i, float v, int i2) {
             //Toast.makeText(MyActivity.this, i+"  Is Selected  "+data.size(), Toast.LENGTH_SHORT).show();
        }

        @Override
        public void onPageSelected( int i) {
          // here you will get the position of selected page
            final int k = i;
             updateViewPager(k);

        }

        @Override
        public void onPageScrollStateChanged(int i) {

        }
    });
}

private void updateViewPager(final int i) {  
    updateButton.setOnClickListener(new OnClickListener() {

        @Override
        public void onClick(View v) {

            Toast.makeText(MyActivity.this, i+"  Is Selected  "+data.size(), Toast.LENGTH_SHORT).show();
            data.set(i, "Replaced "+i);         
            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 int getItemPosition(Object object) {
        return POSITION_NONE;
    }

    @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) {
    }
}
}
Naveen Kumar
quelle
1

Was für mich funktionierte, ging viewPager.getAdapter().notifyDataSetChanged();

und in den Adapter geben Sie Ihren Code zum Aktualisieren der Ansicht getItemPositionwie folgt ein

@Override
public int getItemPosition(Object object) {

    if (object instanceof YourViewInViewPagerClass) { 
        YourViewInViewPagerClass view = (YourViewInViewPagerClass)object;
        view.setData(data);
    }

    return super.getItemPosition(object);
}

Vielleicht ist es nicht die richtige Vorgehensweise, aber es hat funktioniert (der return POSITION_NONETrick verursachte einen Absturz für mich, war also keine Option).

Fonix
quelle
1

Sie können alle Fragmente dynamisch aktualisieren, was Sie in drei Schritten sehen können.

In Ihrem Adapter:

public class MyPagerAdapter extends FragmentPagerAdapter {
private static int NUM_ITEMS = 3;
private Map<Integer, String> mFragmentTags;
private FragmentManager mFragmentManager;

public MyPagerAdapter(FragmentManager fragmentManager) {
    super(fragmentManager);
    mFragmentManager = fragmentManager;
    mFragmentTags = new HashMap<Integer, String>();
}

// Returns total number of pages
@Override
public int getCount() {
    return NUM_ITEMS;
}

// Returns the fragment to display for that page
@Override
public Fragment getItem(int position) {
    switch (position) {
        case 0:
            return FirstFragment.newInstance();
        case 1:
            return SecondFragment.newInstance();
        case 2:
            return ThirdFragment.newInstance();
        default:
            return null;
    }
}

// Returns the page title for the top indicator
@Override
public CharSequence getPageTitle(int position) {
    return "Page " + position;
}

@Override
public Object instantiateItem(ViewGroup container, int position) {
    Object object = super.instantiateItem(container, position);
    if (object instanceof Fragment) {
        Fragment fragment = (Fragment) object;
        String tag = fragment.getTag();
        mFragmentTags.put(position, tag);
    }
    return object;
}

public Fragment getFragment(int position) {
    Fragment fragment = null;
    String tag = mFragmentTags.get(position);
    if (tag != null) {
        fragment = mFragmentManager.findFragmentByTag(tag);
    }
    return fragment;
}}

Jetzt in Ihrer Aktivität:

public class MainActivity extends AppCompatActivity implements ViewPager.OnPageChangeListener{

MyPagerAdapter mAdapterViewPager;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    ViewPager viewPager = (ViewPager) findViewById(R.id.vpPager);
    mAdapterViewPager = new MyPagerAdapter(getSupportFragmentManager());
    viewPager.setAdapter(mAdapterViewPager);
    viewPager.addOnPageChangeListener(this);
}

@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

}

@Override
public void onPageSelected(int position) {

    Fragment fragment = mAdapterViewPager.getFragment(position);
    if (fragment != null) {
        fragment.onResume();
    }
}

@Override
public void onPageScrollStateChanged(int state) {

}}

Zum Schluss in Ihrem Fragment so etwas:

public class YourFragment extends Fragment {

// newInstance constructor for creating fragment with arguments
public static YourFragment newInstance() {

    return new YourFragment();
}

// Store instance variables based on arguments passed
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
}

// Inflate the view for the fragment based on layout XML
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    return inflater.inflate(R.layout.fragment, container, false);
}


@Override
public void onResume() {
    super.onResume();

    //to refresh your view
    refresh();

}}

Den vollständigen Code finden Sie hier .

Vielen Dank, Alvaro Luis Bustamante.

Cabezas
quelle
1

Immer zurückzukehren POSITION_NONEist einfach, aber ein wenig ineffizient, da dies die Instanziierung aller Seiten hervorruft, die bereits instanziiert wurden.

Ich habe eine Bibliothek ArrayPagerAdapter erstellt , um Elemente in PagerAdapters dynamisch zu ändern.

Intern Bring diese Bibliothek Adapter POSITION_NONEauf getItemPosiition()nur bei Bedarf.

Mithilfe dieser Bibliothek können Sie Elemente dynamisch wie folgt ändern.

@Override
protected void onCreate(Bundle savedInstanceState) {
        /** ... **/
    adapter = new MyStatePagerAdapter(getSupportFragmentManager()
                            , new String[]{"1", "2", "3"});
    ((ViewPager)findViewById(R.id.view_pager)).setAdapter(adapter);
     adapter.add("4");
     adapter.remove(0);
}

class MyPagerAdapter extends ArrayViewPagerAdapter<String> {

    public MyPagerAdapter(String[] data) {
        super(data);
    }

    @Override
    public View getView(LayoutInflater inflater, ViewGroup container, String item, int position) {
        View v = inflater.inflate(R.layout.item_page, container, false);
        ((TextView) v.findViewById(R.id.item_txt)).setText(item);
        return v;
    }
}

Die Thils-Bibliothek unterstützt auch von Fragmenten erstellte Seiten.

T. Nakama
quelle
1

Dies ist für alle wie mich, die den Viewpager von einem Dienst (oder einem anderen Hintergrund-Thread) aktualisieren müssen und keiner der Vorschläge hat funktioniert: Nach einigem Überprüfen der Protokollierung wurde mir klar, dass die notifyDataSetChanged () -Methode niemals zurückkehrt. getItemPosition (Object object) wird dort ohne weitere Verarbeitung als All-Ends bezeichnet. Dann fand ich in den Dokumenten der übergeordneten PagerAdapter-Klasse (nicht in den Dokumenten der Unterklassen): "Datensatzänderungen müssen im Hauptthread auftreten und mit einem Aufruf von notifyDataSetChanged () enden." Die Arbeitslösung in diesem Fall war also (unter Verwendung von FragmentStatePagerAdapter und getItemPosition (Objektobjekt) so eingestellt, dass POSITION_NONE zurückgegeben wird):

und dann den Aufruf von notifyDataSetChanged ():

runOnUiThread(new Runnable() {
         @Override
         public void run() {
             pager.getAdapter().notifyDataSetChanged();
         }
     });
Helmwag
quelle
1

Sie können die Pager-Transformation in Viewpager wie folgt hinzufügen

myPager.setPageTransformer(true, new MapPagerTransform());

Im folgenden Code habe ich meine Ansichtsfarbe zur Laufzeit beim Scrollen des Pagers geändert

public class MapPagerTransform implements ViewPager.PageTransformer {


    public void transformPage(View view, float position) {
     LinearLayout  showSelectionLL=(LinearLayout)view.findViewById(R.id.showSelectionLL);

     if (position < 0) {

                showSelectionLL.setBackgroundColor(Color.WHITE);
     }else if (position >0){

                showSelectionLL.setBackgroundColor(Color.WHITE);
     }else {
                showSelectionLL.setBackgroundColor(Color.RED);
     }
   }
}
Akshay Dashore
quelle
1

Ich weiß, ich bin verdammt spät, aber es kann trotzdem jemandem helfen. Ich erweitere nur die entsprechende Antwort und habe auch den Kommentar dazu hinzugefügt.

Gut,

Die Antwort selbst besagt, dass es ineffizient ist

Um es nur bei Bedarf zu aktualisieren, können Sie dies tun

private boolean refresh;

public void refreshAdapter(){
    refresh = true;
    notifyDataSetChanged();
}

@Override
public int getItemPosition(@NonNull Object object) {
    if(refresh){
        refresh = false;
        return POSITION_NONE;
    }else{
        return super.getItemPosition(object);
    }
}
Beispiel
quelle
1

ViewPager wurde nicht für die Unterstützung dynamischer Ansichtsänderungen entwickelt.

Ich hatte eine Bestätigung, als ich nach einem anderen Fehler im Zusammenhang mit diesem https://issuetracker.google.com/issues/36956111 und insbesondere https://issuetracker.google.com/issues/36956111#comment56 suchte

Diese Frage ist etwas alt, aber Google hat dieses Problem kürzlich mit ViewPager2 gelöst . Damit können handgefertigte (nicht gewartete und möglicherweise fehlerhafte) Lösungen durch Standardlösungen ersetzt werden. Es verhindert auch, dass Ansichten unnötig neu erstellt werden, wie dies bei einigen Antworten der Fall ist.

Beispiele für ViewPager2 finden Sie unter https://github.com/googlesamples/android-viewpager2

Wenn Sie ViewPager2 verwenden möchten, müssen Sie der Datei build.gradle die folgende Abhängigkeit hinzufügen:

  dependencies {
     implementation 'androidx.viewpager2:viewpager2:1.0.0-beta02'
  }

Anschließend können Sie Ihren ViewPager in Ihrer XML-Datei durch Folgendes ersetzen:

    <androidx.viewpager2.widget.ViewPager2
        android:id="@+id/pager"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />

Danach müssen Sie ViewPager in Ihrer Aktivität durch ViewPager2 ersetzen

ViewPager2 benötigt entweder einen RecyclerView.Adapter oder einen FragmentStateAdapter. In Ihrem Fall kann es sich um einen RecyclerView.Adapter handeln

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;

import java.util.ArrayList;

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {

    private Context context;
    private ArrayList<String> arrayList = new ArrayList<>();

    public MyAdapter(Context context, ArrayList<String> arrayList) {
        this.context = context;
        this.arrayList = arrayList;
    }

    @NonNull
    @Override
    public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(context).inflate(R.layout.list_item, parent, false);
        return new MyViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
        holder.tvName.setText(arrayList.get(position));
    }

    @Override
    public int getItemCount() {
        return arrayList.size();
    }

    public class MyViewHolder extends RecyclerView.ViewHolder {
        TextView tvName;

        public MyViewHolder(@NonNull View itemView) {
            super(itemView);
            tvName = itemView.findViewById(R.id.tvName);
        }
    }
} 

Wenn Sie ein TabLayout verwendet haben, können Sie einen TabLayoutMediator verwenden:

        TabLayoutMediator tabLayoutMediator = new TabLayoutMediator(tabLayout, viewPager, true, new TabLayoutMediator.OnConfigureTabCallback() {
            @Override
            public void onConfigureTab(@NotNull TabLayout.Tab tab, int position) {
                // configure your tab here
                tab.setText(tabs.get(position).getTitle());
            }
        });

        tabLayoutMediator.attach();

Anschließend können Sie Ihre Ansichten aktualisieren, indem Sie die Daten Ihres Adapters ändern und die notifyDataSetChanged-Methode aufrufen

Yves Delerm
quelle
0

Ich glaube, ich habe eine einfache Möglichkeit gefunden, um Änderungen an Datensätzen zu melden:

Ändern Sie zunächst die Funktionsweise der Funktion instantiateItem ein wenig:

    @Override
    public Object instantiateItem(final ViewGroup container, final int position) {
        final View rootView = mInflater.inflate(...,container, false);
        rootView.setTag(position);
        updateView(rootView, position);
        container.addView(rootView, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
        mViewPager.setObjectForPosition(rootView, position);
        return rootView;
    }

Füllen Sie für "updateView" die Ansicht mit allen Daten, die Sie füllen möchten (setText, setBitmapImage, ...).

Stellen Sie sicher, dass destroyView folgendermaßen funktioniert:

    @Override
    public void destroyItem(final ViewGroup container, final int position, final Object obj) {
        final View viewToRemove = (View) obj;
        mViewPager.removeView(viewToRemove);
    }

Angenommen, Sie müssen die Daten ändern, dies tun und dann die nächste Funktion im PagerAdapter aufrufen:

    public void notifyDataSetChanged(final ViewPager viewPager, final NotifyLocation fromPos,
            final NotifyLocation toPos) {
        final int offscreenPageLimit = viewPager.getOffscreenPageLimit();
        final int fromPosInt = fromPos == NotifyLocation.CENTER ? mSelectedPhotoIndex
                : fromPos == NotifyLocation.MOST_LEFT ? mSelectedPhotoIndex - offscreenPageLimit
                        : mSelectedPhotoIndex + offscreenPageLimit;
        final int toPosInt = toPos == NotifyLocation.CENTER ? mSelectedPhotoIndex
                : toPos == NotifyLocation.MOST_LEFT ? mSelectedPhotoIndex - offscreenPageLimit
                        : mSelectedPhotoIndex + offscreenPageLimit;
        if (fromPosInt <= toPosInt) {
            notifyDataSetChanged();
            for (int i = fromPosInt; i <= toPosInt; ++i) {
                final View pageView = viewPager.findViewWithTag(i);
                mPagerAdapter.updateView(pageView, i);
            }
        }
    }

public enum NotifyLocation {
    MOST_LEFT, CENTER, MOST_RIGHT
}

Wenn Sie beispielsweise alle vom viewPager angezeigten Ansichten benachrichtigen möchten, dass sich etwas geändert hat, können Sie Folgendes aufrufen:

notifyDataSetChanged(mViewPager,NotifyLocation.MOST_LEFT,NotifyLocation.MOST_RIGHT);

Das ist es.

Android-Entwickler
quelle
0

Für das, was es wert ist, scheint es auf KitKat + adapter.notifyDataSetChanged()genug zu sein, um die neuen Ansichten anzuzeigen, vorausgesetzt, Sie sind setOffscreenPageLimitausreichend hoch. Ich bin in der Lage, das gewünschte Verhalten zu erreichen viewPager.setOffscreenPageLimit(2).

mszaro
quelle
0

Ich benutze tatsächlich notifyDataSetChanged()an ViewPagerund CirclePageIndicatorund danach rufe ich destroyDrawingCache()an ViewPagerund es funktioniert. Keine der anderen Lösungen hat für mich funktioniert.

czaku
quelle
Die ursprüngliche Frage ist fast 2 Jahre alt. Ich bin sicher, dass es mittlerweile viele Unterschiede in der Android-API gibt, da die Antworten hier möglicherweise nicht für alle Versionen der Android-API richtig sind.
C0deAttack