ViewPager dynamisch aktualisieren?


316

Ich kann den Inhalt in ViewPager nicht aktualisieren.

Was ist die korrekte Verwendung der Methoden instantiateItem () und getItem () in der FragmentPagerAdapter-Klasse?

Ich habe nur getItem () verwendet, um meine Fragmente zu instanziieren und zurückzugeben:

@Override
public Fragment getItem(int position) {
    return new MyFragment(context, paramters);
}

Das hat gut funktioniert. Außer ich kann den Inhalt nicht ändern.

Also fand ich Folgendes : ViewPager PagerAdapter aktualisiert die Ansicht nicht

"Mein Ansatz ist es, die setTag () -Methode für jede instanziierte Ansicht in der instantiateItem () -Methode zu verwenden."

Jetzt möchte ich instantiateItem () implementieren, um dies zu tun. Aber ich weiß nicht, was ich zurückgeben muss (der Typ ist Object) und wie ist die Beziehung zu getItem (int position)?

Ich habe die Referenz gelesen :

  • public abstract Fragment getItem (int position)

    Gibt das Fragment zurück, das einer bestimmten Position zugeordnet ist.

  • public Object instantiateItem (ViewGroup-Container, int-Position)

    Erstellen Sie die Seite für die angegebene Position. Der Adapter ist für das Hinzufügen der Ansicht zu dem hier angegebenen Container verantwortlich, muss jedoch nur sicherstellen, dass dies bis zum Zeitpunkt der Rückkehr von finishUpdate (ViewGroup) erfolgt. Parameter

    container Die enthaltende Ansicht, in der die Seite angezeigt wird. position Die zu instanziierende Seitenposition.

    Kehrt zurück

    Gibt ein Objekt zurück, das die neue Seite darstellt. Dies muss keine Ansicht sein, kann aber auch ein anderer Container der Seite sein.

aber ich verstehe es immer noch nicht.

Hier ist mein Code. Ich verwende das Support Package v4.

ViewPagerTest

public class ViewPagerTest extends FragmentActivity {
    private ViewPager pager;
    private MyFragmentAdapter adapter; 

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.pager1);

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

        String[] data = {"page1", "page2", "page3", "page4", "page5", "page6"};

        adapter = new MyFragmentAdapter(getSupportFragmentManager(), 6, this, data);
        pager.setAdapter(adapter);

        ((Button)findViewById(R.id.button)).setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                reload();
            }
        });
    }

    private void reload() {
        String[] data = {"changed1", "changed2", "changed3", "changed4", "changed5", "changed6"};
        //adapter = new MyFragmentAdapter(getSupportFragmentManager(), 6, this, data);
        adapter.setData(data);
        adapter.notifyDataSetChanged();
        pager.invalidate();

        //pager.setCurrentItem(0);
    }
}

MyFragmentAdapter

class MyFragmentAdapter extends FragmentPagerAdapter {
    private int slideCount;
    private Context context;
    private String[] data;

    public MyFragmentAdapter(FragmentManager fm, int slideCount, Context context, String[] data) {
        super(fm);
        this.slideCount = slideCount;
        this.context = context;
        this.data = data;
    }

    @Override
    public Fragment getItem(int position) {
        return new MyFragment(data[position], context);
    }

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

    public void setData(String[] data) {
        this.data = data;
    }

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

MyFragment

public final class MyFragment extends Fragment {
    private String text;

    public MyFragment(String text, Context context) {
        this.text = text;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.slide, null);
        ((TextView)view.findViewById(R.id.text)).setText(text);

        return view;
    }
}

Hier ist auch jemand mit einem ähnlichen Problem, keine Antworten http://www.mail-archive.com/android-developers@googlegroups.com/msg200477.html


Um ein besseres Verständnis der beteiligten Komponenten zu erhalten, kann es hilfreich sein, mein Tutorial über einen ViewPager mit Registerkarten und separatem Navigationsverlauf zu lesen. Es kommt mit einem funktionierenden Beispiel auf GitHub und zusätzlichen Ressourcen: medium.com/@nilan/…
marktani


Sie können dieses Beispiel in GitHub überprüfen . Ich hoffe, dieses Beispiel hilft Ihnen.
Cabezas

Gute einfache Lösung: stackoverflow.com/a/35450575/2162226
Gene Bo

