当出现配置更改时,我的ListView复选框状态会丢失,我可以理解为什么会这样。
我尝试实现
public void onSaveInstanceState(final Bundle outState)
我有一个Fragment,想知道最简单的方法是将SparseBooleanArray存储在outState中。
另外,我有点困惑,因为ListView有这个方法:
getListView().getCheckedItemPositions();
这有什么用处?
当出现配置更改时,我的ListView复选框状态会丢失,我可以理解为什么会这样。
我尝试实现
public void onSaveInstanceState(final Bundle outState)
我有一个Fragment,想知道最简单的方法是将SparseBooleanArray存储在outState中。
另外,我有点困惑,因为ListView有这个方法:
getListView().getCheckedItemPositions();
这有什么用处?
在我的情况下,我最终通过在SparseBooleanArray周围实现一个Parcelable包装器来完成它,就像这样:
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");
只是我的看法,不过价值0.02欧元。
Parcel
类拥有writeSparseBooleanArray
和readSparseBooleanArray
函数,可以自动处理读写操作,无需手动编写。 - Mohamed Kamaly我没有看到对问题最后一部分的回答:
另外,我有点困惑,因为ListView有这个方法:
getListView().getCheckedItemPositions();
这有什么用?
假设您正在与Action Mode结合使用复选框,则Action Mode本身将存储所选位置的状态。在正常模式下,按住并保持按住会突出显示一个项目,启用操作模式,然后允许用户点击其他项目以同时选中它们。
发生方向更改时会发生什么? Action Mode将被恢复,并保留高亮显示。但是您的复选框不会,但显然已经检查了哪些数据。
方向更改后,将调用onCreateActionMode()
。在此方法中(但不是之前),getListView().getCheckedItemPositions()
将返回您可能正在寻找的SparseBooleanArray
。
这个巧妙的部分是,如果这是您对SparseBooleanArray
的唯一用例,则甚至不需要担心自己存储它。您可以从系统中检索它,该系统已经在方向更改期间存储它。
这就是getListView().getCheckedItemPositions()
的用处。
Parcelable
持有类,使用Parcel
的本地方法来序列化/反序列化SparseBooleanArray
:import android.os.Parcel;
import android.os.Parcelable;
import android.util.SparseBooleanArray;
public class SparseBooleanArrayHolder implements Parcelable {
private final SparseBooleanArray source;
public SparseBooleanArrayHolder(SparseBooleanArray source) {
this.source = source;
}
public SparseBooleanArray get() {
return source;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeSparseBooleanArray(source);
}
@Override
public int describeContents() {
return 0;
}
public static final Creator<SparseBooleanArrayHolder> CREATOR = new Creator<SparseBooleanArrayHolder>() {
@Override
public SparseBooleanArrayHolder createFromParcel(Parcel in) {
return new SparseBooleanArrayHolder(in.readSparseBooleanArray());
}
@Override
public SparseBooleanArrayHolder[] newArray(int size) {
return new SparseBooleanArrayHolder[size];
}
};
}
我正在尝试做同样的事情。最初我使用了一个HashMap
来传递活动之间的数据,因为它扩展了Serializable
接口。
然而,在进行一些研究后:
"SparseBooleanArrays
旨在比使用HashMap
将Integers
映射到Booleans
更有效。"
但是,我找不到一种简单的方法来存储这个数据结构。我已经考虑过获取SparseBooleanArray
的值并将其存储在HashMap
中,以便可以在活动之间传递。但这似乎增加了更多的复杂性。
不幸的是,我认为我将回归使用HashMap来存储我的选中框列表。
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;
}
}
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
的解决方案,使得可以通过Bundle.putSerializable
调用将SparseArray
放入bundle中。
但是我发现在onRestoreInstanceState
中无法从bundle中获取保存的对象。经过深入研究,我发现savedInstanceState.getSerializable(KEY_TO_STRING_SPARSE_ARRAY) == null
。
然后,我尝试检查savedInstanceState.get(KEY_TO_STRING_SPARSE_ARRAY)
,惊奇地发现得到的是一个SparseArray<String>
而不是SerializableSparseArray<String>
。最后,我使用savedInstanceState.getSparseParcelableArray(KEY_TO_STRING_SPARSE_ARRAY)
从bundle中获取SparseArray
。
接着,我使用Java反射直接将SparseArray<String>
保存到bundle中,而不需要扩展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 实现中使用这个实现是否安全。
onSaveInstanceState()
。请参见http://stackoverflow.com/questions/24294919/maintain-item-selection-on-orientation-change - faizal