Rimuovi Fragment Page da ViewPager in Android


138

Sto cercando di aggiungere e rimuovere dinamicamente frammenti da un ViewPager, aggiungendo lavori senza problemi, ma la rimozione non funziona come previsto.

Ogni volta che voglio rimuovere l'elemento corrente, l'ultimo viene rimosso.

Ho anche provato a utilizzare un FragmentStatePagerAdapter o restituire POSITION_NONE nel metodo getItemPosition dell'adattatore.

Che cosa sto facendo di sbagliato?

Ecco un esempio di base:

MainActivity.java

public class MainActivity extends FragmentActivity implements TextProvider {

    private Button mAdd;
    private Button mRemove;
    private ViewPager mPager;

    private MyPagerAdapter mAdapter;

    private ArrayList<String> mEntries = new ArrayList<String>();

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

        mEntries.add("pos 1");
        mEntries.add("pos 2");
        mEntries.add("pos 3");
        mEntries.add("pos 4");
        mEntries.add("pos 5");

        mAdd = (Button) findViewById(R.id.add);
        mRemove = (Button) findViewById(R.id.remove);
        mPager = (ViewPager) findViewById(R.id.pager);

        mAdd.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
                addNewItem();
            }
        });

        mRemove.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
                removeCurrentItem();
            }
        });

        mAdapter = new MyPagerAdapter(this.getSupportFragmentManager(), this);

        mPager.setAdapter(mAdapter);

    }

    private void addNewItem() {
        mEntries.add("new item");
        mAdapter.notifyDataSetChanged();
    }

    private void removeCurrentItem() {
        int position = mPager.getCurrentItem();
        mEntries.remove(position);
        mAdapter.notifyDataSetChanged();
    }

    @Override
    public String getTextForPosition(int position) {
        return mEntries.get(position);
    }
    @Override
    public int getCount() {
        return mEntries.size();
    }


    private class MyPagerAdapter extends FragmentPagerAdapter {

        private TextProvider mProvider;

        public MyPagerAdapter(FragmentManager fm, TextProvider provider) {
            super(fm);
            this.mProvider = provider;
        }

        @Override
        public Fragment getItem(int position) {
            return MyFragment.newInstance(mProvider.getTextForPosition(position));
        }

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

    }

}

TextProvider.java

public interface TextProvider {
    public String getTextForPosition(int position);
    public int getCount();
}

MyFragment.java

public class MyFragment extends Fragment {

    private String mText;

    public static MyFragment newInstance(String text) {
        MyFragment f = new MyFragment(text);
        return f;
    }

    public MyFragment() {
    }

    public MyFragment(String text) {
        this.mText = text;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {

        View root = inflater.inflate(R.layout.fragment, container, false);

        ((TextView) root.findViewById(R.id.position)).setText(mText);

        return root;
    }

}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <Button
        android:id="@+id/add"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="add new item" />

    <Button
        android:id="@+id/remove"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="remove current item" />

    <android.support.v4.view.ViewPager
        android:id="@+id/pager"
        android:layout_width="match_parent"
        android:layout_height="0dip"
        android:layout_weight="1" />

</LinearLayout>

fragment.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <TextView
        android:id="@+id/position"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:textSize="35sp" />

</LinearLayout>

2
+1 per il codice sorgente
wizurd

è corretto usare un costruttore non predefinito in MyFragment.java??
codeKiller,

Risposte:


95

La soluzione di Louth non era abbastanza per far funzionare le cose per me, poiché i frammenti esistenti non venivano distrutti. Motivato da questa risposta , ho scoperto che la soluzione è quella di sostituire il getItemId(int position)metodo di FragmentPagerAdapterassegnazione di un nuovo ID univoco ogni volta che si è verificato un cambiamento nella posizione prevista di un frammento.

Codice sorgente:

private class MyPagerAdapter extends FragmentPagerAdapter {

    private TextProvider mProvider;
    private long baseId = 0;

    public MyPagerAdapter(FragmentManager fm, TextProvider provider) {
        super(fm);
        this.mProvider = provider;
    }

    @Override
    public Fragment getItem(int position) {
        return MyFragment.newInstance(mProvider.getTextForPosition(position));
    }

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


    //this is called when notifyDataSetChanged() is called
    @Override
    public int getItemPosition(Object object) {
        // refresh all fragments when data set changed
        return PagerAdapter.POSITION_NONE;
    }


    @Override
    public long getItemId(int position) {
        // give an ID different from position when position has been changed
        return baseId + position;
    }