Google hat kürzlich ViewPager2 eingeführt, das dynamische Fragment- / View-Updates vereinfacht. Sie können Beispiele auf github.com/googlesamples/android-viewpager2 oder diese Antwort auf stackoverflow.com/a/57393414/1333448
Yves Delerm

Antworten:


605

Wenn Sie FragmentPagerAdapter oder FragmentStatePagerAdapter verwenden, ist es am besten, nur zu behandeln getItem()und überhaupt nicht zu berühren instantiateItem(). Die instantiateItem()- destroyItem()- isViewFromObject()Schnittstelle in PagerAdapter ist eine untergeordnete Schnittstelle, die FragmentPagerAdapter verwendet, um die viel einfachere getItem()Schnittstelle zu implementieren .

Bevor ich darauf eingehe, sollte ich das klarstellen

Wenn Sie die tatsächlich angezeigten Fragmente ausschalten möchten, müssen Sie FragmentPagerAdapter vermeiden und FragmentStatePagerAdapter verwenden.

Eine frühere Version dieser Antwort hat den Fehler gemacht, FragmentPagerAdapter als Beispiel zu verwenden - das funktioniert nicht, da FragmentPagerAdapter ein Fragment niemals zerstört, nachdem es zum ersten Mal angezeigt wurde.

Ich empfehle , die nicht setTag()und findViewWithTag()Abhilfe der Post , der Sie verknüpfen zur Verfügung gestellt. Wie Sie festgestellt haben, funktioniert es nicht mit Fragmenten setTag()und findViewWithTag()funktioniert nicht mit Fragmenten.

Die richtige Lösung ist das Überschreiben getItemPosition(). Wenn notifyDataSetChanged()ViewPager aufgerufen wird, ruft es getItemPosition()alle Elemente in seinem Adapter auf, um festzustellen, ob sie an eine andere Position verschoben oder entfernt werden müssen.

Standardmäßig wird getItemPosition()zurückgegeben POSITION_UNCHANGED, was bedeutet: "Dieses Objekt ist in Ordnung, wo es sich befindet. Zerstören oder entfernen Sie es nicht." Die Rückgabe POSITION_NONEbehebt das Problem, indem stattdessen gesagt wird: "Dieses Objekt ist kein Element mehr, das ich anzeige. Entfernen Sie es." Dies bewirkt, dass jedes einzelne Element in Ihrem Adapter entfernt und neu erstellt wird.

Dies ist eine völlig legitime Lösung! Durch diesen Fix verhält sich notifyDataSetChanged wie ein normaler Adapter ohne View-Recycling. Wenn Sie dieses Update implementieren und die Leistung zufriedenstellend ist, können Sie an den Rennen teilnehmen. Job erledigt.

Wenn Sie eine bessere Leistung benötigen, können Sie eine schickere getItemPosition()Implementierung verwenden. Hier ist ein Beispiel für einen Pager, der Fragmente aus einer Liste von Zeichenfolgen erstellt:

ViewPager pager = /* get my ViewPager */;
// assume this actually has stuff in it
final ArrayList<String> titles = new ArrayList<String>();

FragmentManager fm = getSupportFragmentManager();
pager.setAdapter(new FragmentStatePagerAdapter(fm) {
    public int getCount() {
        return titles.size();
    }

    public Fragment getItem(int position) {
        MyFragment fragment = new MyFragment();
        fragment.setTitle(titles.get(position));
        return fragment;
    }

    public int getItemPosition(Object item) {
        MyFragment fragment = (MyFragment)item;
        String title = fragment.getTitle();
        int position = titles.indexOf(title);

        if (position >= 0) {
            return position;
        } else {
            return POSITION_NONE;
        }
    }
});

Bei dieser Implementierung werden nur Fragmente angezeigt, die neue Titel anzeigen. Alle Fragmente, die Titel anzeigen, die sich noch in der Liste befinden, werden stattdessen an ihre neue Position in der Liste verschoben, und Fragmente mit Titeln, die überhaupt nicht mehr in der Liste enthalten sind, werden zerstört.

