Лучший способ хранения SparseBooleanArray в Bundle?
Когда происходит изменение конфигурации, мой список ListView Checkbox теряется, и я понимаю, почему.
Я пытаюсь реализовать
public void onSaveInstanceState(final Bundle outState)
в одном из моих фрагментов. Поэтому мне просто интересно, какой самый простой способ сохранить мой SparseBooleanArray в outState.
Кроме того, я немного смущен, поскольку ListView имеет метод:
getListView().getCheckedItemPositions();
Для чего это полезно?
Ответы
Ответ 1
В моем случае я закончил это, выполнив пакетную упаковку вокруг 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 €
Ответ 2
Я не вижу ответов на последнюю часть вопроса:
Кроме того, я немного смущен, поскольку ListView имеет метод:
getListView().getCheckedItemPositions();
Для чего это полезно?
Предполагая, что вы используете флажки в сочетании с Action Mode, сам режим Action сохранит состояние проверенных позиций. В обычном режиме нажатие и удержание будет выделять элемент, активировать режим действия, а затем разрешать пользователю использовать другие элементы, чтобы также проверить их.
Что происходит при изменении ориентации? Режим действия возобновляется, и основные моменты сохраняются. У вас нет флажков, но, очевидно, данные проверяемого.
После изменения ориентации вызывается onCreateActionMode()
. В этом методе (но не раньше) getListView().getCheckedItemPositions()
вернет SparseBooleanArray
, который вы, вероятно, ищете.
Замечательная часть этого вопроса, если это ваш единственный вариант использования для SparseBooleanArray
, вам не нужно даже беспокоиться о его сохранении. Вы можете получить его из системы, которая уже хранит ее в процессе изменения ориентации.
Для чего подходит getListView().getCheckedItemPositions()
.
Ответ 3
Расширьте 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;
}
}
Ответ 4
Вы можете либо придумать какую-то схему сериализации для этой структуры данных, либо переключиться на HashSet и сохранить только проверенные им позиции индекса списка. Поскольку HashSet является сериализуемым, вы можете просто поместить его в пакет состояния экземпляра.
Ответ 5
Я пытаюсь сделать то же самое. Первоначально я использовал HashMap
Как отправить значение hashmap другому действию с использованием намерения
для перехода между действиями, поскольку он расширяет интерфейс Serializable
. Однако после проведения некоторых исследований:
"SparseBooleanArrays
должны быть более эффективными, чем использование HashMap
для отображения Integers
в Booleans
."
Однако я не могу найти простой способ сохранить эту структуру данных. Я созерцал захват значений SparseBooleanArray
и сохранение его в HashMap
, чтобы он мог быть передан между действиями. Но это, кажется, добавляет больше сложности.
К сожалению, я думаю, что я вернусь к использованию HashMaps для хранения списка отмеченных ящиков
Ответ 6
Вот мое решение. Просто простая оболочка, которая может использоваться для сериализации 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);
}
Ответ 7
Я попробовал решение, создающее SerializableSparseArray
расширение SparseArray
, позволяющее поместить SparseArray
в пучок через вызов Bundle.putSerializable
.
Но я обнаружил, что не могу получить сохраненный объект из пакета в onRestoreInstanceState
. Копаясь в проблеме, я обнаружил, что savedInstanceState.getSerializable(KEY_TO_STRING_SPARSE_ARRAY) == null
.
Затем, пытаясь проверить savedInstanceState.get(KEY_TO_STRING_SPARSE_ARRAY)
и, скорее всего, получив SparseArray<String>
not SerializableSparseArray<String>
. Наконец, я использую savedInstanceState.getSparseParcelableArray(KEY_TO_STRING_SPARSE_ARRAY)
для получения SparseArray
обратно из пакета.
Затем, используя java-отражение для сохранения SparseArray<String>
для прямого связывания без расширения с помощью интерфейса 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.