    /**
     * Notify that the position of a fragment has been changed.
     * Create a new ID for each position to force recreation of the fragment
     * @param n number of items which have been changed
     */
    public void notifyChangeInPosition(int n) {
        // shift the ID returned by getItemId outside the range of all previous fragments
        baseId += getCount() + n;
    }
}

Ora, ad esempio se elimini una singola scheda o apporti qualche modifica all'ordine, dovresti chiamare notifyChangeInPosition(1)prima di chiamare notifyDataSetChanged(), il che assicurerà che tutti i frammenti vengano ricreati.

Perché questa soluzione funziona

Sostituzione di getItemPosition ():

Quando notifyDataSetChanged()viene chiamato, l'adattatore chiama il notifyChanged()metodo a ViewPagercui è collegato. I ViewPagercontrolli allora il valore restituito dalla scheda di getItemPosition()per ogni articolo, la rimozione di quelle voci che di ritorno POSITION_NONE(vedere il codice sorgente ) e poi ripopolamento.

Sostituzione di getItemId ():

Ciò è necessario per impedire all'adattatore di ricaricare il vecchio frammento quando ViewPagersi ripopola. Puoi facilmente capire perché questo funziona guardando il codice sorgente per instantiateItem () in FragmentPagerAdapter.

    final long itemId = getItemId(position);

    // Do we already have this fragment?
    String name = makeFragmentName(container.getId(), itemId);
    Fragment fragment = mFragmentManager.findFragmentByTag(name);
    if (fragment != null) {
        if (DEBUG) Log.v(TAG, "Attaching item #" + itemId + ": f=" + fragment);
        mCurTransaction.attach(fragment);
    } else {
        fragment = getItem(position);
        if (DEBUG) Log.v(TAG, "Adding item #" + itemId + ": f=" + fragment);
        mCurTransaction.add(container.getId(), fragment,
                makeFragmentName(container.getId(), itemId));
    }

Come puoi vedere, il getItem()metodo viene chiamato solo se il gestore di frammenti non trova frammenti esistenti con lo stesso ID. A me sembra un bug che i vecchi frammenti siano ancora attaccati anche dopo che notifyDataSetChanged()è stato chiamato, ma la documentazione per ViewPagerafferma chiaramente che:

Nota che questa classe è attualmente in fase di progettazione e sviluppo. L'API probabilmente cambierà negli aggiornamenti successivi della libreria di compatibilità, richiedendo modifiche al codice sorgente delle app quando vengono compilate rispetto alla versione più recente.

Quindi speriamo che la soluzione indicata qui non sia necessaria in una versione futura della libreria di supporto.


1
Grazie mille per questa risposta! È stato estenuante provare a risolvere questo problema senza usare FragmentStatePagerAdapter. Ottima spiegazione anche. Hai trovato un modo per far scattare getItem quando torni al frammento? Ho provato a usare la tua intelligente notificaDataSetChanged () in vari posti (ad esempio onStop) senza successo
u2tall

Non capisco davvero la tua domanda ... Per favore pubblica una nuova domanda incluso il codice sorgente se non riesci a capirlo.
Tim Rae,

1
2016, questa soluzione alternativa è ancora necessaria per far notifyDataSetChanged()funzionare.
Sottomarino Sebastian

Wow! Spiegazione incredibile. Ho avuto un viewpager piuttosto complesso in cui rimuovo e inserisco elementi dinamicamente e non riesco a farlo funzionare senza capire tutto questo.
Rupps,

Ho ancora bisogno di questa soluzione alternativa per sostituire il vecchio frammento. Dang it.
kopikaokao,

291

ViewPager non rimuove i frammenti con il codice sopra perché carica diverse viste (o frammenti nel tuo caso) in memoria. Oltre alla vista visibile, carica anche la vista su entrambi i lati di quella visibile. Ciò fornisce lo scorrimento regolare da una vista all'altra che rende ViewPager così interessante.

Per ottenere l'effetto desiderato, devi fare un paio di cose.

  1. Cambia FragmentPagerAdapter in FragmentStatePagerAdapter. La ragione di ciò è che FragmentPagerAdapter manterrà per sempre tutte le viste che carica in memoria. Dove FragmentStatePagerAdapter dispone di viste che non rientrano nelle viste correnti e attraversabili.

  2. Sostituire il metodo dell'adattatore getItemPosition (mostrato di seguito). Quando chiamiamo mAdapter.notifyDataSetChanged();ViewPager interroga l'adattatore per determinare cosa è cambiato in termini di posizionamento. Usiamo questo metodo per dire che tutto è cambiato, quindi rielaborare tutto il posizionamento della vista.

Ed ecco il codice ...

private class MyPagerAdapter extends FragmentStatePagerAdapter {

