Aggiornare ViewPager in modo dinamico?


316

Non riesco ad aggiornare il contenuto in ViewPager.

Qual è l'uso corretto dei metodi instantiateItem () e getItem () nella classe FragmentPagerAdapter?

Stavo usando solo getItem () per istanziare e restituire i miei frammenti:

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

Questo ha funzionato bene. Solo che non posso cambiare il contenuto.

Quindi ho trovato questo: ViewPager PagerAdapter non aggiorna la vista

"Il mio approccio è quello di utilizzare il metodo setTag () per qualsiasi vista istanziata nel metodo instantiateItem ()"

Ora voglio implementare instantiateItem () per farlo. Ma non so cosa devo restituire (il tipo è Object) e qual è la relazione con getItem (int position)?

Ho letto il riferimento :

  • abstract pubblico Fragment getItem (int position)

    Restituisce il frammento associato a una posizione specificata.

  • public Object instantiateItem (contenitore ViewGroup, posizione int)

    Crea la pagina per la posizione specificata. L'adattatore è responsabile per l'aggiunta della vista al contenitore indicato qui, anche se deve assicurarsi che ciò avvenga solo quando ritorna da finishUpdate (ViewGroup). parametri

    container La vista contenente in cui verrà mostrata la pagina. position La posizione della pagina da istanziare.

    ritorna

    Restituisce un oggetto che rappresenta la nuova pagina. Non è necessario che sia una vista, ma può essere un altro contenitore della pagina.

ma ancora non capisco.

Ecco il mio codice Sto usando il pacchetto di supporto 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;
    }
}

Qui c'è anche qualcuno con un problema simile, nessuna risposta http://www.mail-archive.com/android-developers@googlegroups.com/msg200477.html


Per comprendere meglio i componenti coinvolti, potrebbe essere utile leggere il mio tutorial su un ViewPager a schede con cronologia di navigazione posteriore separata. Viene fornito con un esempio funzionante su GitHub e risorse aggiuntive: medium.com/@nilan/…
marktani


Puoi controllare questo esempio in GitHub . Spero che questo esempio ti aiuti.
Cabezas,

Buona soluzione semplice: stackoverflow.com/a/35450575/2162226
Gene Bo

Google ha recentemente introdotto ViewPager2 che semplifica gli aggiornamenti dinamici di frammento / vista. Puoi vedere gli esempi su github.com/googlesamples/android-viewpager2 o questa risposta su stackoverflow.com/a/57393414/1333448
Yves Delerm,

Risposte:


605

Quando si utilizza FragmentPagerAdapter o FragmentStatePagerAdapter, è meglio trattare esclusivamente getItem()e non toccare instantiateItem()affatto. L' interfaccia instantiateItem()- destroyItem()- isViewFromObject()su PagerAdapter è un'interfaccia di livello inferiore che FragmentPagerAdapter utilizza per implementare l' getItem()interfaccia molto più semplice .

Prima di entrare in questo, dovrei chiarirlo

se si desidera cambiare i frammenti effettivi che vengono visualizzati, è necessario evitare FragmentPagerAdapter e utilizzare FragmentStatePagerAdapter.

Una versione precedente di questa risposta ha commesso l'errore di usare FragmentPagerAdapter per il suo esempio - che non funzionerà perché FragmentPagerAdapter non distrugge mai un frammento dopo che è stato visualizzato per la prima volta.

Non consiglio l' setTag()e findViewWithTag()soluzione fornita nel post si è collegato. Come hai scoperto, usando setTag()efindViewWithTag() non funziona con i frammenti, quindi non è una buona corrispondenza.

La soluzione giusta è ignorare getItemPosition(). Quando notifyDataSetChanged()viene chiamato, ViewPager chiamagetItemPosition() tutti gli elementi nel suo adattatore per vedere se devono essere spostati in una posizione diversa o rimossi.

Per impostazione predefinita, getItemPosition()restituisce POSITION_UNCHANGED, il che significa "Questo oggetto va bene dove si trova, non distruggerlo o rimuoverlo." ritornandoPOSITION_NONE risolve il problema dicendo invece "Questo oggetto non è più un elemento che sto visualizzando, rimuovilo". Quindi ha l'effetto di rimuovere e ricreare ogni singolo elemento nell'adattatore.

Questa è una soluzione completamente legittima! Questa correzione fa sì che notificationDataSetChanged si comporti come un normale adattatore senza riciclo della vista. Se si implementa questa correzione e le prestazioni sono soddisfacenti, si parte per le gare. Lavoro fatto.