Was ist, wenn das Fragment nicht neu erstellt wurde, aber trotzdem aktualisiert werden muss? Aktualisierungen eines lebenden Fragments werden am besten vom Fragment selbst durchgeführt. Das ist schließlich der Vorteil eines Fragments - es ist ein eigener Controller. Ein Fragment kann einem anderen Objekt in einen Listener oder einen Beobachter hinzufügen onCreate()und es dann entfernen onDestroy(), wodurch die Aktualisierungen selbst verwaltet werden. Sie müssen nicht den gesamten Aktualisierungscode getItem()wie in einem Adapter für eine ListView oder andere AdapterView-Typen einfügen.

Eine letzte Sache - nur weil FragmentPagerAdapter ein Fragment nicht zerstört, bedeutet dies nicht, dass getItemPosition in einem FragmentPagerAdapter völlig nutzlos ist. Mit diesem Rückruf können Sie Ihre Fragmente weiterhin im ViewPager neu anordnen. Sie werden jedoch niemals vollständig aus dem FragmentManager entfernt.


4
Funktioniert nicht, es aktualisiert immer noch nichts ... hat meinen Code gepostet.
Ixx

64
Okay, habe das Problem herausgefunden - Sie müssen FragmentStatePagerAdapter anstelle von FragmentPagerAdapter verwenden. FragmentPagerAdapter entfernt niemals Fragmente aus dem FragmentManager, sondern löst sie nur ab und hängt sie an. Weitere Informationen finden Sie in den Dokumenten. Im Allgemeinen möchten Sie FragmentPagerAdapter nur für permanente Fragmente verwenden. Ich habe mein Beispiel mit der Korrektur bearbeitet.
Bill Phillips

1
Ich werde dies später noch einmal überprüfen ... danke für Ihre Antwort. Und dann akzeptiere ich Ihre Antwort, wenn es funktioniert. Vor Ihrem letzten Kommentar habe ich implementiert, um jedes Mal einen neuen ViewPager zu entfernen und hinzuzufügen, und das funktioniert. Keine sehr gute Lösung, aber ich habe jetzt keine Zeit, sie zu ändern.
Ixx

12
Sie haben Recht, das Ändern der Klasse des Adapters in FragmentStatePagerAdapter hat alle meine Probleme behoben. Vielen Dank!
Ixx

2
Selbst wenn ich POSITION_NONE zurückgebe, FragmentStatePagerAdapterkann ich während der Verwendung feststellen, dass mein Fragment entfernt und die neue Instanz erstellt wird. Die neue Instanz erhält jedoch ein savedInstanceState-Bundle mit dem Status eines alten Fragments. Wie kann ich das Fragment vollständig zerstören, sodass Bundle beim erneuten Erstellen null ist?
Gesungen

142

Statt der Rückkehr POSITION_NONEaus getItemPosition()und volle Sicht Erholung verursacht, dies zu tun:

//call this method to update fragments in ViewPager dynamically
public void update(UpdateData xyzData) {
    this.updateData = xyzData;
    notifyDataSetChanged();
}

@Override
public int getItemPosition(Object object) {
    if (object instanceof UpdateableFragment) {
        ((UpdateableFragment) object).update(updateData);
    }
    //don't return POSITION_NONE, avoid fragment recreation. 
    return super.getItemPosition(object);
}

Ihre Fragmente sollten die UpdateableFragmentSchnittstelle implementieren :

public class SomeFragment extends Fragment implements
    UpdateableFragment{

    @Override
    public void update(UpdateData xyzData) {
         // this method will be called for every fragment in viewpager
         // so check if update is for this fragment
         if(forMe(xyzData)) {
           // do whatever you want to update your UI
         }
    }
}

und die Schnittstelle:

public interface UpdateableFragment {
   public void update(UpdateData xyzData);
}

Ihre Datenklasse:

public class UpdateData {
    //whatever you want here
}

1
Bedeutet dies, dass Ihre MainActivity auch die Schnittstelle implementieren muss? Bitte helfen Sie mir hier raus. In meiner aktuellen Implementierung verwende ich meine MainActivity-Klasse als Kommunikator zwischen den Fragmenten, daher war ich über diese Lösung verwirrt.
Rakeeb Rajbhandari

1
Ok, ich habe an einer anderen Stelle mit vollständiger Implementierung geantwortet, an der ich Fragmentdynamisch aktualisiere. Schauen Sie sich das an: stackoverflow.com/questions/19891828/…
M-WaJeEh