    //... your existing code

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

}

1
Ok, questo sembra essere la soluzione più semplice per questo problema. Sebbene un paio di altre soluzioni alternative siano discusse sulle segnalazioni di bug qui: code.google.com/p/android/issues/detail?id=19110 e qui: code.google.com/p/android/issues/detail?id= 19001
Subin Sebastian

@Louth: sto usando ViewPager con TabsAdapter, come esteso da FragmentStatePagerAdapter (ad esempio nella pagina di riferimento di Google: developer.android.com/reference/android/support/v4/view/… ). La mia domanda di follow-up è: come si elimina una scheda in primo luogo? Grazie.
gcl1,

4
@Louth Quando chiamiamo notifyDataSetChanged () il viewpager crea nuovi frammenti, ma quelli più vecchi sono ancora in memoria. E il mio problema è; ricevono una chiamata per il loro evento onOptionsItemSelected. Come posso liberarmi dei vecchi frammenti?
Syloc

1
Grazie, la semplice sostituzione di FragmentPagerAdapter in FragmentStatePagerAdapter ha risolto il mio problema. Dopodiché rimuovo solo AllViews () su ViewPager e ricarica nuovamente i frammenti in modo dinamico.
Michal,

17
Perché restituisci sempre POSITION_NONE invece di guardare davvero se una determinata vista / frammento / oggetto esiste ancora ed è valida? Questo metodo dovrebbe rispondere alla domanda "ehi, posso riutilizzare questo?" - e sempre dicendo "No!" significa solo ricreazione di tutto! Ciò non è necessario in molti casi.
Zordid,

14

la mia soluzione di lavoro per rimuovere la pagina di frammenti dal pager di visualizzazione

public class MyFragmentAdapter extends FragmentStatePagerAdapter {

    private ArrayList<ItemFragment> pages;

    public MyFragmentAdapter(FragmentManager fragmentManager, ArrayList<ItemFragment> pages) {
        super(fragmentManager);
        this.pages = pages;
    }

    @Override
    public Fragment getItem(int index) {
        return pages.get(index);
    }

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

    @Override
    public int getItemPosition(Object object) {
        int index = pages.indexOf (object);

        if (index == -1)
            return POSITION_NONE;
        else
            return index;
    }
}

E quando devo rimuovere alcune pagine per indice, lo faccio

pages.remove(position); // ArrayList<ItemFragment>
adapter.notifyDataSetChanged(); // MyFragmentAdapter

Ecco l'inizializzazione del mio adattatore

MyFragmentAdapter adapter = new MyFragmentAdapter(getSupportFragmentManager(), pages);
viewPager.setAdapter(adapter);

Per favore, condividi il codice per l'aggiunta del frammento?
VVB

Ma aggiungendolo si riflette lentamente dopo due volte / tre volte
VVB

se non stai rimuovendo gli articoli, restituisci solo gli articoli, niente di più ... stai testando su VM o dispositivo reale?
Vasil Valchev,

Prova altre soluzioni, risposte con più voti positivi. Il pager di visualizzazione ha un sistema di incassi molto complesso per migliorare le prestazioni di scorrimento, quindi questi hack non sono gestiti dal pager di visualizzazione originale con lo scopo ...
Vasil Valchev

11

Il frammento deve essere già rimosso ma il problema era lo stato di salvataggio di viewpager

Provare

myViewPager.setSaveFromParentEnabled(false);

Niente ha funzionato ma questo ha risolto il problema!

Saluti !


2

Ho avuto l'idea di copiare semplicemente il codice sorgente android.support.v4.app.FragmentPagerAdpaterin una classe personalizzata denominata CustumFragmentPagerAdapter. Questo mi ha dato la possibilità di modificare in instantiateItem(...)modo tale che ogni volta che viene chiamato, rimuove / distrugge il frammento attualmente collegato prima di aggiungere il nuovo frammento ricevuto dal getItem()metodo.

Basta modificare il instantiateItem(...)modo seguente:

@Override
public Object instantiateItem(ViewGroup container, int position) {
    if (mCurTransaction == null) {
        mCurTransaction = mFragmentManager.beginTransaction();
    }
    final long itemId = getItemId(position);

    // Do we already have this fragment?
    String name = makeFragmentName(container.getId(), itemId);
    Fragment fragment = mFragmentManager.findFragmentByTag(name);

    // remove / destroy current fragment
    if (fragment != null) {
        mCurTransaction.remove(fragment);
    }

    // get new fragment and add it
    fragment = getItem(position);
    mCurTransaction.add(container.getId(), fragment,    makeFragmentName(container.getId(), itemId));

    if (fragment != mCurrentPrimaryItem) {
        fragment.setMenuVisibility(false);
        fragment.setUserVisibleHint(false);
    }

    return fragment;
}

1

Puoi combinare entrambi in meglio:

private class MyPagerAdapter extends FragmentStatePagerAdapter {

