2012-06-29 84 views
14

當配置更改發生時,我的ListView複選框狀態會丟失,我明白爲什麼。 我試圖實施在Bundle中存儲SparseBooleanArray的最佳方法是什麼?

public void onSaveInstanceState(final Bundle outState) 

在我的片段之一。所以我只是想知道在outState中存儲我的SparseBooleanArray的最簡單方法是什麼。

而且,我有點困惑,因爲ListView控件有方法:

getListView().getCheckedItemPositions(); 

這是什麼好?

+0

根本不需要使用'onSaveInstanceState()'。見http://stackoverflow.com/questions/24294919/maintain-item-selection-on-orientation-change – faizal

回答

0

您可以爲該數據結構創建一些序列化方案,或切換到HashSet並僅存儲檢查的列表索引位置。由於HashSet是可序列化的,因此可以將其放入實例狀態Bundle中。

0

我正在嘗試做同樣的事情。最初我是使用一個HashMap

How to send hashmap value to another activity using an intent

,因爲它延長了Serializable接口活動之間通過。然而,之後做一些研究:

SparseBooleanArrays意在比使用HashMap映射IntegersBooleans更有效率。」

但是,我找不到一個簡單的方法來存儲這個數據結構。我曾設想抓取SparseBooleanArray的值並將其存儲在HashMap中,以便它可以在活動之間傳遞。但是這似乎增加了更多的複雜性。

不幸的是,我想我會恢復到使用包含HashMap來存儲我的選中的複選框列表

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

我看不出有任何問題的答案的最後一部分:

而且,我有點困惑,因爲ListView有方法:

getListView().getCheckedItemPositions();

這有什麼用?

假設您將複選框與動作模式一起使用,動作模式本身將存儲檢查位置的狀態。在正常模式下,按住即可突出顯示一個項目,啓用操作模式,然後允許用戶點擊其他項目以檢查它們。

方向改變會發生什麼?動作模式恢復並保留高光。您的複選框不是,但顯然是檢查的數據。

調整方向後,調用onCreateActionMode()。在這種方法中(但不是之前),getListView().getCheckedItemPositions()將返回您可能要查找的SparseBooleanArray

關於這一點的精妙之處在於,如果這是您的唯一用例SparseBooleanArray,您甚至不必擔心自己存儲它。您可以從已經的系統中檢索它,並將其存儲在整個方向更改中。

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

37

就我而言,我最終通過實施圍繞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€

+0

偉大的解決方案。精彩地工作。好工作 – Stephen

+0

從Bundle讀取時,應該將bundle.getParcelable(「myBooleanArray」)強制轉換爲(SparseBooleanArray)。 – localhost

+0

感謝編輯@localhost! – opsidao

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


} 
0

我試圖解決創建SerializableSparseArray延長SparseArray使其能夠把SparseArray成束通過Bundle.putSerializable電話。

但我發現我無法從包中保存的對象onRestoreInstanceState。深入研究這個問題,我發現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>直接捆綁而不用與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實現中使用該實現是否安全。

相關問題