Se hai bisogno di prestazioni migliori, puoi usare getItemPosition()un'implementazione più elaborata . Ecco un esempio per un cercapersone che crea frammenti da un elenco di stringhe:

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

Con questa implementazione, verranno visualizzati solo i frammenti che visualizzano nuovi titoli. Eventuali frammenti che visualizzano titoli che sono ancora nell'elenco verranno invece spostati nella loro nuova posizione nell'elenco e verranno distrutti frammenti con titoli che non sono più nell'elenco.

Cosa succede se il frammento non è stato ricreato, ma deve comunque essere aggiornato? Gli aggiornamenti di un frammento vivente sono gestiti meglio dal frammento stesso. Questo è il vantaggio di avere un frammento, dopotutto: è il suo controller. Un frammento può aggiungere un ascoltatore o un osservatore a un altro oggetto in onCreate(), quindi rimuoverlo onDestroy(), gestendo così gli aggiornamenti stessi. Non è necessario inserire tutto il codice di aggiornamento all'internogetItem() come in un adattatore per ListView o altri tipi di AdapterView.

Un'ultima cosa - solo perché FragmentPagerAdapter non distrugge un frammento non significa che getItemPosition sia completamente inutile in un FragmentPagerAdapter. È ancora possibile utilizzare questo callback per riordinare i frammenti in ViewPager. Tuttavia, non li rimuoverà mai completamente dal FragmentManager.


4
Non funziona, non aggiorna ancora nulla ... ha pubblicato il mio codice.
Ixx,

64
Bene, ho capito il problema: devi usare FragmentStatePagerAdapter anziché FragmentPagerAdapter. FragmentPagerAdapter non rimuove mai i frammenti dal FragmentManager, si stacca e li attacca. Controlla i documenti per ulteriori informazioni - in generale, vuoi usare FragmentPagerAdapter solo per frammenti permanenti. Ho modificato il mio esempio con la correzione.
Bill Phillips,

1
Lo rivedrò ad un certo punto in seguito ... grazie per la risposta. E poi accetto la tua risposta se funziona. Prima del tuo ultimo commento ho implementato per rimuovere e aggiungere un nuovo ViewPager ogni volta e funziona. Non è un'ottima soluzione, ma non ho tempo per cambiarla.
Ixx,

12
Hai ragione, cambiare la classe dell'adattatore in FragmentStatePagerAdapter ha risolto tutti i miei problemi. Grazie!
Ixx,

2
Anche quando ritorno POSITION_NONE, durante l'utilizzo FragmentStatePagerAdapter, vedo che rimuove il mio frammento e crea la nuova istanza. Ma la nuova istanza riceve un pacchetto salvatoInstanceState con lo stato di un vecchio frammento. Come posso distruggere completamente il frammento in modo che Bundle sia nullo quando viene creato di nuovo?
Cantato l'

142

Invece di tornare POSITION_NONEda getItemPosition()e causare ricreazione a piena vista, fai questo:

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

I tuoi frammenti dovrebbero implementare l' UpdateableFragmentinterfaccia:

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

e l'interfaccia:

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

La tua classe di dati:

public class UpdateData {
    //whatever you want here
}

1
Questo significa che anche MainActivity deve implementare l'interfaccia ??? Per favore, aiutatemi qui. Nella mia attuale implementazione, sto usando la mia classe MainActivity come comunicatore tra i frammenti, quindi ero confuso su questa soluzione.
Rakeeb Rajbhandari,

1
Ok ho risposto in un altro posto con la piena attuazione in cui sto aggiornando Fragmentin modo dinamico, avere uno sguardo: stackoverflow.com/questions/19891828/...
M-Wajeeh

@ M-WaJeEh forse qualche esempio per favore !!! ho una lista con l'elenco delle città e dopo aver selezionato la città apre viewpager (3 pagine) con informazioni su questa città. funziona bene. ma dopo aver selezionato un'altra viewpager di città mostra solo che la d page on refreshes the 1-st page only after swiping pages? thw 2pagina 3 d è sempre vuota. grazie
SERG

4
tra tutte le cose che ho visto negli ultimi 2,5 giorni della mia vita lavorativa .. questa è l'unica che ha funzionato per me .. tutti i tipi di arigato, grazie, spaciba, sukran .. n.
Orkun Ozen,

2
Mother of Dragons !, funziona come un incantesimo, l'unica soluzione ha funzionato per me ... grazie mille !!
Sebastián Guerrero,

24