@ M-WaJeEh vielleicht ein Beispiel bitte !!! Ich habe eine Listenansicht mit einer Liste von Städten und nach Auswahl der Stadt öffnet sich der Viewpager (3 Seiten) mit Informationen über diese Stadt. es funktioniert ok Nach Auswahl eines anderen Stadtansichtspagers wird jedoch nur die 3D- d page on refreshes the 1-st page only after swiping pages? thw 2Seite immer leer angezeigt . danke
SERG

4
unter all den Sachen, die ich in den letzten 2,5 Tagen meines Arbeitslebens gesehen habe. Dies ist die einzige, die für mich funktioniert hat. Alle Arten von Arigato, danke, Spaciba, Sukran.
Orkun Ozen

2
Mutter der Drachen!, Das funktioniert wie ein Zauber, die einzige Lösung hat für mich funktioniert ... vielen Dank !!
Sebastián Guerrero

24

für diejenigen, die immer noch das gleiche Problem haben, mit dem ich vorher konfrontiert war, als ich ein ViewPagermit 7 Fragmenten habe. Die Standardeinstellung für diese Fragmente zum Laden des englischen Inhalts aus dem APIDienst, aber das Problem hier, dass ich die Sprache aus der Einstellungsaktivität ändern möchte und nach Abschluss der Einstellungsaktivität ViewPagerin der Hauptaktivität die Fragmente aktualisieren soll, um sie an die Sprachauswahl des Benutzers anzupassen und zu laden Der arabische Inhalt, wenn der Benutzer hier Arabisch wählt, was ich getan habe, um vom ersten Mal an zu arbeiten

1- Sie müssen FragmentStatePagerAdapter wie oben erwähnt verwenden.

2- on mainActivity Ich habe den onResume überschrieben und Folgendes getan

if (!(mPagerAdapter == null)) {
    mPagerAdapter.notifyDataSetChanged();
}

3-i hat den getItemPosition()in mPagerAdapter überschrieben und zurückgeben lassen POSITION_NONE.

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

wirkt wie Charme


1
Funktioniert außer in einer Situation. Wenn Sie das letzte Element in viewPager löschen. Dann wird es nicht aus der Liste entfernt.
Vlado Pandžić

Ich habe Fragmente sowohl auf der linken als auch auf der rechten Seite des aktuellen Fragments im Viewpager dynamisch hinzugefügt. Dieser hat funktioniert! Vielen Dank.
H8pathak

15

Ich bin auf dieses Problem gestoßen und habe es heute endlich gelöst. Ich schreibe auf, was ich gelernt habe, und hoffe, dass es für jemanden hilfreich ist, der neu bei Android ist ViewPagerund wie ich aktualisiere. Ich verwende FragmentStatePagerAdapterin API Level 17 und habe derzeit nur 2 Fragmente. Ich denke, es muss etwas nicht korrekt sein, bitte korrigieren Sie mich, danke. Geben Sie hier die Bildbeschreibung ein

  1. Serialisierte Daten müssen in den Speicher geladen werden. Dies kann unter Verwendung eines CursorLoader/ AsyncTask/ Thread. Ob es automatisch geladen wird, hängt von Ihrem Code ab. Wenn Sie a verwenden CursorLoader, wird es automatisch geladen, da ein registrierter Datenbeobachter vorhanden ist.

  2. Nach dem Aufruf viewpager.setAdapter(pageradapter)wird der Adapter getCount()ständig aufgerufen, um Fragmente zu erstellen. Wenn also Daten geladen werden, getCount()kann 0 zurückgegeben werden, sodass Sie keine Dummy-Fragmente für keine angezeigten Daten erstellen müssen.

  3. Nachdem die Daten geladen wurden, erstellt der Adapter keine Fragmente automatisch, da sie getCount()immer noch 0 sind. Daher können wir die tatsächlich geladene Datennummer festlegen, von der zurückgegeben werden soll getCount(), und dann die Adapter aufrufen notifyDataSetChanged(). ViewPagerBeginnen Sie mit der Erstellung von Fragmenten (nur die ersten beiden Fragmente) anhand von Daten im Speicher. Es ist fertig, bevor notifyDataSetChanged()es zurückgegeben wird. Dann ViewPagerhat der die richtigen Fragmente, die Sie brauchen.

  4. Wenn die Daten in der Datenbank und im Speicher beide aktualisiert werden (Durchschreiben) oder nur Daten im Speicher aktualisiert werden (Zurückschreiben) oder nur Daten in der Datenbank aktualisiert werden. In den letzten beiden Fällen, wenn Daten nicht automatisch aus der Datenbank in den Speicher geladen werden (wie oben erwähnt). Der ViewPagerund der Pager-Adapter verarbeiten nur Daten im Speicher.

  5. Wenn also die Daten im Speicher aktualisiert werden, müssen wir nur die Adapter aufrufen notifyDataSetChanged(). Da das Fragment bereits erstellt wurde, werden die Adapter onItemPosition()vor der notifyDataSetChanged()Rückkehr aufgerufen . Es muss nichts getan werden getItemPosition(). Dann werden die Daten aktualisiert.


