【问题标题】:Best way to store SparseBooleanArray in Bundle?将 SparseBooleanArray 存储在 Bundle 中的最佳方法?
【发布时间】:2012-07-01 11:30:29
【问题描述】:

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

public void onSaveInstanceState(final Bundle outState)

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

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

getListView().getCheckedItemPositions();

这有什么用?

【问题讨论】:

标签: android methods onconfigurationchanged sparse-array


【解决方案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 类有writeSparseBooleanArrayreadSparseBooleanArray 而不是手动写读写
  • @MohamedKamalKamaly 如您所见here 该方法甚至不在 API 的发布版本中,而这个答案已经有四年了。无论如何,如果我是你,我不会太快使用这些新方法......
  • 哎呀,我才意识到我在那里看错了方法:P
【解决方案2】:

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

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

getListView().getCheckedItemPositions();

这有什么用?

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

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

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

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

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

【讨论】:

    【解决方案3】:

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

    【讨论】:

    • 谢谢,就我而言,这比创建其他类更容易
    • HashMap 将是 SparseBooleanArray 比 HashSet 更好的替代品
    【解决方案4】:

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

    How to send hashmap value to another activity using an intent

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

    SparseBooleanArrays 旨在比使用HashMapIntegers 映射到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&lt;String&gt; 而不是 SerializableSparseArray&lt;String&gt;。最后,我使用savedInstanceState.getSparseParcelableArray(KEY_TO_STRING_SPARSE_ARRAY) 从捆绑包中获取SparseArray

            然后,使用java反射将SparseArray&lt;String&gt;保存到直接捆绑,而不用SerializableParcelable接口扩展。这有点脏,但我认为您可以制作自己的实用程序函数来隐藏以下详细实现。

            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 实现中使用该实现是否安全。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2021-01-31
              • 1970-01-01
              • 2021-12-22
              • 2011-06-26
              • 1970-01-01
              • 2011-02-27
              • 2013-12-10
              • 1970-01-01
              相关资源
              最近更新 更多