2012-06-29 13 views
14

Quando si verifica un cambio di configurazione, gli stati della mia casella di controllo ListView si perdono, e capisco perché. cerco di implementareIl modo migliore per archiviare SparseBooleanArray in bundle?

public void onSaveInstanceState(final Bundle outState) 

in uno dei miei frammenti. Quindi mi chiedo quale sia il modo più semplice per archiviare il mio SparseBooleanArray nello outState.

Inoltre, io sono un po 'confuso, come ListView ha il metodo:

getListView().getCheckedItemPositions(); 

Che cos'è questo bene per?

+0

Potrebbe non essere necessario utilizzare 'onSaveInstanceState()'. Vedi http://stackoverflow.com/questions/24294919/maintain-item-selection-on-orientation-change – faizal

risposta

0

È possibile inventare uno schema di serializzazione per tale struttura dati o passare a un HashSet e memorizzare solo le posizioni dell'indice di lista in esso controllate. Poiché HashSet è serializzabile, puoi semplicemente inserirlo nel pacchetto di stato di istanza.

0

Sto provando a fare la stessa cosa. Inizialmente ero usando un HashMap

How to send hashmap value to another activity using an intent

passare tra attività poiché estende l'interfaccia Serializable. Tuttavia, dopo aver fatto qualche ricerca:

"SparseBooleanArrays sono destinati ad essere più efficiente rispetto all'utilizzo di un HashMap per mappare Integers a Booleans."

Tuttavia, non riesco a trovare un modo semplice per archiviare questa struttura dati. Ho pensato di prendere i valori dello SparseBooleanArray e di memorizzarlo in un HashMap in modo che possa essere passato tra le attività. Ma questo sembra aggiungere più complessità.

Purtroppo, penso che ho intenzione di tornare a utilizzare HashMaps per memorizzare mia lista di caselle selezionate

0

Ecco la mia soluzione. Solo un semplice wrapper che può essere usato per serializzare un SparseBooleanArray.

public class SerializableSparseBooleanArrayContainer implements Serializable { 

    private static final long serialVersionUID = 393662066105575556L; 
    private SparseBooleanArray mSparseArray; 

    public SerializableSparseBooleanArrayContainer(SparseBooleanArray mDataArray) { 
     this.mSparseArray = mDataArray; 
    } 

    public SparseBooleanArray getSparseArray() { 
     return mSparseArray; 
    } 

    public void setSparseArray(SparseBooleanArray sparseArray) { 
     this.mSparseArray = sparseArray; 
    } 

    private void writeObject(java.io.ObjectOutputStream out) throws IOException { 
     out.writeLong(serialVersionUID); 
     int sparseArraySize = mSparseArray.size(); 
     out.write(sparseArraySize); 
     for (int i = 0 ; i < sparseArraySize; i++){ 
      int key = mSparseArray.keyAt(i); 
      out.writeInt(key); 
      boolean value = mSparseArray.get(key); 
      out.writeBoolean(value); 
     } 
    } 

    private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException { 
     long readSerialVersion = in.readLong(); 
     if (readSerialVersion != serialVersionUID) { 
      throw new IOException("serial version mismatch"); 
     } 
     int sparseArraySize = in.read(); 
     mSparseArray = new SparseBooleanArray(sparseArraySize); 
     for (int i = 0 ; i < sparseArraySize; i++) { 
      int key = in.readInt(); 
      boolean value = in.readBoolean(); 
      mSparseArray.put(key, value); 
     } 
    } 

} 

Ho poi aggiungere l'oggetto a un fascio in questo modo:

@Override 
public void onSaveInstanceState(Bundle outState) { 
    super.onSaveInstanceState(outState); 
    SparseBooleanArray sparseBooleanArray = getSparseBooleanArray(); 
    SerializableSparseBooleanArrayContainer sparseBooleanArraySerializable = new SerializableSparseBooleanArrayContainer(sparseBooleanArray); 
    outState.putSerializable(SOME_BUNDLE_KEY, sparseBooleanArraySerializable); 
} 
4