Ich habe festgestellt, dass nach dem Aktualisieren der Daten (im Speicher) nichts mehr zu tun ist. Rufen Sie notifyDataSetChanged()einfach an und aktualisieren Sie sie einfach automatisch, da sich das von mir verwendete Fragment (mit Diagrammansicht) selbst aktualisiert. Wenn es nicht wie ein statisches Fragment ist, muss ich anrufen onItemPositon()und es POSITION_NONE tut mir leid
oscarthecat

Hat jemand eine Idee, mit welchem ​​Tool dieses Diagramm erstellt wurde?
JuiCe

12

Probieren Sie destroyDrawingCache()ViewPager nach notifyDataSetChanged()in Ihrem Code aus.


3
Hier gilt das gleiche. Funktioniert super danke. Ich hatte es besonders schwer, weil ich es nicht Fragmentsin meinem View Pager verwende. Also vielen Dank!
Seth

11

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 mit den neuen Daten neu. 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.


können Sie mir in meiner Ausgabe stackoverflow.com/questions/40149039/…
Albert

Können Sie mir helfen mit diesem Thema bitte stackoverflow.com/questions/41547995/...
viper

10

Aus irgendeinem Grund funktionierte keine der Antworten für mich, so dass ich die restoreState-Methode überschreiben musste, ohne super in meinem fragmentStatePagerAdapter aufzurufen. Code:

private class MyAdapter extends FragmentStatePagerAdapter {

    // [Rest of implementation]

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

}

1
Nachdem ich alles andere in dieser SO-Antwort ausprobiert habe, hat dies für mich funktioniert. Ich beende () eine Aktivität, die zu der Aktivität zurückkehrt, in der sich der betreffende PagerAdapter befindet. Ich möchte, dass in diesem Fall alle meine Fragmente neu erstellt werden, da die gerade abgeschlossene Aktivität möglicherweise den Status geändert hat, der zum Zeichnen der Fragmente verwendet wird.
Johnny Lambada

OMG .. es funktioniert .. es funktioniert .. Kudos: D In meinem Fall muss ich Viewpager aktuelles Element Objekt entfernen. Das aktuelle Fragment wird jedoch nicht aktualisiert, nachdem alle oben genannten Methoden / Schritte ausprobiert wurden.
Rahul Khurana

3

Ich habe die von Bill Phillips bereitgestellte Lösung leicht an meine Bedürfnisse angepasst

private class PagerAdapter extends FragmentStatePagerAdapter{
    Bundle oBundle;
    FragmentManager oFragmentManager;
    ArrayList<Fragment> oPooledFragments;

public PagerAdapter(FragmentManager fm) {
    super(fm);
    oFragmentManager=fm;

    }
@Override
public int getItemPosition(Object object) {

    Fragment oFragment=(Fragment)object;
    oPooledFragments=new ArrayList<>(oFragmentManager.getFragments());
    if(oPooledFragments.contains(oFragment))
        return POSITION_NONE;
    else
        return POSITION_UNCHANGED;
    } 
}

Damit wird die getItemPosition()Rückgabe POSITION_NONEnur für die Fragmente aufgerufen, die sich aktuell im FragmentManagerWann getItemPositionbefinden. (Beachten Sie, dass dies FragmentStatePagerund die damit ViewPagerverbundenen Elemente in einem Fragment enthalten sind, das nicht in einer Aktivität enthalten ist.)


2

Ich hatte ein ähnliches Problem, möchte aber nicht auf die vorhandenen Lösungen (fest codierte Tag-Namen usw.) vertrauen und konnte die Lösung von M-WaJeEh nicht für mich arbeiten lassen. Hier ist meine Lösung:

