Лучший способ хранения SparseBooleanArray в Bundle?

Когда происходит изменение конфигурации, мой список ListView Checkbox теряется, и я понимаю, почему. Я пытаюсь реализовать

public void onSaveInstanceState(final Bundle outState) 

В одном из моих фрагментов. Поэтому мне просто интересно, какой самый простой способ сохранить мой SparseBooleanArray в outState .

Кроме того, я немного смущен, поскольку ListView имеет метод:

 getListView().getCheckedItemPositions(); 

Для чего это полезно?

Solutions Collecting From Web of "Лучший способ хранения SparseBooleanArray в Bundle?"

В моем случае я закончил это, применив Parcelable wrapper вокруг SparseBooleanArray, например:

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

Это позволяет вам сохранять и загружать SparseBooleanArray, просто делая:

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

Только мой .02 €

Я не вижу ответов на последнюю часть вопроса:

Кроме того, я немного смущен, поскольку ListView имеет метод:

getListView().getCheckedItemPositions();

Для чего это полезно?

Предполагая, что вы используете флажки в сочетании с Action Mode, сам режим Action сохранит состояние проверенных позиций. В обычном режиме нажатие и удержание выделяет элемент, активирует режим действия, а затем позволяет пользователю нажимать другие элементы, чтобы также проверить их.

Что происходит при изменении ориентации? Режим действия возобновляется и основные моменты сохраняются. Ваши флажки не установлены, но, очевидно, данные проверяемого.

После изменения ориентации onCreateActionMode() . В этом методе (но не раньше) getListView().getCheckedItemPositions() вернет SparseBooleanArray который вы, вероятно, ищете.

Отличная часть этого SparseBooleanArray , если это ваш единственный случай использования SparseBooleanArray , вам не нужно даже беспокоиться о его сохранении. Вы можете получить его из системы, которая уже хранит ее в процессе изменения ориентации.

Это то, что getListView().getCheckedItemPositions() подходит для.

Расширьте SparseArray для реализации 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; } } 

Вы можете либо придумать какую-то схему сериализации для этой структуры данных, либо переключиться на HashSet и сохранить только те позиции индекса списка, в которых они отмечены. Поскольку HashSet является сериализуемым, вы можете просто поместить его в состояние Bundle.

Я пытаюсь сделать то же самое. Первоначально я использовал HashMap

Как отправить значение hashmap в другое действие с использованием намерения

Для перехода между действиями, поскольку он расширяет интерфейс Serializable . Однако после ряда исследований:

« SparseBooleanArrays предназначены для более эффективной работы, чем использование HashMap для сопоставления Integers с Booleans ».

Однако я не могу найти простой способ сохранить эту структуру данных. Я предполагал захватить значения SparseBooleanArray и хранить его в HashMap чтобы он мог быть передан между действиями. Но это, похоже, усложняет.

К сожалению, я думаю, что я вернусь к использованию HashMaps для хранения списка отмеченных ящиков

Вот мое решение. Просто простая оболочка, которая может использоваться для сериализации 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); } } } 

Затем я добавляю объект в связку следующим образом:

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

Я попробовал решение, создающее SerializableSparseArray расширяющее SparseArray позволяющее помещать SparseArray в пакет через Bundle.putSerializable call.

Но я обнаружил, что не могу получить сохраненный объект из пакета в onRestoreInstanceState . Копаясь в проблеме, я обнаружил, что savedInstanceState.getSerializable(KEY_TO_STRING_SPARSE_ARRAY) == null .

Затем, пытаясь проверить savedInstanceState.get(KEY_TO_STRING_SPARSE_ARRAY) и, скорее SparseArray<String> получил SparseArray<String> not SerializableSparseArray<String> . Наконец, я использую savedInstanceState.getSparseParcelableArray(KEY_TO_STRING_SPARSE_ARRAY) для получения SparseArray из пакета.

Затем, используя java-отражение для сохранения SparseArray<String> для непосредственного Parcelable без расширения интерфейса Serializable или Parcelable . Это немного грязно, но я думаю, что вы можете сделать свою собственную функцию утилиты, скрывая следующую детальную реализацию.

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

Я протестировал код, и он работает на Android 4.4.4. И я хотел бы знать, можно ли использовать реализацию в других реализациях SDK.