non vedo alcuna risposta all'ultima parte della domanda:

Inoltre, I' m un po 'confuso, come ListView ha il metodo:

getListView().getCheckedItemPositions();

A cosa serve questo?

Supponendo che si stiano utilizzando le caselle di controllo insieme alla modalità di azione, la Modalità azione stessa memorizzerà lo stato delle posizioni controllate. Nella modalità normale, una pressione prolungata evidenzierà un elemento, attiva la modalità di azione e quindi consente all'utente di toccare altri elementi per controllarli.

Cosa succede durante un cambio di orientamento? La modalità di azione riprende e gli highlights vengono mantenuti. Le tue caselle di controllo non lo sono, ma chiaramente i dati di ciò che è controllato sono.

Dopo un cambio di orientamento, viene chiamato onCreateActionMode(). In questo metodo (ma non prima), getListView().getCheckedItemPositions() restituirà il SparseBooleanArray che stai cercando.

La bella parte di questo è se questo è il tuo unico caso d'uso per lo SparseBooleanArray, non devi neanche preoccuparti di memorizzarlo da solo. È possibile recuperarlo dal sistema che è già memorizzandolo attraverso il cambio di orientamento.

Questo è ciò che getListView().getCheckedItemPositions() è buono per.

37

Nel mio caso, ho finito per farlo mediante l'attuazione di un involucro Parcelable intorno al SparseBooleanArray, in questo modo: si

import android.os.Parcel; 
import android.os.Parcelable; 
import android.util.SparseBooleanArray; 

public class SparseBooleanArrayParcelable extends SparseBooleanArray implements Parcelable { 
    public static Parcelable.Creator<SparseBooleanArrayParcelable> CREATOR = new Parcelable.Creator<SparseBooleanArrayParcelable>() { 
    @Override 
    public SparseBooleanArrayParcelable createFromParcel(Parcel source) { 
     SparseBooleanArrayParcelable read = new SparseBooleanArrayParcelable(); 
     int size = source.readInt(); 

     int[] keys = new int[size]; 
     boolean[] values = new boolean[size]; 

     source.readIntArray(keys); 
     source.readBooleanArray(values); 

     for (int i = 0; i < size; i++) { 
     read.put(keys[i], values[i]); 
     } 

     return read; 
    } 

    @Override 
    public SparseBooleanArrayParcelable[] newArray(int size) { 
     return new SparseBooleanArrayParcelable[size]; 
    } 
    }; 

    public SparseBooleanArrayParcelable() { 

    } 

    public SparseBooleanArrayParcelable(SparseBooleanArray sparseBooleanArray) { 
    for (int i = 0; i < sparseBooleanArray.size(); i++) { 
     this.put(sparseBooleanArray.keyAt(i), sparseBooleanArray.valueAt(i)); 
    } 
    } 

    @Override 
    public int describeContents() { 
    return 0; 
    } 

    @Override 
    public void writeToParcel(Parcel dest, int flags) { 
    int[] keys = new int[size()]; 
    boolean[] values = new boolean[size()]; 

    for (int i = 0; i < size(); i++) { 
     keys[i] = keyAt(i); 
     values[i] = valueAt(i); 
    } 

    dest.writeInt(size()); 
    dest.writeIntArray(keys); 
    dest.writeBooleanArray(values); 
    } 
} 

Questo permette di salvare e caricare il vostro SparseBooleanArray da solo facendo:

Bundle bundle; //For your activity/fragment state, to include in an intent, ... 
SparseBooleanArray sbarray; //Probably from your listview.getCheckedItemPositions() 

//Write it 
bundle.putParcelable("myBooleanArray", new SparseBooleanArrayParcelable(sbarray)); 

//Read it back 
sbarray = (SparseBooleanArray) bundle.getParcelable("myBooleanArray"); 

Solo il mio .02 €

+0

Ottima soluzione. Ha funzionato meravigliosamente. Buon lavoro – Stephen

+0

È necessario eseguire il cast bundle.getParcelable ("myBooleanArray") a (SparseBooleanArray) durante la lettura dal pacchetto. – localhost