Ich behalte Verweise auf die in getItem erstellten Fragmente in einem Array. Dies funktioniert einwandfrei, solange die Aktivität nicht aufgrund von Konfigurationsänderungen oder Speichermangel oder was auch immer zerstört wird (-> Wenn Fragmente zur Aktivität zurückkehren, kehren sie in ihren letzten Zustand zurück, ohne dass 'getItem' erneut aufgerufen wird und somit das Array nicht aktualisiert wird ).

Um dieses Problem zu vermeiden, habe ich instantiateItem (ViewGroup, int) implementiert und mein Array dort wie folgt aktualisiert:

        @Override
    public Object instantiateItem(ViewGroup container, int position) {
        Object o = super.instantiateItem(container, position);
        if(o instanceof FragmentX){
            myFragments[0] = (FragmentX)o;
        }else if(o instanceof FragmentY){
            myFragments[1] = (FragmentY)o;
        }else if(o instanceof FragmentZ){
            myFragments[2] = (FragmentZ)o;
        }
        return o;
    }

Einerseits bin ich froh, dass ich eine Lösung gefunden habe, die für mich funktioniert, und wollte sie mit Ihnen teilen, aber ich wollte auch fragen, ob jemand anderes etwas Ähnliches versucht hat und ob es einen Grund gibt, warum ich es nicht tun sollte mach es so Bisher funktioniert es sehr gut für mich ...


Ich habe in stackoverflow.com/a/23427215/954643 etwas Ähnliches getan , obwohl Sie das Element wahrscheinlich auch aus myFragmentsin entfernen sollten, public void destroyItem(ViewGroup container, int position, Object object)damit Sie keine veraltete Fraging-Referenz abrufen.
Qix

1

Ich verwende die EventBus- Bibliothek, um FragmentInhalte in zu aktualisieren ViewPager. Die Logik ist einfach, genau wie das Dokument von EventBus . Die FragmentPagerAdapterInstanz muss nicht gesteuert werden. Der Code ist hier:

1: Ereignisse definieren

Definieren Sie, welche Nachricht aktualisiert werden soll.

public class UpdateCountEvent {
    public final int count;

    public UpdateCountEvent(int count) {
        this.count = count;
    }
}

2. Bereiten Sie die Abonnenten vor

Schreiben Sie den folgenden Code in das Fragment, das aktualisiert werden muss.

@Override
public void onStart() {
    super.onStart();
    EventBus.getDefault().register(this);
}

@Override
public void onStop() {
    EventBus.getDefault().unregister(this);  
    super.onStop();
}

public void onEvent(UpdateCountEvent event) {//get update message
    Toast.makeText(getActivity(), event.count, Toast.LENGTH_SHORT).show();
}

3.Post-Ereignisse

Schreiben Sie den folgenden Code in einen anderen Activityoder anderen Code Fragment, der aktualisiert werden muss

//input update message
EventBus.getDefault().post(new UpdateCountEvent(count));

1

Wenn Sie FragmentStatePagerAdapter verwenden möchten, besuchen Sie bitte https://code.google.com/p/android/issues/detail?can=2&start=0&num=100&q=&colspec=ID%20Type%20Status%20Owner%20Summary% 20Stars & groupby = & sort = & id = 37990 . Es gibt Probleme mit FragmentStatePagerAdapter, die Ihren Anwendungsfall möglicherweise stören oder nicht.

Außerdem hat Link auch nur wenige Lösungen. Nur wenige können Ihren Anforderungen entsprechen.


Von dieser URL habe ich eine Lösung gefunden. Ich habe den modifizierten FragmentStatePagerAdapter aus diesem Gist verwendet .
Rafael

Cool. Ja. Link hat auch wenige Lösungen.
cgr

1

Ich habe das gleiche Problem gelebt und zu oft gesucht. Eine Antwort im Stackoverflow oder über Google war keine Lösung für mein Problem. Mein Problem war einfach. Ich habe eine Liste, ich zeige diese Liste mit Viewpager. Wenn ich ein neues Element zum Kopf der Liste hinzufüge und den Viewpager aktualisiere, ändert sich nichts. Meine endgültige Lösung war sehr einfach, die jeder verwenden kann. Wenn ein neues Element zur Liste hinzugefügt wird und die Liste aktualisiert werden soll. Setzen Sie zuerst den Viewpager-Adapter auf null, erstellen Sie dann den Adapter neu und setzen Sie i auf den Viewpager.