    //... your existing code

    @Override
    public int getItemPosition(Object object){

      if(Any_Reason_You_WantTo_Update_Positions) //this includes deleting or adding pages
 return PagerAdapter.POSITION_NONE;
    }
else
return PagerAdapter.POSITION_UNCHANGED; //this ensures high performance in other operations such as editing list items.

}

Funziona solo se si sta eliminando la scheda all'estrema destra, per eliminare una scheda interna o la scheda di sinistra, è necessario riordinare le schede e restituire la nuova posizione come indicato nella documentazione developer.android.com/reference/android/support/v4 / view /…
BitByteDog

1

Per i futuri lettori!

Ora puoi usare ViewPager2 per aggiungere dinamicamente, rimuovere frammenti dal viewpager.

Citando il modulo come riferimento API

ViewPager2 sostituisce ViewPager, affrontando la maggior parte dei punti deboli del suo predecessore, incluso il supporto del layout da destra a sinistra, l'orientamento verticale, raccolte di frammenti modificabili, ecc.

Date un'occhiata a MutableCollectionFragmentActivity.ktin googlesample / android-viewpager2 per un esempio di aggiungere, rimuovere i frammenti in modo dinamico dalla viewpager.


Per tua informazione:

articoli:

Riferimento API

Note di rilascio

Repo campioni: https://github.com/googlesamples/android-viewpager2


Ho 2 frammenti di mainframment e di altro. Sto aggiungendo altroframmento dopo ogni 5a posizione e non è nell'arraylist, quindi come rimuoverlo se non è nella lista? Sto usando viewpager2
b devloper

0

La risposta di Louth funziona bene. Ma non penso sempre che POSITION_NONE sia una buona idea. Perché POSITION_NONE significa che il frammento dovrebbe essere distrutto e verrà creato un nuovo frammento. Puoi verificarlo nella funzione dataSetChanged nel codice sorgente di ViewPager.

        if (newPos == PagerAdapter.POSITION_NONE) {
            mItems.remove(i);
            i--;
            ... not related code
            mAdapter.destroyItem(this, ii.position, ii.object);

Quindi penso che faresti meglio a usare un arraylist di weakReference per salvare tutti i frammenti che hai creato. E quando aggiungi o rimuovi qualche pagina, puoi ottenere la posizione giusta dal tuo arraylist.

 public int getItemPosition(Object object) {
    for (int i = 0; i < mFragmentsReferences.size(); i ++) {
        WeakReference<Fragment> reference = mFragmentsReferences.get(i);
        if (reference != null && reference.get() != null) {
            Fragment fragment = reference.get();
            if (fragment == object) {
                return i;
            }
        }
    }
    return POSITION_NONE;
}

Secondo i commenti, getItemPosition viene chiamato quando la vista host tenta di determinare se la posizione di un elemento è cambiata. E il valore restituito indica la sua nuova posizione.

Ma questo non è abbastanza. Abbiamo ancora un passo importante da fare. Nel codice sorgente di FragmentStatePagerAdapter, esiste un array chiamato "mFragments" che memorizza nella cache i frammenti che non vengono distrutti. E nella funzione instantiateItem.

if (mFragments.size() > position) {
        Fragment f = mFragments.get(position);
        if (f != null) {
            return f;
        }
    }

Ha restituito direttamente il frammento memorizzato nella cache quando rileva che il frammento memorizzato nella cache non è nullo. Quindi c'è un problema. Dall'esempio, eliminiamo una pagina in posizione 2, in primo luogo, rimuoviamo quel frammento dal nostro arraylist di riferimento. quindi in getItemPosition restituirà POSITION_NONE per quel frammento, quindi quel frammento verrà distrutto e rimosso da "mFragments".

 mFragments.set(position, null);

Ora il frammento nella posizione 3 sarà nella posizione 2. Verrà chiamato un istanteItem con la posizione param 3. Al momento, il terzo elemento in "mFramgents" non è nullo, quindi tornerà direttamente. Ma in realtà quello che ha restituito è il frammento nella posizione 2. Quindi, quando trasformiamo in pagina 3, troveremo lì una pagina vuota.

Per aggirare questo problema. Il mio consiglio è che è possibile copiare il codice sorgente di FragmentStatePagerAdapter nel proprio progetto e quando si aggiungono o rimuovono operazioni, è necessario aggiungere e rimuovere elementi nella matrice "mFragments".

Le cose saranno più semplici se usi PagerAdapter invece di FragmentStatePagerAdapter. In bocca al lupo.


0

aggiungere o rimuovere frammenti in viewpager in modo dinamico.
Chiamare setupViewPager (viewPager) all'avvio dell'attività. Per caricare frammenti diversi, chiama setupViewPagerCustom (viewPager). ad es. alla chiamata clic sul pulsante: setupViewPagerCustom (viewPager);

    private void setupViewPager(ViewPager viewPager)
{
    ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager());
    adapter.addFrag(new fragmnet1(), "HOME");
    adapter.addFrag(new fragmnet2(), "SERVICES");

    viewPager.setAdapter(adapter);
}

private void setupViewPagerCustom(ViewPager viewPager)
{

    ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager());

