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 .
- Cómo obtener NUEVA anchura / altura del diseño raíz en onConfigurationChanged?
- Android: Cómo evitar el cambio de tamaño de la ventana al mostrar el teclado virtual
- Mis opiniones se están restableciendo en el cambio de orientación
- Android: Dejar de recrear la actividad en el cambio de orientación
- los archivos xml de layout-land no funcionan con onConfigurationChanged call back
Además, estoy un poco confundido, como ListView tiene el método:
getListView().getCheckedItemPositions();
¿Para qué sirve esto?
- Restaurar el estado de TextView después de la rotación de la pantalla?
- Evitar el descarte de diálogo en la rotación de la pantalla en Android
- Reinicio condicional en Activity onConfigurationChanged
- Usando onConfigurationChanged en un fragmento
- Android Layout de la tierra no funciona
- Qué se llama después del método onConfigurationchanged ()
- Actividad de manipulación que gira en Android
- Android onConfigurationChanged: cómo guardar y restaurar fragmento de la pila trasera?
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.