15

当配置更改发生时,我的 ListView Checkbox 状态会丢失,我明白这是为什么。我尝试实施

public void onSaveInstanceState(final Bundle outState)

在我的一个片段中。所以我只是想知道将我的 SparseBooleanArray 存储在 outState 中的最简单方法是什么

另外,我有点困惑,因为 ListView 有方法:

getListView().getCheckedItemPositions();

这有什么好处?

4

8 回答 8

40

就我而言,我最终通过在 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€</p>

于 2013-05-23T10:15:16.197 回答
4

我没有看到问题最后一部分的任何答案:

另外,我有点困惑,因为 ListView 有方法:

getListView().getCheckedItemPositions();

这有什么好处?

假设您将复选框与动作模式结合使用,动作模式本身将存储选中位置的状态。在正常模式下,按住将突出显示一个项目,启用操作模式,然后允许用户点击其他项目以检查它们。

方向改变会发生什么?恢复动作模式并保留亮点。您的复选框不是,但显然检查的数据是。

在方向改变之后,onCreateActionMode()被调用。在此方法中(但不是之前),getListView().getCheckedItemPositions()将返回SparseBooleanArray您可能正在寻找的。

有趣的是,如果这是您的唯一用例SparseBooleanArray,您甚至不必担心自己存储它。您可以从已经在整个方向更改中存储它的系统中检索它。

getListView().getCheckedItemPositions()就是好的。

于 2013-04-28T17:29:16.750 回答
2

您可以为该数据结构发明一些序列化方案,也可以切换到 HashSet 并仅在其中存储已检查的列表索引位置。由于 HashSet 是可序列化的,因此您可以将其放入实例状态 Bundle 中。

于 2012-06-29T23:35:54.960 回答
1

扩展 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;
    }


}
于 2014-02-05T10:36:26.277 回答
1

我正在尝试做同样的事情。最初我使用的是HashMap

如何使用意图将哈希图值发送到另一个活动

在活动之间传递,因为它扩展了Serializable接口。但是,经过一番研究:

“旨在比使用 a映射到SparseBooleanArrays更有效。”HashMapIntegersBooleans

但是,我找不到存储此数据结构的简单方法。我已经考虑获取的值SparseBooleanArray并将其存储在 a 中HashMap,以便它可以在活动之间传递。但这似乎增加了更多的复杂性。

不幸的是,我想我将恢复使用 HashMaps 来存储我的复选框列表

于 2012-08-16T05:49:27.413 回答
1

我将创建一个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];
        }
    };
}
于 2018-04-16T16:20:05.307 回答
0

I tried the solution creating SerializableSparseArray extending SparseArray making it possible to put a SparseArray into bundle via Bundle.putSerializable call.

But I found I can't obtain the saved object from bundle in onRestoreInstanceState. Digging into the issue I found that savedInstanceState.getSerializable(KEY_TO_STRING_SPARSE_ARRAY) == null.

Then, trying to inspect savedInstanceState.get(KEY_TO_STRING_SPARSE_ARRAY) and suprisingly got a SparseArray<String> not SerializableSparseArray<String>. Finally I'm using savedInstanceState.getSparseParcelableArray(KEY_TO_STRING_SPARSE_ARRAY) to obtaining SparseArray back from a bundle.

Then, using java reflection to save a SparseArray<String> to bundle directly without extending with Serializable or Parcelable interface. It's a little bit dirty but I think you can make your own utility function hiding the following detail implementation.

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

I've tested the code and it works on Android 4.4.4. And I'd like to know if it's safe to use the implementation in other SDK implementations.

于 2014-08-08T07:09:09.413 回答
0

这是我的解决方案。只是一个简单的包装器,可用于序列化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);
}
于 2013-01-25T08:39:40.810 回答