    adapter.addFrag(new fragmnet3(), "Contact us");
    adapter.addFrag(new fragmnet4(), "ABOUT US");


    viewPager.setAdapter(adapter);
}

//Viewpageradapter, handles the views

static class ViewPagerAdapter extends FragmentStatePagerAdapter
{
    private final List<Fragment> mFragmentList = new ArrayList<>();
    private final List<String> mFragmentTitleList = new ArrayList<>();

    public ViewPagerAdapter(FragmentManager manager){
        super(manager);
    }

    @Override
    public Fragment getItem(int position) {
        return mFragmentList.get(position);
    }

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

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

    public void addFrag(Fragment fragment, String title){
        mFragmentList.add(fragment);
        mFragmentTitleList.add(title);
    }

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

0

Ho avuto dei problemi con FragmentStatePagerAdapter. Dopo aver rimosso un oggetto:

  • c'era un altro oggetto usato per una posizione (un oggetto che non apparteneva alla posizione ma ad una posizione accanto ad essa)
  • o alcuni frammenti non sono stati caricati (nella pagina era visibile solo uno sfondo vuoto)

Dopo molti esperimenti, ho trovato la seguente soluzione.

public class SomeAdapter extends FragmentStatePagerAdapter {

    private List<Item> items = new ArrayList<Item>();

    private boolean removing;

    @Override
    public Fragment getItem(int position) {
        ItemFragment fragment = new ItemFragment();
        Bundle args = new Bundle();
        // use items.get(position) to configure fragment
        fragment.setArguments(args);
        return fragment;
    }

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

    @Override
    public int getItemPosition(Object object) {
        if (removing) {
            return PagerAdapter.POSITION_NONE;
        }

        Item item = getItemOfFragment(object);

        int index = items.indexOf(item);
        if (index == -1) {
            return POSITION_NONE;
        } else {
            return index;
        }
    }

   public void addItem(Item item) {
        items.add(item);
        notifyDataSetChanged();
    }

    public void removeItem(int position) {
        items.remove(position);

        removing = true;
        notifyDataSetChanged();
        removing = false;
    }
}

Questa soluzione utilizza un hack solo in caso di rimozione di un elemento. Altrimenti (ad es. Quando si aggiunge un articolo) mantiene la pulizia e le prestazioni di un codice originale.

Ovviamente, dall'esterno dell'adattatore, si chiama solo addItem / removeItem, non è necessario chiamare notificationDataSetChanged ().


0

Prova questa soluzione. Ho usato il databinding per la vista vincolante. È possibile utilizzare la funzione comune "findViewById ()".

public class ActCPExpense extends BaseActivity implements View.OnClickListener,  {
private static final String TAG = ActCPExpense.class.getSimpleName();
private Context mContext;
private ActCpLossBinding mBinding;

private ViewPagerAdapter adapter;



@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    try {
        setContentView(R.layout.act_cp_loss);
        mBinding = DataBindingUtil.setContentView(this, R.layout.act_cp_loss);
        mContext = ActCPExpense.this;
        initViewsAct();


    } catch (Exception e) {
        LogUtils.LOGE(TAG, e);
    }

}


private void initViewsAct() {

    adapter = new ViewPagerAdapter(getSupportFragmentManager());
    adapter.addFragment(FragmentCPPayee.newInstance(), "Title");
    mBinding.viewpager.setAdapter(adapter);
    mBinding.tab.setViewPager(mBinding.viewpager);



}



@Override
public boolean onOptionsItemSelected(MenuItem itemActUtility) {
    int i = itemActUtility.getItemId();
    if (i == android.R.id.home) {
        onBackPressed();

    } 

    return super.onOptionsItemSelected(itemActUtility);
}

@Override
public void onClick(View view) {
    super.onClick(view);
    int id = view.getId();
    if (id == R.id.btnAdd) {
        addFragment();

    } else if (id == R.id.btnDelete) {
        removeFragment();

    }


}
    private void addFragment(){
    adapter.addFragment(FragmentCPPayee.newInstance("Title");
    adapter.notifyDataSetChanged();
    mBinding.tab.setViewPager(mBinding.viewpager);
}
private void removeFragment(){
    adapter.removeItem(mBinding.viewpager.getCurrentItem());
    mBinding.tab.setViewPager(mBinding.viewpager);
}


class ViewPagerAdapter extends FragmentStatePagerAdapter {
    private final List<Fragment> mFragmentList = new ArrayList<>();
    private final List<String> mFragmentTitleList = new ArrayList<>();


    public ViewPagerAdapter(FragmentManager manager) {
        super(manager);
    }

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


    @Override
    public Fragment getItem(int position) {
        return mFragmentList.get(position);
    }

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


    public void addFragment(Fragment fragment, String title) {
        mFragmentList.add(fragment);
        mFragmentTitleList.add(title);

    }

    public void removeItem(int pos) {

        destroyItem(null, pos, mFragmentList.get(pos));
        mFragmentList.remove(pos);
        mFragmentTitleList.remove(pos);
        adapter.notifyDataSetChanged();
        mBinding.viewpager.setCurrentItem(pos - 1, false);
    }

    @Override
    public CharSequence getPageTitle(int position) {
        return "Title " + String.valueOf(position + 1);
    }
}


}

0

Ho aggiunto una funzione "clearFragments" e ho usato quella funzione per cancellare l'adattatore prima di impostare i nuovi frammenti. Questo chiama le azioni di rimozione appropriate dei frammenti. La mia classe pagerAdapter:

private class ChartPagerAdapter extends FragmentPagerAdapter{
    private ArrayList<Fragment> fragmentList;

