2013-05-22 5 views
41

Sto usando Fragment con listView. Riempio ArrayAdapter associato a questa listview, dai dati ricevuti in Caricatore personalizzato (da internet). Custom ArrayAdapter supporta lo scrolling infinito (paging).Buona soluzione per conservare gli oggetti listview quando l'utente ruota il telefono e conserva tutti i dati in ArrayAdapter

Qual è il modo migliore per archiviare elementi in ArrayAdapter quando l'utente ruota il dispositivo e mantiene la posizione di scorrimento in ListView?

Sto pensando alla creazione di frammenti non visuali con ArrayAdapter e utilizzando il metodo setRetainInstance per il salvataggio dei valori.

Qualche suggerimento per una soluzione migliore?

+0

http://developer.android.com/guide/topics/resources/runtime-changes.html – Raghunandan

+0

è possibile controllare questo per i frammenti http://www.androiddesignpatterns.com/2013/04/retaining -objects-across-config-changes.html è possibile controllare onConfigurationChanged @ http://developer.android.com/reference/android/app/Fragment.html – Raghunandan

+0

Cosa succede se si imposta l'ArrayAdapter static, nel controllo onCreate se è null , se lo crea, altrimenti chiama invalido su di esso (per forzare un ridisegno)? – Ethan

risposta

56

Per lavorare con il ciclo di vita di Android framework e Fragment è necessario implementare il metodo onSaveInstanceState nel frammento.Per semplicità ho assunto che si dispone di una matrice di valori di stringa che si può arrivare a (io in genere estendo ArrayAdapter per incapsulare vista di costruzione e di fornire un metodo comodo per accedere a tutto il set di dati sottostante):

public void onSaveInstanceState(Bundle savedState) { 

    super.onSaveInstanceState(savedState); 

    // Note: getValues() is a method in your ArrayAdapter subclass 
    String[] values = mAdapter.getValues(); 
    savedState.putStringArray("myKey", values); 

} 

È possono poi recuperare i dati nel vostro metodo di onCreate (o onCreateView o onActivityCreated - vedi the Fragment JavaDoc) come questo:

public void onCreate (Bundle savedInstanceState) { 

    super.onCreate(savedInstanceState); 

    if (savedInstanceState != null) { 
     String[] values = savedInstanceState.getStringArray("myKey"); 
     if (values != null) { 
      mAdapter = new MyAdapter(values); 
     } 
    } 

    ... 

} 

Ciò garantisce che tutti gli eventi del ciclo di vita saranno trattati correttamente, senza perdita di dati, tra cui la rotazione del dispositivo e l'utente passaggio ad altre applicazioni. Il pericolo di non utilizzare onSaveInstanceState e di utilizzare la memoria è il pericolo che Android recuperi quella memoria. Lo stato salvato non sarebbe influenzato da questo, ma l'utilizzo di variabili di istanza o di frammenti nascosti causerebbe la perdita di dati.

Se savedStateInstance è nullo, non c'è stato da ripristinare.

if (values != null) Il if (values != null) è semplicemente di guardia contro la possibilità che nessun array è stato salvato, ma se si codifica il tuo ArrayAdapter per gestire un set di dati nullo non sarà necessario questo.

La soluzione definitiva, se le righe sono istanze di una delle proprie classi e non singoli elementi di dati, è quella di implementare l'interfaccia Parcelable su quella classe, quindi è possibile utilizzare savedState.putParcelableArray("myKey", myArray). Saresti sorpreso di quanto sia utile sapere come implementare Parcelable: ti permette di passare le tue lezioni all'interno di intenti e ti permette di scrivere molto codice più pulito.

+0

Sembra buono per salvare i dati, ma come posso mantenere la posizione di scorrimento in ListView – barbarian

+1

Bene ListView ha un metodo onSaveInstanceState() che restituisce un metodo Parcelable e un metodo onRestoreInstanceState (Parcelable) per ripristinare lo stato. Se ricordo correttamente, lo stato salvato include la posizione di scorrimento. – Phil

+19

Per mantenere la posizione di scorrimento, salvare anche: listView.getFirstVisiblePosition() in onSaveInstanceState(). Dopo aver impostato l'adattatore su listView, aggiungi questa riga: listView.setSelectionFromTop (index, 0); dove l'indice è l'indice che hai salvato. –

6

Quando il dispositivo viene ruotato, l'app viene riavviata. Quindi viene chiamato onSaveInstance prima che l'app venga distrutta. È possibile salvare l'adattatore di array in onSaveInstance e quando il numero onCreate viene infine chiamato quando l'app viene riavviata, è possibile recuperare l'adattatore di array e impostarlo sulla visualizzazione elenco.

+5

non hai ragione, solo l'attività in primo piano viene ricreata. –

+0

Sì, viene ricreata solo l'attività in primo piano, ma insieme a essa vengono ricreati anche i frammenti all'interno dell'attività. –

+1

Non ha senso "salvare un ArrayAdaptor". L'approccio corretto è quello di salvare i dati utilizzati da ArrayAdaptor e quindi ricreare ArrayAdaptor - vedere la mia soluzione di schema. – Phil

1

quando l'orientamento cambia il ciclo di vita delle attività di chiama e poi onRestart fare qualcosa del genere -

@Override 
protected void onRestart() { 
    super.onRestart(); 
    mAdapter.getFilter().filter(getFilterSettings()); 
    mAdapter.notifyDataSetChanged(); 
} 
+0

non hai ragione, i documenti affermano chiaramente che l'attività viene ricreata in seguito al cambio di orientamento. –

+0

crea i metodi 'onPause()' e 'onRestart()' e stampa il registro e vedi cosa succede. – mjosh