myVPager.setAdapter(null);
myFragmentAdapter = new MyFragmentAdapter(getSupportFragmentManager(),newList);
myVPager.setAdapter(myFragmentAdapter);

Stellen Sie sicher, dass Ihr Adapter FragmentStatePagerAdapter erweitern muss


1

Hier ist meine Implementierung, die die Informationen von @Bill Phillips One enthält. Die meiste Zeit wird ein Fragment zwischengespeichert, außer wenn sich die Daten geändert haben. Einfach und scheint gut zu funktionieren.

MyFragmentStatePagerAdapter.java

private boolean mIsUpdating = false;
public void setIsUpdating(boolean mIsUpdating) {
        this.mIsUpdating = mIsUpdating;
    }


@Override
public int getItemPosition(@NonNull Object object) {

    if (mIsUpdating) {
        return POSITION_NONE;
    }
    else {
        return super.getItemPosition(object);
    }
}

MyActivity.java

mAdapter.setIsUpdating(true);
mAdapter.notifyDataSetChanged();
mAdapter.setIsUpdating(false);

0

Ich hatte so viele verschiedene Ansätze ausprobiert, dass keiner mein Problem wirklich gelöst hat. Im Folgenden wird beschrieben, wie ich es mit einer Mischung von Lösungen löse, die von Ihnen allen bereitgestellt werden. Vielen Dank an alle.

class PagerAdapter extends FragmentPagerAdapter {

    public boolean flag_refresh=false;

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

    @Override
    public Fragment getItem(int page) {
        FragmentsMain f;
        f=new FragmentsMain();
        f.page=page;
        return f;
    }

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

    @Override
    public int getItemPosition(Object item) {
        int page= ((FragmentsMain)item).page;

        if (page == 0 && flag_refresh) {
            flag_refresh=false;
            return POSITION_NONE;
        } else {
            return super.getItemPosition(item);
        }
    }

    @Override
    public void destroyItem(View container, int position, Object object) {

        ((ViewPager) container).removeView((View) object);
    }
}

Ich möchte Seite 0 erst nach onResume () aktualisieren.

 adapter=new PagerAdapter(getSupportFragmentManager());
 pager.setAdapter(adapter);

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

    if (adapter!=null) {
        adapter.flag_refresh=true;
        adapter.notifyDataSetChanged();
    }
}

In meinem FragmentsMain gibt es eine öffentliche Ganzzahl "Seite", die mir sagen kann, ob es sich um die Seite handelt, die ich aktualisieren möchte.

public class FragmentsMain extends Fragment {

private Cursor cursor;
private static Context context;
public int page=-1;

0

Ich weiß, dass ich zu spät zur Party komme. Ich habe das Problem behoben, indem ich TabLayout#setupWithViewPager(myViewPager);gleich danach anriefFragmentPagerAdapter#notifyDataSetChanged();


0

Diese Lösung funktioniert nicht für alle, aber in meinem Fall ist jedes Fragment in meinem ViewPager eine andere Klasse, und es existiert immer nur eine.

Mit dieser Einschränkung ist diese Lösung sicher und sollte in der Produktion sicher verwendet werden können.

private void updateFragment(Item item) {

    List<Fragment> fragments = getSupportFragmentManager().getFragments();
    for (Fragment fragment : fragments) {

        if (fragment instanceof MyItemFragment && fragment.isVisible()) {
            ((MyItemFragment) fragment).update(item);
        }

    }
}

Wenn Sie mehrere Versionen desselben Fragments haben, können Sie mit derselben Strategie Methoden für diese Fragmente aufrufen, um festzustellen, ob es sich um das Fragment handelt, das Sie aktualisieren möchten.


0

Dies kann für jemanden hilfreich sein. In meinem Fall hat der Ansichtspager beim Einfügen einer neuen Seite zweimal nach der Position eines vorhandenen Fragments gefragt, aber nicht nach der Position des neuen Elements, was zu falschem Verhalten und nicht angezeigten Daten führte.

  1. Kopieren Sie die Quelle für FragmentStatePagerAdapter (scheint seit Jahren nicht mehr aktualisiert worden zu sein).

  2. Überschreiben Sie notifyDataSetChanged ()