    ChartPagerAdapter(FragmentManager fm){
        super(fm);
        fragmentList = new ArrayList<>();
    }

    void setFragments(ArrayList<? extends Fragment> fragments){
        fragmentList.addAll(fragments);
    }

    void clearFragments(){
        for(Fragment fragment:fragmentList)
            getChildFragmentManager().beginTransaction().remove(fragment).commit();
        fragmentList.clear();
    }

    @Override
    public Fragment getItem(int i) {
        return fragmentList.get(i);
    }

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

}

0

ho risolto questo problema con questi passaggi

1- usa FragmentPagerAdapter

2- in ogni frammento creare un ID casuale

fragment.id = new Random().nextInt();

3- sovrascrivere getItemPosition nell'adattatore

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

GetItemId 4-override nell'adattatore

 @Override
public long getItemId(int position) {
    return mDatasetFragments.get(position).id;
}

5- ora il codice di eliminazione è

 adapter.mDatasetFragments.remove(< item to delete position >);
 adapter.notifyDataSetChanged();

questo ha funzionato per me spero aiuto


0

Il mio codice della versione finale, risolto TUTTI i bug. Mi ci sono voluti 3 giorni

Aggiornato il 2020/07/18: avevo modificato molto il codice sorgente e risolto tanti bug, ma non prometto che funzionerà ancora oggi.

https://github.com/lin1987www/FragmentBuilder/blob/master/commonLibrary/src/main/java/android/support/v4/app/FragmentStatePagerAdapterFix.java

public class FragmentStatePagerAdapterFix extends PagerAdapter {
    private static final String TAG = FragmentStatePagerAdapterFix.class.getSimpleName();
    private static final boolean DEBUG = false;

    private WeakReference<FragmentActivity> wrFragmentActivity;
    private WeakReference<Fragment> wrParentFragment;
    private final FragmentManager mFragmentManager;
    private FragmentTransaction mCurTransaction = null;

    protected ArrayList<Fragment> mFragments = new ArrayList<>();
    protected ArrayList<FragmentState> mFragmentStates = new ArrayList<>();
    protected ArrayList<String> mFragmentTags = new ArrayList<>();
    protected ArrayList<String> mFragmentClassNames = new ArrayList<>();
    protected ArrayList<Bundle> mFragmentArgs = new ArrayList<>();

    private Fragment mCurrentPrimaryItem = null;
    private boolean[] mTempPositionChange;

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

    public FragmentActivity getFragmentActivity() {
        return wrFragmentActivity.get();
    }

    public Fragment getParentFragment() {
        return wrParentFragment.get();
    }

    public FragmentStatePagerAdapterFix(FragmentActivity activity) {
        mFragmentManager = activity.getSupportFragmentManager();
        wrFragmentActivity = new WeakReference<>(activity);
        wrParentFragment = new WeakReference<>(null);
    }

    public FragmentStatePagerAdapterFix(Fragment fragment) {
        mFragmentManager = fragment.getChildFragmentManager();
        wrFragmentActivity = new WeakReference<>(fragment.getActivity());
        wrParentFragment = new WeakReference<>(fragment);
    }

    public void add(Class<? extends android.support.v4.app.Fragment> fragClass) {
        add(fragClass, null, null);
    }

    public void add(Class<? extends android.support.v4.app.Fragment> fragClass, Bundle args) {
        add(fragClass, args, null);
    }

