将 SparseBooleanArray 存储在 Bundle 中的最佳方法?
Posted
技术标签:
【中文标题】将 SparseBooleanArray 存储在 Bundle 中的最佳方法?【英文标题】:Best way to store SparseBooleanArray in Bundle? 【发布时间】:2012-07-01 11:30:29 【问题描述】:当配置更改发生时,我的 ListView 复选框状态会丢失,我明白这是为什么。 我尝试实现
public void onSaveInstanceState(final Bundle outState)
在我的一个片段中。所以我只是想知道将我的 SparseBooleanArray 存储在 outState 中的最简单方法是什么。
另外,我有点困惑,因为 ListView 有方法:
getListView().getCheckedItemPositions();
这有什么用?
【问题讨论】:
您可能根本不需要使用onSaveInstanceState()
。见***.com/questions/24294919/…
【参考方案1】:
在我的例子中,我最终通过在 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");
只是我的 .02€
【讨论】:
从 Bundle 读取时,您应该将 bundle.getParcelable("myBooleanArray") 转换为 (SparseBooleanArray)。Parcel
类有writeSparseBooleanArray
和readSparseBooleanArray
而不是手动写读写
@MohamedKamalKamaly 如您所见here 该方法甚至不在 API 的发布版本中,而这个答案已经有四年了。无论如何,如果我是你,我不会太快使用这些新方法......
哎呀,我才意识到我在那里看错了方法:P【参考方案2】:
我没有看到问题最后一部分的任何答案:
另外,我有点困惑,因为 ListView 有方法:
getListView().getCheckedItemPositions();
这有什么用?
假设您将复选框与动作模式结合使用,动作模式本身将存储选中位置的状态。在正常模式下,按住会突出显示一个项目,启用操作模式,然后允许用户点击其他项目来检查它们。
方向改变会发生什么?恢复动作模式并保留亮点。您的复选框不是,但显然检查的数据是。
方向改变后,onCreateActionMode()
被调用。在此方法中(但不是之前),getListView().getCheckedItemPositions()
将返回您可能正在寻找的SparseBooleanArray
。
有趣的是,如果这是SparseBooleanArray
的唯一用例,您甚至不必担心自己存储它。您可以从系统中检索它,该系统已经在方向变化时存储它。
这就是getListView().getCheckedItemPositions()
的好处。
【讨论】:
【参考方案3】:您可以为该数据结构发明一些序列化方案,或者切换到 HashSet 并仅在其中存储已检查的列表索引位置。由于 HashSet 是可序列化的,因此您可以将其放入实例状态 Bundle 中。
【讨论】:
谢谢,就我而言,这比创建其他类更容易 HashMap我正在尝试做同样的事情。最初我使用的是HashMap
How to send hashmap value to another activity using an intent
在活动之间传递,因为它扩展了Serializable
接口。但是,在做了一些研究之后:
“SparseBooleanArrays
旨在比使用HashMap
将Integers
映射到Booleans
更有效。”
但是,我找不到存储此数据结构的简单方法。我已经考虑获取SparseBooleanArray
的值并将其存储在HashMap
中,以便它可以在活动之间传递。但这似乎增加了更多的复杂性。
不幸的是,我想我将恢复使用 HashMaps 来存储我的复选框列表
【讨论】:
【参考方案5】:扩展 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;
【讨论】:
【参考方案6】:我将创建一个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];
;
【讨论】:
【参考方案7】:这是我的解决方案。只是一个简单的包装器,可用于序列化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);
【讨论】:
【参考方案8】:我尝试了创建SerializableSparseArray
扩展SparseArray
的解决方案,从而可以通过Bundle.putSerializable
调用将SparseArray
放入捆绑包中。
但是我发现我无法从 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)
从捆绑包中获取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 实现中使用该实现是否安全。
【讨论】:
以上是关于将 SparseBooleanArray 存储在 Bundle 中的最佳方法?的主要内容,如果未能解决你的问题,请参考以下文章