La mejor manera de almacenar SparseBooleanArray en Bundle?

Cuando ocurre un cambio de configuración, mis estados de Checkbox ListView se pierden, lo que entiendo por qué. Trato de implementar

public void onSaveInstanceState(final Bundle outState) 

En uno de mis Fragmentos. Así que me estoy preguntando cuál es la manera más fácil de almacenar mi SparseBooleanArray en el outState .

Además, estoy un poco confundido, como ListView tiene el método:

 getListView().getCheckedItemPositions(); 

¿Para qué sirve esto?

En mi caso, terminé haciéndolo mediante la implementación de un wrapper Parcelable alrededor del SparseBooleanArray, así:

 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); } } 

Esto le permite guardar y cargar su SparseBooleanArray simplemente haciendo:

 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"); 

Sólo mi .02 €

No veo ninguna respuesta a la última parte de la pregunta:

Además, estoy un poco confundido, como ListView tiene el método:

getListView().getCheckedItemPositions();

¿Para qué sirve esto?

Suponiendo que esté usando casillas de verificación junto con el modo de acción, el modo de acción en sí almacenará el estado de las posiciones seleccionadas. En el modo normal, una pulsación y retención resaltará un elemento, activará el modo de acción y, a continuación, permitirá al usuario pulsar otros elementos para también comprobarlos.

¿Qué sucede en un cambio de orientación? Se reanuda el modo de acción y se mantienen los puntos destacados. Sus casillas de verificación no lo son, pero claramente los datos de lo que está marcado.

Después de un cambio de orientación, onCreateActionMode() se llama. En este método (pero no antes), getListView().getCheckedItemPositions() devolverá el SparseBooleanArray que probablemente estás buscando.

La parte ingeniosa acerca de esto es si este es su único caso de uso para el SparseBooleanArray , ni siquiera tiene que preocuparse por almacenarlo usted mismo. Puede recuperarlo del sistema que ya lo está almacenando a través del cambio de orientación.

Eso es lo que getListView().getCheckedItemPositions() es bueno para.

Extender SparseArray para implementar 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; } } 

Puede inventar algún esquema de serialización para esa estructura de datos o cambiar a un HashSet y almacenar sólo las posiciones de índice de lista que están marcadas. Puesto que HashSet es serializable usted puede apenas ponerlo en el paquete del estado de la instancia.

Estoy intentando hacer lo mismo. Inicialmente estaba usando un HashMap

Cómo enviar el valor de hashmap a otra actividad usando una intención

Para pasar entre actividades ya que extiende la interfaz Serializable . Sin embargo, después de hacer algunas investigaciones:

" SparseBooleanArrays están destinados a ser más eficientes que usar un HashMap para asignar Integers a Booleans ."

Sin embargo, no puedo encontrar una forma sencilla de almacenar esta estructura de datos. He contemplado agarrar los valores de la SparseBooleanArray y almacenar en un HashMap para que pueda ser pasado entre las actividades. Pero esto parece agregar más complejidad.

Por desgracia, creo que voy a volver a usar HashMaps para almacenar mi lista de cuadros marcados

Aquí está mi solución. Sólo un simple envoltorio que se puede utilizar para serializar 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); } } } 

A continuación, agregar el objeto a un lote de la siguiente manera:

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

Intenté la solución que creaba SerializableSparseArray amplía SparseArray que hace posible poner un SparseArray en paquete vía la llamada de Bundle.putSerializable .

Pero he encontrado que no puedo obtener el objeto guardado del paquete en onRestoreInstanceState . Cavando en el problema encontré que savedInstanceState.getSerializable(KEY_TO_STRING_SPARSE_ARRAY) == null .

Entonces, tratando de inspeccionar savedInstanceState.get(KEY_TO_STRING_SPARSE_ARRAY) y SparseArray<String> obtuvo un SparseArray<String> no SerializableSparseArray<String> . Por último, estoy utilizando savedInstanceState.getSparseParcelableArray(KEY_TO_STRING_SPARSE_ARRAY) para obtener SparseArray nuevo desde un lote.

Luego, usar la reflexión de Java para guardar un SparseArray<String> para agrupar directamente sin extender con la interfaz Serializable o Parcelable . Es un poco sucio, pero creo que puede hacer su propia función de utilidad ocultar la siguiente implementación de detalle.

 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); } 

He probado el código y funciona en Android 4.4.4. Y me gustaría saber si es seguro utilizar la implementación en otras implementaciones de SDK.

FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.