    public void add(Class<? extends android.support.v4.app.Fragment> fragClass, String tag) {
        add(fragClass, null, tag);
    }

    public void add(Class<? extends android.support.v4.app.Fragment> fragClass, Bundle args, String tag) {
        add(fragClass, args, tag, getCount());
    }

    public void add(Class<? extends android.support.v4.app.Fragment> fragClass, Bundle args, String tag, int position) {
        mFragments.add(position, null);
        mFragmentStates.add(position, null);
        mFragmentTags.add(position, tag);
        mFragmentClassNames.add(position, fragClass.getName());
        mFragmentArgs.add(position, args);
        mTempPositionChange = new boolean[getCount()];
    }

    public void remove(int position) {
        if (position < getCount()) {
            mTempPositionChange = new boolean[getCount()];
            for (int i = position; i < mTempPositionChange.length; i++) {
                mTempPositionChange[i] = true;
            }
            mFragments.remove(position);
            mFragmentStates.remove(position);
            mFragmentTags.remove(position);
            mFragmentClassNames.remove(position);
            mFragmentArgs.remove(position);
        }
    }

    public void clear(){
        mFragments.clear();
        mFragmentStates.clear();
        mFragmentTags.clear();
        mFragmentClassNames.clear();
        mFragmentArgs.clear();
    }

    @Override
    public void startUpdate(ViewGroup container) {
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        Fragment fragment;
        // If we already have this item instantiated, there is nothing
        // to do.  This can happen when we are restoring the entire pager
        // from its saved state, where the fragment manager has already
        // taken care of restoring the fragments we previously had instantiated.
        fragment = mFragments.get(position);
        if (fragment != null) {
            return fragment;
        }
        if (mCurTransaction == null) {
            mCurTransaction = mFragmentManager.beginTransaction();
        }
        FragmentState fs = mFragmentStates.get(position);
        if (fs != null) {
            fragment = fs.instantiate(getFragmentActivity(), getParentFragment());
            // Fix bug
            // http://stackoverflow.com/questions/11381470/classnotfoundexception-when-unmarshalling-android-support-v4-view-viewpagersav
            if (fragment.mSavedFragmentState != null) {
                fragment.mSavedFragmentState.setClassLoader(fragment.getClass().getClassLoader());
            }
        }
        if (fragment == null) {
            fragment = Fragment.instantiate(getFragmentActivity(), mFragmentClassNames.get(position), mFragmentArgs.get(position));
        }
        if (DEBUG) {
            Log.v(TAG, "Adding item #" + position + ": f=" + fragment);
        }
        fragment.setMenuVisibility(false);
        fragment.setUserVisibleHint(false);
        mFragments.set(position, fragment);
        mFragmentStates.set(position, null);
        mCurTransaction.add(container.getId(), fragment, mFragmentTags.get(position));
        return fragment;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        Fragment fragment = (Fragment) object;
        if (mCurTransaction == null) {
            mCurTransaction = mFragmentManager.beginTransaction();
        }
        if (DEBUG) {
            Log.v(TAG, "Removing item #" + position + ": f=" + object
                    + " v=" + ((Fragment) object).getView());
        }
        if (position < getCount()) {
            FragmentState fragmentState = new FragmentState(fragment);
            Fragment.SavedState savedState = mFragmentManager.saveFragmentInstanceState(fragment);
            if (savedState != null) {
                fragmentState.mSavedFragmentState = savedState.mState;
            }
            mFragmentStates.set(position, fragmentState);
            mFragments.set(position, null);
        }
        mCurTransaction.remove(fragment);
    }

    @Override
    public void setPrimaryItem(ViewGroup container, int position, Object object) {
        Fragment fragment = (Fragment) object;
        if (fragment != mCurrentPrimaryItem) {
            if (mCurrentPrimaryItem != null) {
                mCurrentPrimaryItem.setMenuVisibility(false);
                mCurrentPrimaryItem.setUserVisibleHint(false);
            }
            if (fragment != null) {
                fragment.setMenuVisibility(true);
                fragment.setUserVisibleHint(true);
            }
            mCurrentPrimaryItem = fragment;
        }
    }

    @Override
    public void finishUpdate(ViewGroup container) {
        if (mCurTransaction != null) {
            mCurTransaction.commitAllowingStateLoss();
            mCurTransaction = null;
            mFragmentManager.executePendingTransactions();
            // Fix: Fragment is added by transaction. BUT didn't add to FragmentManager's mActive.
            for (Fragment fragment : mFragments) {
                if (fragment != null) {
                    fixActiveFragment(mFragmentManager, fragment);
                }
            }
        }
    }

