2013-02-13 14 views
12

C'è un altro modo per salvare lo stato del frammento nidificato? O se non dovremmo farlo, perché? Grazie !Impossibile conservare frammenti nidificati

02-13 11:42:43.258: E/AndroidRuntime(7167): java.lang.IllegalStateException: Can't retain fragements that are nested in other fragments 
02-13 11:42:43.258: E/AndroidRuntime(7167):  at android.support.v4.app.Fragment.setRetainInstance(Fragment.java:742) 
+1

Huh. Non ricevo un'eccezione come te. Per me, onCreateView nel genitore ottiene semplicemente un nuovo frammento figlio da findFragmentById. Il che significa che deve essere reinizializzato, il che è molto lento nel caso di un Google MapFragment. :( – aij

+1

Si prega di notare che, questo non è più un problema dopo la correzione in https://code.google.com/p/android/issues/detail?id=197271 –

+1

È rilasciato nella libreria di supporto 24 –

risposta

8

È possibile utilizzare FragmentManager.saveFragmentInstanceState(Fragment) per recuperare uno stato di frammento. Il valore restituito implementa Parcelable, quindi puoi metterlo in un pacchetto.

Per il ripristino, è possibile fornire lo stato dopo aver creato il frammento utilizzando Fragment.setInitialSavedState(Fragment.SavedState).

+0

che ho ottenuto stackoverflowerror quando si tenta questo. Perché? – Zyoo

+26

Inizio del sito !!! :) –

+1

Guardando il codice sorgente, questo non risolve questo problema. 'setRetainInstance' consente di conservare campi di classi arbitrari, anche campi non categorizzabili, non serializzabili. Questo metodo, tuttavia, semplicemente chiama 'onSaveInstanceState (Bundle)' sul tuo frammento, il che significa che tutto ciò che puoi fare è salvare cose che puoi mettere in un Bundle. – Matthias

8

Dal libreria di supporto 20+ (https://code.google.com/p/android/issues/detail?id=74222), c'è un bug con un'istanza ricreazione per Fragments bambino, c'è una correzione per esso - http://ideaventure.blogspot.com.au/2014/10/nested-retained-fragment-lost-state.html

codice dalla pagina web (aggiungere questo al vostro genitore Fragment) -

private FragmentManager childFragmentManager() {//!!!Use this instead of getFragmentManager, support library from 20+, has a bug that doesn't retain instance of nested fragments!!!! 
     if(mRetainedChildFragmentManager == null) { 
      mRetainedChildFragmentManager = getChildFragmentManager(); 
     } 
     return mRetainedChildFragmentManager; 
    } 

    @Override 
    public void onAttach(Activity activity) { 
     super.onAttach(activity); 

     if (mRetainedChildFragmentManager != null) { 
      //restore the last retained child fragment manager to the new 
      //created fragment 
      try { 
       Field childFMField = Fragment.class.getDeclaredField("mChildFragmentManager"); 
       childFMField.setAccessible(true); 
       childFMField.set(this, mRetainedChildFragmentManager); 
      } catch (NoSuchFieldException e) { 
       e.printStackTrace(); 
      } catch (IllegalAccessException e) { 
       e.printStackTrace(); 
      } 
     } 
    } 
@Override 
    public void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setRetainInstance(true); 

    } 
+0

Come sopra, non usare la riflessione per favore. O almeno dimostrare che questo è l'unico modo. – NecipAllef

+6

@NecipAllef, come sopra, se stai dicendo che qualcosa non funziona, offri una soluzione migliore. E downvote solo se sai che esiste. – Luten

2

Problema:mChildFrgamentManager sia ricreata (https://code.google.com/p/android/issues/detail?id=74222)
Soluzione: ritegno mChildFrgamentManager se frammento ha setRetainInstance(true):

@Override 
public void onAttach(Activity activity) { 
    super.onAttach(activity); 

    if (getRetainInstance()) { 
     if (mRetainedChildFragmentManager != null) { 
      try { 
       Field childFMField = Fragment.class.getDeclaredField("mChildFragmentManager"); 
       childFMField.setAccessible(true); 
       childFMField.set(this, mRetainedChildFragmentManager); 
      } catch (NoSuchFieldException e) { 
       e.printStackTrace(); 
      } catch (IllegalAccessException e) { 
       e.printStackTrace(); 
      } 
     } else { 
      mRetainedChildFragmentManager = getChildFragmentManager(); 
     } 
    } 
} 

Attenzione: Con questo codice, setRetainInstace dovrebbe essere chiamato prima onAttach.

P.S: Questa è una versione leggermente migliorata della risposta di @attels.

+0

Come si chiama un metodo prima su Attacco in un frammento ??? –

+0

@akshayrajkore cosa significa "prima"? È un metodo '' onAttach'' di Fragment. – Luten

+0

Non utilizzare la riflessione! – NecipAllef

2

Questo non è più un limite con l'ultima libreria di supporto, dopo questo AOSP commit.

Di seguito è commettere un messaggio:

Permesso setRetainInstance (vero) su frammenti annidati

Salva frammenti attraverso i cambiamenti di configurazione come oggetti nonconfiguration arbitrariamente annidate. Ciò consente l'utilizzo dei frammenti child di tipo retain-instance come dipendenze opache arbitrarie all'interno di altri frammenti .

+0

Questo è principalmente vero, il nuovo * aggiornamento della libreria di supporto funziona su 7.0 e 5.0 e sotto, ma su Android 6.0 non funziona correttamente. La strategia di utilizzare i frammenti di conservazione diventerà più della norma poiché su 7.0 si risparmiano solo 1 MB di dati tramite onSaveInstance – Darxval