FragmentPagerAdapter non rimuove correttamente gli elementi (frammenti)

Ho implementato il FragmentPagerAdapter e utilizzando un elenco per tenere tutti i frammenti per visualizzare il mio ViewPager. su addItem () faccio semplicemente aggiungere un frammento istanziato e quindi call notifyDataSetChanged (). Non sono sicuro se questo sia necessario o no.

Il mio problema è semplicemente … cominciare con il frammento 1

  • android emulator non inizierà su ubuntu 12.04 dopo aver eseguito da ssh
  • Android: memorizzare il nome utente e la password?
  • Come dividere il layout lineare in due colonne?
  • Come installare / sostituire su Android senza utilizzare Eclipse
  • HttpResponse usando emissione android: eseguire causa sempre l'exception?
  • Il metodo di ContentValues ​​non viene deriso
  • [Fragment 1] 

    aggiungere un nuovo frammento 2

     [Fragment 1] [Fragment 2] 

    rimuovere il frammento 2

     [Fragment 1] 

    aggiungere nuovo frammento 3

     [Fragment 1] [Fragment 2] 

    Quando si aggiungono nuovi frammenti tutto sembra grande. Una volta rimosso un frammento e quindi aggiunto un frammento appena istanziato il vecchio frammento è ancora visualizzato. Quando vado a .getClass.getName () mi sta dando il nome di Frammento 3 ma vedo ancora Frammento 2.

    Credo che questo potrebbe essere un problema con istantiateItem o simili, ma ho pensato che l'adattatore whereva gestire quello per noi. Qualsiasi suggerimento sarebbe molto apprezzato.

    codice dell'adattatore …

     public class MyPagerAdapter extends FragmentPagerAdapter { public final ArrayList<Fragment> screens2 = new ArrayList<Fragment>(); private Context context; public MyPagerAdapter(FragmentManager fm, Context context) { super(fm); this.context = context; } public void removeType(String name){ for(Fragment f: screens2){ if(f.getClass().getName().contains(name)){ screens2.remove(f); return; } } this.notifyDataSetChanged(); } public boolean addSt(String tag, Class<?> clss, Bundle args){ if(clss==null) return false; if(!clss.getName().contains("St")) return false; if(!args.containsKey("cId")) return false; boolean has = false; boolean hasAlready = false; for(Fragment tab: screens2){ if(tab.getClass().getName().contains("St")){ has = true; if(tab.getArguments().containsKey("cId")) if(tab.getArguments().getLong("cId") == args.getLong("cId")){ hasAlready = true; } if(!hasAlready){ // exists but is different so replace screens2.remove(tab); this.addScreen(tag, clss, args, C.PAGE_ST); // if returned true then it notifies dataset return true; } } hasAlready = false; } if(!has){ // no st yet exist in adapter this.addScreen(tag, clss, args, C.PAGE_ST); return true; } return false; } public boolean removeCCreate(){ this.removeType("Create"); return false; } @Override public int getItemPosition(Object object) { return POSITION_NONE; //To make notifyDataSetChanged() do something } public void addCCreate(){ this.removeCCreate(); Log.w("addding c", " "); this.addScreen("Create C", CreateCFragment.class, null, C.PAGE_CREATE_C); } public void addScreen(String tag, Class<?> clss, Bundle args, int type){ if(clss!=null){ screens2.add(Fragment.instantiate(context, clss.getName(), args)); } } @Override public int getCount() { return screens2.size(); } @Override public Fragment getItem(int position) { return screens2.get(position); } } 

    Mi rendo conto che il codice usa alcuni "ghetto" per determinare il tipo di frammento ma ho scritto questo codice rigorosamente per la funzionalità di test. Qualsiasi aiuto o idee sarebbe grande, come sembra che non molte persone avventurarsi nel mondo di FragmentPagerAdapters.

  • Tabella degli alfabeti Android Visualizza la list dei contatti come elenco delle applicazioni
  • Come bind a una port inferiore a 1024 in Android?
  • Cursore Android sopra l'elenco generico di oggetti business?
  • EditText con tastiera morbida e button "Indietro"
  • Corretto: "Android: Rileva la messa a fuoco / il colore compresso"
  • Come get il button Next sul softkeyboard android al posto di Go Button in phonegap
  • 8 Solutions collect form web for “FragmentPagerAdapter non rimuove correttamente gli elementi (frammenti)”

    Ho avuto lo stesso problema, e la mia soluzione ha sovrastato il metodo "destroyItem" come segue.

     @Override public void destroyItem(ViewGroup container, int position, Object object) { FragmentManager manager = ((Fragment)object).getFragmentManager(); FragmentTransaction trans = manager.beginTransaction(); trans.remove((Fragment)object); trans.commit(); } 

    È lavoro per me, qualcuno ha altre soluzioni?

    aggiornato:

    Ho trovato che il codice frammentato ha rimosso inutile, quindi ho aggiunto una condizione per evitarlo.

     @Override public void destroyItem(ViewGroup container, int position, Object object) { if (position >= getCount()) { FragmentManager manager = ((Fragment) object).getFragmentManager(); FragmentTransaction trans = manager.beginTransaction(); trans.remove((Fragment) object); trans.commit(); } } 

    Aggiornato questo post e ha incluso la mia soluzione (se qualcuno può migliorare mi faccia sapere)

    Ok ho risolto il mio problema in un modo hackish, ma sì che sta funzionando;). Se qualcuno può migliorare la mia soluzione, fathemes sapere. Per la mia nuova soluzione ora utilizzo un CustomFragmentStatePagerAdapter ma non salva lo stato come dovrebbe e memorizza tutti i frammenti in un elenco. Ciò può causare un problema di memory se l'utente dispone di più di 50 frammenti, come fa il normale FragmentPagerAdapter. Sarebbe bello se qualcuno possa aggiungere la cosa dello Stato alla mia soluzione senza rimuovere le mie correzioni. Grazie.

    Quindi ecco il mio CustomFragmentStatePagerAdapter.java

     package com.tundem.webLab.Adapter; import java.util.ArrayList; import android.os.Bundle; import android.os.Parcelable; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentTransaction; import android.support.v4.view.PagerAdapter; import android.util.Log; import android.view.View; import android.view.ViewGroup; public abstract class CustomFragmentStatePagerAdapter extends PagerAdapter { private static final String TAG = "FragmentStatePagerAdapter"; private static final boolean DEBUG = false; private final FragmentManager mFragmentManager; private FragmentTransaction mCurTransaction = null; public ArrayList<Fragment.SavedState> mSavedState = new ArrayList<Fragment.SavedState>(); public ArrayList<Fragment> mFragments = new ArrayList<Fragment>(); private Fragment mCurrentPrimaryItem = null; public CustomFragmentStatePagerAdapter(FragmentManager fm) { mFragmentManager = fm; } /** * Return the Fragment associated with a specified position. */ public abstract Fragment getItem(int position); @Override public void startUpdate(ViewGroup container) {} @Override public Object instantiateItem(ViewGroup container, int position) { // 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. // DONE Remove of the add process of the old stuff /* if (mFragments.size() > position) { Fragment f = mFragments.get(position); if (f != null) { return f; } } */ if (mCurTransaction == null) { mCurTransaction = mFragmentManager.beginTransaction(); } Fragment fragment = getItem(position); if (DEBUG) Log.v(TAG, "Adding item #" + position + ": f=" + fragment); if (mSavedState.size() > position) { Fragment.SavedState fss = mSavedState.get(position); if (fss != null) { try // DONE: Try Catch { fragment.setInitialSavedState(fss); } catch (Exception ex) { // Schon aktiv (kA was das heißt xD) } } } while (mFragments.size() <= position) { mFragments.add(null); } fragment.setMenuVisibility(false); mFragments.set(position, fragment); mCurTransaction.add(container.getId(), fragment); return fragment; } @Override public void destroyItem(ViewGroup container, int position, Object object) { Fragment fragment = (Fragment) object; if (mCurTransaction == null) { mCurTransaction = mFragmentManager.beginTransaction(); } mCurTransaction.remove(fragment); /*if (mCurTransaction == null) { mCurTransaction = mFragmentManager.beginTransaction(); } if (DEBUG) Log.v(TAG, "Removing item #" + position + ": f=" + object + " v=" + ((Fragment) * object).getView()); while (mSavedState.size() <= position) { mSavedState.add(null); } mSavedState.set(position, mFragmentManager.saveFragmentInstanceState(fragment)); * 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); } if (fragment != null) { fragment.setMenuVisibility(true); } mCurrentPrimaryItem = fragment; } } @Override public void finishUpdate(ViewGroup container) { if (mCurTransaction != null) { mCurTransaction.commitAllowingStateLoss(); mCurTransaction = null; mFragmentManager.executePendingTransactions(); } } @Override public boolean isViewFromObject(View view, Object object) { return ((Fragment) object).getView() == view; } @Override public Parcelable saveState() { Bundle state = null; if (mSavedState.size() > 0) { state = new Bundle(); Fragment.SavedState[] fss = new Fragment.SavedState[mSavedState.size()]; mSavedState.toArray(fss); state.putParcelableArray("states", fss); } for (int i = 0; i < mFragments.size(); i++) { Fragment f = mFragments.get(i); if (f != null) { if (state == null) { state = new Bundle(); } String key = "f" + i; mFragmentManager.putFragment(state, key, f); } } return state; } @Override public void restoreState(Parcelable state, ClassLoader loader) { if (state != null) { Bundle bundle = (Bundle) state; bundle.setClassLoader(loader); Parcelable[] fss = bundle.getParcelableArray("states"); mSavedState.clear(); mFragments.clear(); if (fss != null) { for (int i = 0; i < fss.length; i++) { mSavedState.add((Fragment.SavedState) fss[i]); } } 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) { while (mFragments.size() <= index) { mFragments.add(null); } f.setMenuVisibility(false); mFragments.set(index, f); } else { Log.w(TAG, "Bad fragment at key " + key); } } } } } } 

    Ecco il mio normale FragmentAdapter.java

     package com.tundem.webLab.Adapter; import java.util.LinkedList; import java.util.List; import android.support.v4.app.FragmentManager; import com.tundem.webLab.fragments.BaseFragment; import com.viewpagerindicator.TitleProvider; public class FragmentAdapter extends CustomFragmentStatePagerAdapter implements TitleProvider { public List<BaseFragment> fragments = new LinkedList<BaseFragment>(); private int actPage; public FragmentAdapter(FragmentManager fm) { super(fm); } public void setActPage(int actPage) { this.actPage = actPage; } public void addItem(BaseFragment fragment) { // TODO if exists don't open / change to that tab fragments.add(fragment); } public BaseFragment getActFragment() { return getItem(getActPage()); } public int getActPage() { return actPage; } @Override public BaseFragment getItem(int position) { if (position < getCount()) { return fragments.get(position); } else return null; } @Override public int getCount() { return fragments.size(); } @Override public String getTitle(int position) { return fragments.get(position).getTitle(); } @Override public int getItemPosition(Object object) { return POSITION_NONE; } } 

    E questo è il modo in cui elimino un frammento. (So ​​che è un po 'più di solo .remove ()). Essere liberi di migliorare la mia soluzione, puoi anche aggiungere questo codice da qualche parte nell'adattatore in modo sì. È all'utente che cerca di implementarlo. Lo uso nel mio TabHelper.java (una class che gestisce tutte le operazioni di tabulazione come eliminare, aggiungere, …)

      int act = Cfg.mPager.getCurrentItem(); Cfg.mPager.removeAllViews(); Cfg.mAdapter.mFragments.remove(act); try { Cfg.mAdapter.mSavedState.remove(act); } catch (Exception ex) {/* Already removed */} try { Cfg.mAdapter.fragments.remove(act); } catch (Exception ex) {/* Already removed */} Cfg.mAdapter.notifyDataSetChanged(); Cfg.mIndicator.notifyDataSetChanged(); 

    Descrizione del Cfg. cosa. Salvo il riferimento a quegli oggetti in una class cfg, quindi posso sempre utilizzarli senza la necessità di una speciale Factory.java …

    Si. spero che sia stato in grado di aiutare. Sentitevi liberi di migliorare questo, ma fathemes sapere in modo da poter migliorare anche il mio codice.

    Grazie.

    Se mi mancava qualsiasi codice, faccia sapere.


    La mia vecchia risposta funziona anche ma solo se hai diversi frammenti. FileFragment, WebFragment … Non se si utilizza una di queste tipologie di frammenti due volte.

    Ho avuto pseudo lavoro per ora. È una soluzione veramente sporca e sto ancora cercando una migliore. Per favore aiuto.

    Ho cambiato il codice, where elimino una scheda a questa:

      public static void deleteActTab() { //We set this on the indicator, NOT the pager int act = Cfg.mPager.getCurrentItem(); Cfg.mAdapter.removeItem(act); List<BaseFragment> frags = new LinkedList<BaseFragment>(); frags = Cfg.mAdapter.fragments; Cfg.mPager = (ViewPager)Cfg.act.findViewById(R.id.pager); Cfg.mPager.setAdapter(Cfg.mAdapter); Cfg.mIndicator.setViewPager(Cfg.mPager); Cfg.mAdapter.fragments = frags; if(act > 0) { Cfg.mPager.setCurrentItem(act-1); Cfg.mIndicator.setCurrentItem(act-1); } Cfg.mIndicator.notifyDataSetChanged(); } 

    Se qualcuno può migliorare questo codice, fathemes sapere. Se qualcuno può dirci la risposta reale per quel problema. per favore aggiungila qui. Ci sono molte persone che sperimentano questo problema. Ho aggiunto una reputazione di 50 per colui che lo risolve. Posso anche dare una donazione a chi lo risolve.

    Grazie

    Prendendo "il meglio di entrambi i mondi" (voglio dire le risposte di @Tericky Shih e @mikepenz) abbiamo breve e semplice:

     public class MyPagerAdapter extends FragmentPagerAdapter { public ArrayList<Fragment> fragments = new ArrayList<Fragment>(); ... @Override public void destroyItem(ViewGroup container, int position, Object object) { super.destroyItem(container, position, object); if (position >= getCount()) fm.beginTransaction().remove((Fragment) object).commit(); } @Override public int getItemPosition(Object object) { if (fragments.contains(object)) return fragments.indexOf(object); else return POSITION_NONE; } } 

    La differenza principale è che se un frammento non viene modificato, non è necessario distruggere la sua vista e non dovrà restituire POSITION_NONE . Allo stesso tempo, ho incontrato una situazione in cui ViewPager stava tenendo un riferimento a un elemento già distrutto, pertanto il controllo if (fragments.contains(object)) aiuta a determinare se l'elemento non è più necessario.

    Ho avuto una situazione simile al tuo. Ho recentemente bisogno di aggiungere e rimuovere frammenti dal ViewPager. Nella prima modalità, ho frammenti 0, 1 e 2 e nella seconda modalità ho frammenti 0 e 3. Voglio che il frammento 0 sia lo stesso per entrambi i modi e tieni informazioni.

    Tutto quello che wherevo fare era sostituire FragmentPagerAdapter.getItemId per assicurarmi che ho restituito un numero univoco per each frammento diverso: l'impostazione predefinita è quella di restituire la posizione. Ho anche dovuto impostare l'adattatore in ViewPager ancora una volta – una nuova istanza functionrà, ma lo riponiamo alla stessa istanza. L'impostazione dell'adattatore comport la visualizzazione di ViewPager rimuovendo tutte le viste e tentando di aggiungerle nuovamente.

    Tuttavia, il trucco è che l'adattatore chiama getItem solo quando vuole istanziare il frammento – non each volta che lo mostra. Questo è perché vengono memorizzati nella cache e li guarda dalla "posizione" restituita da getItemId.

    Immagina di avere tre frammenti (0, 1 e 2) e vuoi rimuovere "1". Se si ritorna "posizione" per getItemId, la rimozione di Frammento 1 non functionrà perché quando si tenta di mostrare Frammento 2 dopo l'eliminazione di Frammento 1, il pager / adattatore pensa che sia già in possesso del Frammento per quella "posizione" e continuerà a visualizzare Fragmento 1 .

    FYI: Ho provato a notifyDataSetChanged invece di impostare l'adattatore ma non funzionava per me.

    In primo luogo, l'esempio override getItemId e quello che ho fatto per il mio getItem:

     public class SectionsPagerAdapter extends FragmentPagerAdapter { ... @Override public long getItemId(int position) { // Mode 1 uses Fragments 0, 1 and 2. Mode 2 uses Fragments 0 and 3 if ( mode == 2 && position == 1 ) return 3; return position; } @Override public Fragment getItem(int position) { if ( mode == 1 ) { switch (position) { case 0: return <<fragment 0>>; case 1: return <<fragment 1>>; case 2: return <<fragment 2>>; } } else // Mode 2 { switch (position) { case 0: return <<fragment 0>>; case 1: return <<fragment 3>>; } } return null; } } 

    Ora la modifica della modalità:

     private void modeChanged(int newMode) { if ( newMode == mode ) return; mode = newMode; // Calling mSectionsPagerAdapter.notifyDataSetChanged() is not enough here mViewPager.setAdapter(mSectionsPagerAdapter); } 

    Forse questa risposta ti aiuta.

    Utilizzare FragmentStatePagerAdapter invece di FragmentPagerAdapter .

    Poiché FragmentPagerAdapter non distory le viste. Per ulteriori informazioni, leggere questa risposta .

    Non funzionava per me. La mia soluzione è stata inserita FragmentStatePagerAdapter.java nel mio progetto, denominata FragmentStatePagerAdapter2.java. In destroyItem (), ho cambiato un po 'in base ai log degli errori. A partire dal

     // mFragments.set(position, null); 

    a

     if (position < mFragments.size())mFragments.remove(position); 

    Forse non hai lo stesso problema, basta controllare il log.Hope questo aiuta qualcuno!

    Dopo un sacco di tentativi, ho fatto funzionare in modo che rimuova o attacca un terzo frammento alla posizione finale correttamente.

     Object fragments[] = new Object[3]; int mItems = 2; MyAdapter mAdapter; ViewPager mPager; public void addFragment(boolean bool) { mAdapter.startUpdate(mPager); if (!bool) { mAdapter.destroyItem(mPager, 2, fragments[2]); mItems = 2; fNach = false; } else if (bool && !fNach){ mItems = 3; mAdapter.instantiateItem(mPager,2); fNach = true; } mAdapter.finishUpdate(mPager); mAdapter.notifyDataSetChanged(); } public class MyAdapter extends FragmentPagerAdapter { MyAdapter(FragmentManager fm) { super(fm); } @Override public int getCount() { return mItems; } @Override public CharSequence getPageTitle(int position) { ... (code for the PagerTitleStrip) } @Override public Fragment getItem(int position) { Fragment f = null; switch (position) { case 0: f = new Fragment1(); break; case 1: f = new Fragment2(); break; case 2: f = new Fragment3(); break; } return f; } @Override public Object instantiateItem(ViewGroup container, int position) { Object o = super.instantiateItem(container,position); fragments[position] = o; return o; } @Override public void destroyItem(ViewGroup container, int position, Object object) { super.destroyItem(container, position, object); System.out.println("Destroy item " + position); if (position >= getCount()) { FragmentManager manager = ((Fragment) object).getFragmentManager(); FragmentTransaction ft = manager.beginTransaction(); ft.remove((Fragment) object); ft.commit(); } } } 

    Alcuni chiarimenti: per get il riferimento dell'object per call destroyItem, ho memorizzato gli oggetti restituiti da istantiateItem in un Array. Quando si aggiungono o rimuovono frammenti, è necessario annunciarlo con startUpdate, finishUpdate e notifyDataSetChanged. Il numero di elementi deve essere modificato manualmente, poiché l'aggiunta aumenta e l'istanza, getItem lo crea. Per la cancellazione, si chiama destroyItem e in questo codice è essenziale la posizione> = mItems, perché destroyItem viene chiamato anche se un frammento esce dalla cache. Non si desidera eliminarlo. L'unica cosa che non funziona è l'animation che si muove. Dopo aver rimosso l'ultima pagina, l'animation "non può scorrere a sinistra" non viene ripristinata correttamente nella nuova ultima pagina. Se lo scorri, viene visualizzata una pagina vuota e si ritorna indietro.

    Il vero problema è che FragmentPagerAdapter utilizza la posizione del frammento nell'elenco come ID. Quindi, se aggiungi una nuova list o semplicemente rimuovi gli elementi "istantiateItem" troverà frammenti diversi per i nuovi elementi nell'elenco …

     @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); 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)); } if (fragment != mCurrentPrimaryItem) { fragment.setMenuVisibility(false); fragment.setUserVisibleHint(false); } return fragment; } 

    e

      private static String makeFragmentName(int viewId, long id) { return "android:switcher:" + viewId + ":" + id; } 

    e

      * Return a unique identifier for the item at the given position. * <p> * <p>The default implementation returns the given position. * Subclasss should override this method if the positions of items can change.</p> * * @param position Position within this adapter * @return Unique identifier for the item at position */ public long getItemId(int position) { return position; } 
    L'Android è un fan Android di Google, tutto su telefoni Android, Android Wear, Android Dev e applicazioni Android Games e così via.