    @Override
    public boolean isViewFromObject(View view, Object object) {
        return ((Fragment) object).getView() == view;
    }

    @Override
    public Parcelable saveState() {
        Bundle state = null;
        // 目前顯示的 Fragments
        for (int i = 0; i < mFragments.size(); i++) {
            Fragment f = mFragments.get(i);
            if (f != null && f.isAdded()) {
                if (state == null) {
                    state = new Bundle();
                }
                String key = "f" + i;
                mFragmentManager.putFragment(state, key, f);
            }
        }
        if (mFragmentStates.size() > 0) {
            if (state == null) {
                state = new Bundle();
            }
            FragmentState[] fs = new FragmentState[mFragmentStates.size()];
            mFragmentStates.toArray(fs);
            state.putParcelableArray("states_fragment", fs);
        }
        return state;
    }

    @Override
    public void restoreState(Parcelable state, ClassLoader loader) {
        if (state != null) {
            Bundle bundle = (Bundle) state;
            bundle.setClassLoader(loader);
            Parcelable[] fs = bundle.getParcelableArray("states_fragment");
            mFragments.clear();
            mFragmentStates.clear();
            mFragmentTags.clear();
            mFragmentClassNames.clear();
            mFragmentArgs.clear();
            if (fs != null) {
                for (int i = 0; i < fs.length; i++) {
                    FragmentState fragmentState = (FragmentState) fs[i];
                    mFragmentStates.add(fragmentState);
                    if (fragmentState != null) {
                        mFragmentArgs.add(fragmentState.mArguments);
                        mFragmentTags.add(fragmentState.mTag);
                        mFragmentClassNames.add(fragmentState.mClassName);
                    } else {
                        mFragmentArgs.add(null);
                        mFragmentTags.add(null);
                        mFragmentClassNames.add(null);
                    }
                    mFragments.add(null);
                }
            }
            Iterable<String> keys = bundle.keySet();
            for (String key : keys) {
                if (key.startsWith("f")) {
                    int index = Integer.parseInt(key.substring(1));
                    Fragment f = mFragmentManager.getFragment(bundle, key);
                    if (f != null) {
                        f.setMenuVisibility(false);
                        mFragments.set(index, f);
                        mFragmentArgs.set(index, f.mArguments);
                        mFragmentTags.set(index, f.mTag);
                        mFragmentClassNames.set(index, f.getClass().getName());
                    } else {
                        Log.w(TAG, "Bad fragment at key " + key);
                    }
                }
            }
            // If restore will change
            notifyDataSetChanged();
        }
    }

    public static void fixActiveFragment(FragmentManager fragmentManager, Fragment fragment) {
        FragmentManagerImpl fm = (FragmentManagerImpl) fragmentManager;
        if (fm.mActive != null) {
            int index = fragment.mIndex;
            Fragment origin = fm.mActive.get(index);
            if (origin != null) {
                if ((origin.mIndex != fragment.mIndex) || !(origin.equals(fragment))) {
                    Log.e(TAG,
                            String.format("fixActiveFragment: Not Equal! Origin: %s %s, Fragment: %s $s",
                                    origin.getClass().getName(), origin.mIndex,
                                    fragment.getClass().getName(), fragment.mIndex
                            ));
                }
            }
            fm.mActive.set(index, fragment);
        }
    }

    // Fix
    // http://stackoverflow.com/questions/10396321/remove-fragment-page-from-viewpager-in-android
    @Override
    public int getItemPosition(Object object) {
        int index = mFragments.indexOf(object);
        if (index < 0) {
            return PagerAdapter.POSITION_NONE;
        }
        boolean isPositionChange = mTempPositionChange[index];
        int result = PagerAdapter.POSITION_UNCHANGED;
        if (isPositionChange) {
            result = PagerAdapter.POSITION_NONE;
        }
        return result;
    }
}

Ho 2 frammenti di mainframment e di altro. Sto aggiungendo altroframmento dopo ogni 5a posizione e non è nell'arraylist, quindi come rimuoverlo se non è nella lista? Sto usando viewpager2
b devloper

collegamento github non funzionante
b devloper

-1

Potresti semplicemente ignorare il destroyItemmetodo

@Override
public void destroyItem(ViewGroup container, int position, Object object) {
    fragmentManager.beginTransaction().remove((Fragment) object).commitNowAllowingStateLoss();
}

-9

Spero che questo possa aiutare quello che vuoi.

private class MyPagerAdapter extends FragmentStatePagerAdapter {

    //... your existing code

    @Override
    public int getItemPosition(Object object){
        return PagerAdapter.POSITION_UNCHANGED;
    }
}

9
questo è esattamente l'opposto della risposta accettata - come può funzionare ??
Zordid,
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.