per quelli che ancora affrontano lo stesso problema che ho affrontato prima quando ho un ViewPagercon 7 frammenti. il valore predefinito per questi frammenti carica il contenuto inglese dal APIservizio, ma qui il problema è che voglio cambiare la lingua dall'attività delle impostazioni e dopo aver terminato l'attività delle impostazioni, voglio che l' ViewPagerattività principale aggiorni i frammenti in modo che corrispondano alla selezione della lingua dell'utente e caricino il contenuto arabo se l'utente sceglie l'arabo qui cosa ho fatto per lavorare dalla prima volta

1- È necessario utilizzare FragmentStatePagerAdapter come indicato sopra.

2- su mainActivity ho la precedenza su onResume e ho fatto quanto segue

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

3-i ha ignorato l' getItemPosition()mPagerAdapter e lo ha restituito POSITION_NONE.

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

funziona come il fascino


1
Funziona tranne in una situtaion. Quando si elimina l'ultimo elemento in viewPager. Quindi non rimuove dall'elenco.
Vlado Pandžić,

Stavo aggiungendo in modo dinamico frammenti sia a sinistra che a destra del frammento corrente nel viewpager. Questo ha funzionato! Grazie.
h8pathak,

15

Ho riscontrato questo problema e finalmente risolto oggi, quindi scrivo ciò che ho appreso e spero che sia utile per chi è nuovo su Android ViewPagere aggiorno come faccio. Sto usando FragmentStatePagerAdapternel livello API 17 e attualmente ho solo 2 frammenti. Penso che ci debba essere qualcosa di non corretto, per favore correggimi, grazie. inserisci qui la descrizione dell'immagine

  1. I dati serializzati devono essere caricati in memoria. Questo può essere fatto usando un CursorLoader/ AsyncTask/ Thread. Il caricamento automatico dipende dal codice. Se si utilizza a CursorLoader, viene caricato automaticamente poiché esiste un osservatore di dati registrato.

  2. Dopo aver chiamato viewpager.setAdapter(pageradapter), l'adattatore getCount()viene costantemente chiamato per creare frammenti. Quindi, se i dati vengono caricati, getCount()può restituire 0, quindi non è necessario creare frammenti fittizi per i dati non visualizzati.

  3. Dopo che i dati sono stati caricati, l'adattatore non costruirà automaticamente i frammenti poiché getCount()è ancora 0, quindi possiamo impostare il numero di dati effettivamente caricato da restituire getCount(), quindi chiamare l'adattatore notifyDataSetChanged(). ViewPagerinizia a creare frammenti (solo i primi 2 frammenti) dai dati in memoria. È fatto prima che notifyDataSetChanged()venga restituito. Quindi ViewPagerha i frammenti giusti di cui hai bisogno.

  4. Se i dati nel database e nella memoria sono entrambi aggiornati (write through), oppure vengono aggiornati solo i dati in memoria (write back) o vengono aggiornati solo i dati nel database. Negli ultimi due casi, se i dati non vengono caricati automaticamente dal database in memoria (come menzionato sopra). L' ViewPageradattatore per cercapersone gestisce solo i dati in memoria.

  5. Quindi, quando i dati in memoria vengono aggiornati, dobbiamo solo chiamare l'adattatore notifyDataSetChanged(). Poiché il frammento è già stato creato, l'adattatore onItemPosition()verrà chiamato prima della notifyDataSetChanged()restituzione. Non è necessario fare nulla getItemPosition(). Quindi i dati vengono aggiornati.


non ho trovato nulla da fare dopo aver aggiornato i dati (nella memoria), basta chiamare notifyDataSetChanged()quindi appena aggiornato automaticamente, perché il frammento (con chartview) che uso si aggiornerà da solo. se non lo è, come un frammento statico, onItemPositon()POSITION_NONE
dovrò

Qualcuno ha idea di quale strumento è stato utilizzato per fare quel diagramma?
JuiCe,

12

Prova destroyDrawingCache()su ViewPager dopo notifyDataSetChanged()nel tuo codice.


3
Anch'io. Funziona benissimo grazie. Ho avuto un momento particolarmente difficile perché non sto usando il Fragmentsmio cercapersone. Quindi grazie!
Seth,

11

Dopo ore di frustrazione, mentre provando tutte le soluzioni di cui sopra per superare questo problema e anche cercando molte soluzioni su altre questioni simili come questo , questo e questo , che tutti falliti con me per risolvere questo problema e per rendere il ViewPagerdistruggere il vecchio Fragmente riempire il pagercon la nuova Fragments. Ho risolto il problema come segue:

1) Rendi la ViewPagerclasse estesa FragmentPagerAdaptercome segue:

 public class myPagerAdapter extends FragmentPagerAdapter {

2) Creare un articolo per il ViewPagernegozio che memorizza il titlee il fragmentseguente:

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) Fai in modo che il costruttore del ViewPagertake my FragmentManageristanza lo memorizzi nel mio classcome segue:

private FragmentManager mFragmentManager;
private ArrayList<PagerItem> mPagerItems;

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

4) Creare un metodo per reimpostare i adapterdati con i nuovi dati eliminando direttamente tutti i precedenti fragmentdallo fragmentManagerstesso per fare in modo adapterdi impostare fragmentnuovamente il nuovo dal nuovo elenco come segue:

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) Dal contenitore Activityo Fragmentnon inizializzare nuovamente l'adattatore con i nuovi dati. Impostare i nuovi dati tramite il metodo setPagerItemscon i nuovi dati come segue:

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

Spero possa essere d'aiuto.


mi potete aiutare nel mio problema stackoverflow.com/questions/40149039/…
albert

potete per favore aiutarmi con questo problema stackoverflow.com/questions/41547995/…
viper

10

Per qualche motivo nessuna delle risposte ha funzionato per me, quindi ho dovuto ignorare il metodo restoreState senza chiamare super nel mio fragmentStatePagerAdapter. Codice:

private class MyAdapter extends FragmentStatePagerAdapter {

    // [Rest of implementation]

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

}

1
Dopo aver provato tutto il resto in questa risposta SO, questa è quella che ha funzionato per me. Sto finendo () ing un'attività che sta tornando all'attività che ospita PagerAdapter in questione. Voglio che tutti i miei frammenti vengano ricreati in questo caso perché l'attività che ho appena finito potrebbe aver cambiato lo stato utilizzato per disegnare i frammenti.
JohnnyLambada,

OMG .. funziona .. funziona .. kudos: D Nel mio caso ho bisogno di rimuovere l'oggetto oggetto viewpager corrente. ma il frammento corrente non si aggiorna dopo aver provato tutti i metodi / i passaggi precedenti.
Rahul Khurana,

3

Ho leggermente modificato la soluzione fornita da Bill Phillips per soddisfare le mie esigenze

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