    @Override
    public void notifyDataSetChanged() {
        mFragments.clear();
        super.notifyDataSetChanged();
    }
  3. Fügen Sie destroyItem () eine Überprüfung der Integrität hinzu, um Abstürze zu vermeiden:

    if (position < mFragments.size()) {
        mFragments.set(position, null);
    }

0

Ich habe alle obigen Antworten und eine Reihe anderer Beiträge durchgesehen, konnte aber immer noch nichts finden, was für mich funktioniert (mit verschiedenen Fragmenttypen sowie dem dynamischen Hinzufügen und Entfernen von Registerkarten). Der folgende FWIW-Ansatz hat bei mir funktioniert (falls jemand anderes die gleichen Probleme hat).

public class MyFragmentStatePageAdapter extends FragmentStatePagerAdapter {
    private static final String TAB1_TITLE = "Tab 1";
    private static final String TAB2_TITLE = "Tab 2";
    private static final String TAB3_TITLE = "Tab 3";

    private ArrayList<String> titles = new ArrayList<>();
    private Map<Fragment, Integer> fragmentPositions = new HashMap<>();


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


    public void update(boolean showTab1, boolean showTab2, boolean showTab3) {
        titles.clear();

        if (showTab1) {
            titles.add(TAB1_TITLE);
        }
        if (showTab2) {
            titles.add(TAB2_TITLE);
        }
        if (showTab3) {
            titles.add(TAB3_TITLE);
        }
        notifyDataSetChanged();
    }


    @Override
    public int getCount() {
        return titles.size();
    }

    @Override
    public Fragment getItem(int position) {
        Fragment fragment = null;

        String tabName = titles.get(position);
        if (tabName.equals(TAB1_TITLE)) { 
            fragment =  Tab1Fragment.newInstance();
        } else if (tabName.equals(TAB2_TITLE)) {
            fragment =  Tab2Fragment.newInstance();
        } else if (tabName.equals(TAB3_TITLE)) {
            fragment =  Tab3Fragmen.newInstance();
        } 

        ((BaseFragment)fragment).setTitle(tabName);
        fragmentPositions.put(fragment, position);
        return fragment;
    }

    @Override
    public CharSequence getPageTitle(int position) {
        return titles.get(position);
    }

    @Override
    public int getItemPosition(Object item) {
        BaseFragment fragment = (BaseFragment)item;
        String title = fragment.getTitle();
        int position = titles.indexOf(title);

        Integer fragmentPosition = fragmentPositions.get(item);
        if (fragmentPosition != null && position == fragmentPosition) {
            return POSITION_UNCHANGED;
        } else {
            return POSITION_NONE;
        }
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        super.destroyItem(container, position, object);
        fragmentPositions.remove(object);
    }
}

0

Verwenden Sie FragmentStatePagerAdapter anstelle von FragmentPagerAdapter, wenn Sie Fragmente auf Indexbasis neu erstellen oder neu laden möchten. Wenn Sie beispielsweise ein anderes Fragment als FirstFragment neu laden möchten, können Sie die Instanz überprüfen und die Position wie folgt zurückgeben

 public int getItemPosition(Object item) {
    if(item instanceof FirstFragment){
       return 0;
    }
    return POSITION_NONE;
 }

0

Sie müssen das mFragments-Element getItemPosition von instantiateItem ändern.

    if (mFragments.size() > position) {
        Fragment f = mFragments.get(position);

        if (f != null) {
            int newPosition = getItemPosition(f);
            if (newPosition == POSITION_UNCHANGED) {
                return f;
            } else if (newPosition == POSITION_NONE) {
                mFragments.set(position, null);
            } else {
                mFragments.set(newPosition, f);
            }
        }
    }

Basierend auf AndroidX FragmentStatePagerAdapter.java, da sich mFragmentsdie Position der Elemente beim Aufruf von notifyDataSetChanged () nicht ändert.

Quelle: https://github.com/cuichanghao/infivt/blob/master/library/src/main/java/cc/cuichanghao/library/FragmentStatePagerChangeableAdapter.java

Beispiel: https://github.com/cuichanghao/infivt/blob/master/app/src/main/java/cc/cuichanghao/infivt/MainActivityChangeablePager.kt

Sie können dieses Projekt ausführen, um die Arbeitsweise zu bestätigen. https://github.com/cuichanghao/infivt

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.