+0

fatto quello. onRestart non viene chiamato per l'attività normale senza i parametri configChanges. –

-1

Non conosco il contenuto della vostra ArrayAdapter o List sostegno, ma che dire di fare il sostegno di dati l'elenco è serializzabile, salvandolo e quindi caricandolo quando viene ricreata la vista?

Tradizionalmente ho visto questa app utilizzata quando stai cercando di archiviare dati quando l'app viene chiusa o rischia di essere uccisa rimanendo sullo sfondo. C'è un ottimo post on serialization completo di codice di esempio qui. Camminano prendendo un ArrayList di oggetti personalizzati, scrivendolo su un file e riaprendolo in seguito. Penso che se hai implementato questo approccio, scrivendo i dati del tuo supporto List o ArrayAdapter in onPause() prima che l'attività venga distrutta, puoi ricaricare quel file quando l'attività è ricreata.

Altri approcci (a/k/a piani di backup):

(1) facile ma sciatto - Se l'elenco è un po 'primitiva, come una lista di stringhe, si può sempre non crei i valori singolarmente per SharedPreferences e reclamandoli su ricarica. Assicurati di assegnare qualche ID univoco nel processo di archiviazione.

NOTA mentre questo può funzionare, SharedPreferecnes in genere non è progettato per gestire grandi quantità di dati, quindi se la lista è lunga eviterei questo approccio. Per alcuni punti di dati, tuttavia, non riesco a vedere che si tratti di un problema.

(2) Un po 'più difficile, ma rischioso - Se siete List il backup della scheda contiene oggetti che implementano parcelable o che sono già serializzabili, considerare passando il List tramite un Intent ad un'attività esistente che è in background e utilizzando un callback per recuperare quei dati quando l'attività viene ricreata. Questo è simile alla tua idea di creare uno sfondo Fragment. Entrambi gli approcci corrono il rischio che lo Activity o il Fragment di destinazione non siano presenti.

+1

Perché utilizzare SharedPreferences quando si dispone del metodo onSaveInstanceState? Per quanto riguarda il trasferimento dei dati in un'altra parte dell'applicazione - disordinato e in definitiva inutile se Android distrugge l'applicazione per recuperare memoria se è in background. Comprendere il ciclo di vita di Attività e Frammento e lavorarci. – Phil

+0

Sì, non penso che sia una buona idea e sicuramente l'InstanceState è meglio. Ma se dovessi solo memorizzare un paio di stringhe per un breve periodo, è solo un modo veloce per farlo. – Rarw

4

È abbastanza semplice salvare gli articoli del proprio adattatore in onSaveInstance.

Ora è necessario salvare la posizione (scorrimento). È possibile farlo

il modo più semplice

Dal momento che cambia screenOrientation si fa qualche comunque, si può permettere per alcuni errori marginali e semplicemente, nel vostro onSaveInstance, salvare il primo elemento visibile utilizzando listView.getFirstVisiblePosition().

Quindi nel tuo onRestoreInstance è possibile recuperare questo indice per scorrere allo stesso elemento utilizzando listview.setSelection(position). Certo, puoi usare listview.smoothScrollToPosition(position) ma sarebbe strano.


nel modo più duro

di avere più esatta posizione, è necessario andare giù più 1 livello: Prendi la posizione di scorrimento (non index) e salvare quella posizione. Quindi, dopo il ripristino, scorrere di nuovo in questa posizione. Nel tuo onSaveInstance, salva la posizione restituita da listview.getScrollY(). Quando recuperi questa posizione nel tuo onRestoreInstance, puoi tornare a questa posizione usando listview.scrollTo(0, position).


Perché è davvero un modo difficile?

Semplice. Probabilmente non sarai in grado di tornare a questa posizione perché la tua listview non avrà ancora superato la misurazione e il layout.È possibile superare questo problema aspettando il layout dopo aver impostato l'adattatore usando getViewObserver().addOnGlobalLayoutListener. In questo callback, pubblicare un eseguibile per scorrere fino alla posizione.


io consiglio di usare il primo "modo più semplice", poiché in generale quando l'orientamento dello schermo è stata modificata l'utente sarà probabilmente muovere le dita indietro. Abbiamo solo bisogno di mostrargli gli stessi articoli "dare o avere".

-1

Per salvare i dati per la visualizzazione elenco come non ricaricare più e più volte durante la ricreazione del frammento è quello di salvare tali dati in un membro statico di una classe. Qualcosa come

class YourData{ 
public static List<YourDataObject> listViewData; 
} 

E poi da adattatore prima caricare i dati da internet o dal database locale quindi impostare che recuperati i dati per quel membro qualcosa di statico come questo:

@Override 
    public void onCreate(@Nullable Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     //Run the process to get data from database 
     YourData.listViewData = dataRetrievedFromDatabase; 
    } 

e poi nella vostra onResume metodo di aggiornamento di questi valori per l'adattatore qualcosa come:

@Override 
    public void onResume() { 
     super.onResume(); 
     if(YourData.listViewData!=null){ 
      yourListAdapater.setData = YourData.listViewData; 
      listView.setAdapter(yourListAdapater); 
     }else{ 
     //run your method to get data from server 
     } 
    } 

questo può essere utilizzato per salvare qualsiasi tipo di dati f o una singola sessione

+0

Questo non funzionerà in modo affidabile poiché Android può recuperare memoria quando un'applicazione o un'attività non è attiva. Per lavorare con il ciclo di vita di Android framework e Fragment è necessario implementare il metodo 'onSaveInstanceState' nel frammento. – Phil

Problemi correlati