in modo che i getItemPosition()ritorni POSITION_NONEsolo per quei frammenti che sono attualmente nel FragmentManagerquando getItemPositionviene chiamato. (Si noti che questo FragmentStatePagere i ViewPagerrelativi associati sono contenuti in un frammento non in un'attività)


2

Ho avuto un problema simile ma non voglio fidarmi delle soluzioni esistenti (nomi di tag codificati, ecc.) E non sono riuscito a far funzionare la soluzione di M-WaJeEh per me. Ecco la mia soluzione:

Conservo i riferimenti ai frammenti creati in getItem in un array. Funziona bene fino a quando l'attività non viene distrutta a causa di configurationChange o mancanza di memoria o altro (-> quando si ritorna all'attività, i frammenti ritornano al loro ultimo stato senza che venga richiamato 'getItem' e quindi senza aggiornare l'array ).

Per evitare questo problema ho implementato instantiateItem (ViewGroup, int) e ho aggiornato il mio array lì, in questo modo:

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

Quindi, da un lato, sono felice di aver trovato una soluzione che funziona per me e volevo condividerla con te, ma volevo anche chiedere se qualcun altro ha provato qualcosa di simile e se c'è qualche motivo per cui non dovrei farlo così? Finora funziona molto bene per me ...


Ho fatto qualcosa di correlato in stackoverflow.com/a/23427215/954643 , anche se probabilmente dovresti rimuovere anche l'elemento da myFragmentsdentro public void destroyItem(ViewGroup container, int position, Object object)in modo da non prendere un riferimento di frustrazione stantio.
qix

1

Uso la libreria EventBus per aggiornare il Fragmentcontenuto ViewPager. La logica è semplice, proprio come il documento di EventBus come fare . Non è necessario controllare l' FragmentPagerAdapteristanza. Il codice è qui:

1: definire gli eventi

Definire quale messaggio è necessario per l'aggiornamento.

public class UpdateCountEvent {
    public final int count;

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

2. Preparare gli abbonati

Scrivi sotto il codice nel frammento che è necessario aggiornare.

@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. Eventi post

Scrivi sotto il codice in altro Activityo altro Fragmentche deve aggiornare il parametro

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

1

Se desideri utilizzare FragmentStatePagerAdapter, dai un'occhiata a https://code.google.com/p/android/issues/detail?can=2&start=0&num=100&q=&colspec=ID%20Type%20Status%20Owner%20Summary% 20Stelle & groupby = & sort = & id = 37990 . Ci sono problemi con FragmentStatePagerAdapter che possono o meno danneggiare il tuo caso d'uso.

Inoltre, link ha anche poche soluzioni..few potrebbe adattarsi alle tue esigenze.


Da quell'URL ho trovato la soluzione. Ho usato FragmentStatePagerAdapter modificato da questo gist gist.github.com/ypresto/8c13cb88a0973d071a64 nel mio codice e ha iniziato a funzionare come dovrebbe essere.
Rafael,

Freddo. Sì. Link ha anche poche soluzioni.
cgr

1

Ho vissuto lo stesso problema e ho cercato troppe volte. Qualsiasi risposta fornita in StackOverflow o tramite Google non era la soluzione al mio problema. Il mio problema è stato facile. Ho un elenco, mostro questo elenco con viewpager. Quando aggiungo un nuovo elemento in testa all'elenco e aggiorno il viewpager, nulla è cambiato. La mia soluzione finale è stata molto semplice che chiunque può usare. Quando un nuovo elemento viene aggiunto all'elenco e si desidera aggiornare l'elenco. Innanzitutto impostare l'adattatore viewpager su null, quindi ricreare l'adattatore e impostarlo su viewpager.

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

Assicurarsi che l'adattatore deve estendere FragmentStatePagerAdapter


1

Ecco la mia implementazione che incorpora le informazioni di @Bill Phillips One ottiene la memorizzazione nella cache dei frammenti per la maggior parte del tempo, tranne quando i dati sono cambiati. Semplice e sembra funzionare bene.

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

Avevo provato così tanti approcci diversi, nessuno dei quali risolveva davvero il mio problema. Di seguito è riportato come lo risolvo con un mix di soluzioni fornite da tutti voi. Grazie a tutti.

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

Voglio solo aggiornare la pagina 0 dopo onResume ().

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

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

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

Nel mio FragmentsMain, c'è una "pagina" intera pubblica, che può dirmi se è la pagina che voglio aggiornare.

public class FragmentsMain extends Fragment {

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

0

So che sono in ritardo per il Partito. Ho risolto il problema chiamando TabLayout#setupWithViewPager(myViewPager);subito dopoFragmentPagerAdapter#notifyDataSetChanged();


0

Questa soluzione non funzionerà per tutti, ma nel mio caso, ogni frammento nel mio ViewPager è una classe diversa e ne esiste solo una alla volta.

Con questo vincolo, questa soluzione è sicura e dovrebbe essere sicura da usare in produzione.

private void updateFragment(Item item) {

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

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

    }
}

Se hai più versioni dello stesso frammento, puoi utilizzare questa stessa strategia per chiamare i metodi su quei frammenti per determinare se è il frammento che desideri aggiornare.


0

Questo potrebbe essere di aiuto a qualcuno - nel mio caso quando inserivo una nuova pagina il pager della vista chiedeva due volte la posizione di un frammento esistente, ma non chiedeva la posizione del nuovo elemento, causando comportamenti errati e dati non visualizzati.

  1. Copia l'origine di FragmentStatePagerAdapter (sembra non essere stato aggiornato da anni).

  2. Sostituisci notificationDataSetChanged ()

    @Override
    public void notifyDataSetChanged() {
        mFragments.clear();
        super.notifyDataSetChanged();
    }
  3. Aggiungi un controllo di integrità a destroyItem () per prevenire arresti anomali:

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

0

Ho esaminato tutte le risposte sopra e diversi altri post, ma ancora non sono riuscito a trovare qualcosa che abbia funzionato per me (con diversi tipi di frammenti insieme all'aggiunta e alla rimozione dinamiche di schede). Il seguente approccio FWIW è ciò che ha funzionato per me (nel caso in cui qualcun altro abbia gli stessi problemi).

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

Utilizzare FragmentStatePagerAdapter invece di FragmentPagerAdapter se si desidera ricreare o ricaricare il frammento in base all'indice Ad esempio, se si desidera ricaricare frammento diverso da FirstFragment, è possibile controllare l'istanza e restituire la posizione in questo modo

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

0

È necessario modificare l'elemento mFragments di instantiateItem getItemPosition.

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

Basato su AndroidX FragmentStatePagerAdapter.java, poiché mFragmentsla posizione degli elementi non cambia quando si chiama notifyDataSetChanged ().

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

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

È possibile eseguire questo progetto per confermare come lavorare. https://github.com/cuichanghao/infivt

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.