+0

Grazie per la modifica di @localhost! – opsidao

1

Estendere SparseArray per implementare un Serializable:

import java.io.IOException; 
import java.io.ObjectInputStream; 
import java.io.ObjectOutputStream; 
import java.io.Serializable; 
import android.util.SparseArray; 



/** 
* @author Asaf Pinhassi www.mobiledev.co.il 
* @param <E> 
* 
*/ 
public class SerializableSparseArray<E> extends SparseArray<E> implements Serializable{ 

    private static final long serialVersionUID = 824056059663678000L; 

    public SerializableSparseArray(int capacity){ 
     super(capacity); 
    } 

    public SerializableSparseArray(){ 
     super(); 
    } 

    /** 
    * This method is private but it is called using reflection by java 
    * serialization mechanism. It overwrites the default object serialization. 
    * 
    * <br/><br/><b>IMPORTANT</b> 
    * The access modifier for this method MUST be set to <b>private</b> otherwise {@link java.io.StreamCorruptedException} 
    * will be thrown. 
    * 
    * @param oos 
    *   the stream the data is stored into 
    * @throws IOException 
    *    an exception that might occur during data storing 
    */ 
    private void writeObject(ObjectOutputStream oos) throws IOException { 
     Object[] data = new Object[size()]; 

     for (int i=data.length-1;i>=0;i--){ 
      Object[] pair = {keyAt(i),valueAt(i)}; 
      data[i] = pair; 
     } 
     oos.writeObject(data); 
    } 

    /** 
    * This method is private but it is called using reflection by java 
    * serialization mechanism. It overwrites the default object serialization. 
    * 
    * <br/><br/><b>IMPORTANT</b> 
    * The access modifier for this method MUST be set to <b>private</b> otherwise {@link java.io.StreamCorruptedException} 
    * will be thrown. 
    * 
    * @param oos 
    *   the stream the data is read from 
    * @throws IOException 
    *    an exception that might occur during data reading 
    * @throws ClassNotFoundException 
    *    this exception will be raised when a class is read that is 
    *    not known to the current ClassLoader 
    */ 
    private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { 
     Object[] data = (Object[]) ois.readObject(); 
     for (int i=data.length-1;i>=0;i--){ 
      Object[] pair = (Object[]) data[i]; 
      this.append((Integer)pair[0],(E)pair[1]); 
     } 
     return; 
    } 


} 
0

Ho provato la soluzione creando SerializableSparseArray estendentesi SparseArray rendendo possibile mettere un SparseArray in fascio tramite Bundle.putSerializable chiamata.

Ma ho trovato che non è possibile ottenere l'oggetto salvato dal pacchetto in onRestoreInstanceState. Scavando nel problema ho trovato che savedInstanceState.getSerializable(KEY_TO_STRING_SPARSE_ARRAY) == null.

Quindi, cercando di ispezionare savedInstanceState.get(KEY_TO_STRING_SPARSE_ARRAY) e sorprendentemente ottenuto un SparseArray<String> non SerializableSparseArray<String>. Finalmente sto usando savedInstanceState.getSparseParcelableArray(KEY_TO_STRING_SPARSE_ARRAY) per ottenere SparseArray da un pacchetto.

Quindi, utilizzando java reflection per salvare un SparseArray<String> da raggruppare direttamente senza estendere con l'interfaccia Serializable o Parcelable. È un po 'sporco, ma penso che si possa fare la propria funzione di utilità nascondendo la seguente implementazione di dettaglio.

try { 
    // FIXME WTF 
    Method m = Bundle.class.getMethod("putSparseParcelableArray", String.class, SparseArray.class); 
    m.invoke(savedInstanceState, KEY_TO_STRING_SPARSE_ARRAY, mSparseStringArray); 
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) { 
    Log.e(TAG, "exception", e); 
} 

Ho testato il codice e funziona su Android 4.4.4. E mi piacerebbe sapere se è sicuro utilizzare l'implementazione in altre implementazioni SDK.

Problemi correlati