diff options
| author | Adam Powell <adamp@google.com> | 2011-10-07 11:16:51 -0700 |
|---|---|---|
| committer | Android (Google) Code Review <android-gerrit@google.com> | 2011-10-07 11:16:51 -0700 |
| commit | e0f2515b444ceae20571cbe29e44237d259f54bd (patch) | |
| tree | 27b356e3ff22ab23ce6be9af914811dc474bb731 /core/java/android | |
| parent | 41c95f3642323ec4cfdc84a3bb70537af0da8cca (diff) | |
| parent | 14c080497e427c4dd9395df5bacb28bcc4291a32 (diff) | |
| download | frameworks_base-e0f2515b444ceae20571cbe29e44237d259f54bd.zip frameworks_base-e0f2515b444ceae20571cbe29e44237d259f54bd.tar.gz frameworks_base-e0f2515b444ceae20571cbe29e44237d259f54bd.tar.bz2 | |
Merge "Fix bug 5399568 - ListView check states inconsistent after data set change"
Diffstat (limited to 'core/java/android')
| -rw-r--r-- | core/java/android/widget/AbsListView.java | 102 |
1 files changed, 86 insertions, 16 deletions
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index c218f23..0d287cf 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -237,9 +237,11 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te SparseBooleanArray mCheckStates; /** - * Running state of which IDs are currently checked + * Running state of which IDs are currently checked. + * If there is a value for a given key, the checked state for that ID is true + * and the value holds the last known position in the adapter for that id. */ - LongSparseArray<Boolean> mCheckedIdStates; + LongSparseArray<Integer> mCheckedIdStates; /** * Controls how the next layout will happen @@ -472,6 +474,13 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te static final int OVERSCROLL_LIMIT_DIVISOR = 3; /** + * How many positions in either direction we will search to try to + * find a checked item with a stable ID that moved position across + * a data set change. If the item isn't found it will be unselected. + */ + private static final int CHECK_POSITION_SEARCH_DISTANCE = 20; + + /** * Used to request a layout when we changed touch mode */ private static final int TOUCH_MODE_UNKNOWN = -1; @@ -806,7 +815,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te if (adapter != null) { if (mChoiceMode != CHOICE_MODE_NONE && mAdapter.hasStableIds() && mCheckedIdStates == null) { - mCheckedIdStates = new LongSparseArray<Boolean>(); + mCheckedIdStates = new LongSparseArray<Integer>(); } } @@ -901,7 +910,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te return new long[0]; } - final LongSparseArray<Boolean> idStates = mCheckedIdStates; + final LongSparseArray<Integer> idStates = mCheckedIdStates; final int count = idStates.size(); final long[] ids = new long[count]; @@ -948,7 +957,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te mCheckStates.put(position, value); if (mCheckedIdStates != null && mAdapter.hasStableIds()) { if (value) { - mCheckedIdStates.put(mAdapter.getItemId(position), Boolean.TRUE); + mCheckedIdStates.put(mAdapter.getItemId(position), position); } else { mCheckedIdStates.delete(mAdapter.getItemId(position)); } @@ -980,7 +989,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te if (value) { mCheckStates.put(position, true); if (updateIds) { - mCheckedIdStates.put(mAdapter.getItemId(position), Boolean.TRUE); + mCheckedIdStates.put(mAdapter.getItemId(position), position); } mCheckedItemCount = 1; } else if (mCheckStates.size() == 0 || !mCheckStates.valueAt(0)) { @@ -1010,7 +1019,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te mCheckStates.put(position, newValue); if (mCheckedIdStates != null && mAdapter.hasStableIds()) { if (newValue) { - mCheckedIdStates.put(mAdapter.getItemId(position), Boolean.TRUE); + mCheckedIdStates.put(mAdapter.getItemId(position), position); } else { mCheckedIdStates.delete(mAdapter.getItemId(position)); } @@ -1032,7 +1041,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te mCheckStates.put(position, true); if (mCheckedIdStates != null && mAdapter.hasStableIds()) { mCheckedIdStates.clear(); - mCheckedIdStates.put(mAdapter.getItemId(position), Boolean.TRUE); + mCheckedIdStates.put(mAdapter.getItemId(position), position); } mCheckedItemCount = 1; } else if (mCheckStates.size() == 0 || !mCheckStates.valueAt(0)) { @@ -1081,7 +1090,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te mCheckStates = new SparseBooleanArray(); } if (mCheckedIdStates == null && mAdapter != null && mAdapter.hasStableIds()) { - mCheckedIdStates = new LongSparseArray<Boolean>(); + mCheckedIdStates = new LongSparseArray<Integer>(); } // Modal multi-choice mode only has choices when the mode is active. Clear them. if (mChoiceMode == CHOICE_MODE_MULTIPLE_MODAL) { @@ -1411,7 +1420,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te boolean inActionMode; int checkedItemCount; SparseBooleanArray checkState; - LongSparseArray<Boolean> checkIdState; + LongSparseArray<Integer> checkIdState; /** * Constructor called from {@link AbsListView#onSaveInstanceState()} @@ -1435,10 +1444,14 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te checkedItemCount = in.readInt(); checkState = in.readSparseBooleanArray(); long[] idState = in.createLongArray(); + int[] idPositions = in.createIntArray(); - if (idState.length > 0) { - checkIdState = new LongSparseArray<Boolean>(); - checkIdState.setValues(idState, Boolean.TRUE); + final int idLength = idState.length; + if (idLength > 0) { + checkIdState = new LongSparseArray<Integer>(); + for (int i = 0; i < idLength; i++) { + checkIdState.put(idState[i], idPositions[i]); + } } } @@ -1455,6 +1468,15 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te out.writeInt(checkedItemCount); out.writeSparseBooleanArray(checkState); out.writeLongArray(checkIdState != null ? checkIdState.getKeys() : new long[0]); + + int size = checkIdState != null ? checkIdState.size() : 0; + int[] idPositions = new int[size]; + if (size > 0) { + for (int i = 0; i < size; i++) { + idPositions[i] = checkIdState.valueAt(i); + } + out.writeIntArray(idPositions); + } } @Override @@ -1549,7 +1571,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te ss.checkState = mCheckStates.clone(); } if (mCheckedIdStates != null) { - final LongSparseArray<Boolean> idState = new LongSparseArray<Boolean>(); + final LongSparseArray<Integer> idState = new LongSparseArray<Integer>(); final int count = mCheckedIdStates.size(); for (int i = 0; i < count; i++) { idState.put(mCheckedIdStates.keyAt(i), mCheckedIdStates.valueAt(i)); @@ -4770,15 +4792,63 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te return selectedPos >= 0; } + void confirmCheckedPositionsById() { + // Clear out the positional check states, we'll rebuild it below from IDs. + mCheckStates.clear(); + + boolean checkedCountChanged = false; + for (int checkedIndex = 0; checkedIndex < mCheckedIdStates.size(); checkedIndex++) { + final long id = mCheckedIdStates.keyAt(checkedIndex); + final int lastPos = mCheckedIdStates.valueAt(checkedIndex); + + final long lastPosId = mAdapter.getItemId(lastPos); + if (id != lastPosId) { + // Look around to see if the ID is nearby. If not, uncheck it. + final int start = Math.max(0, lastPos - CHECK_POSITION_SEARCH_DISTANCE); + final int end = Math.min(lastPos + CHECK_POSITION_SEARCH_DISTANCE, mItemCount); + boolean found = false; + for (int searchPos = start; searchPos < end; searchPos++) { + final long searchId = mAdapter.getItemId(searchPos); + if (id == searchId) { + found = true; + mCheckStates.put(searchPos, true); + mCheckedIdStates.setValueAt(checkedIndex, searchPos); + break; + } + } + + if (!found) { + mCheckedIdStates.delete(id); + checkedIndex--; + mCheckedItemCount--; + checkedCountChanged = true; + if (mChoiceActionMode != null && mMultiChoiceModeCallback != null) { + mMultiChoiceModeCallback.onItemCheckedStateChanged(mChoiceActionMode, + lastPos, id, false); + } + } + } else { + mCheckStates.put(lastPos, true); + } + } + + if (checkedCountChanged && mChoiceActionMode != null) { + mChoiceActionMode.invalidate(); + } + } + @Override protected void handleDataChanged() { int count = mItemCount; int lastHandledItemCount = mLastHandledItemCount; mLastHandledItemCount = mItemCount; - if (count > 0) { - int newPos; + if (mChoiceMode != CHOICE_MODE_NONE && mAdapter != null && mAdapter.hasStableIds()) { + confirmCheckedPositionsById(); + } + if (count > 0) { + int newPos; int selectablePos; // Find the row we are supposed to sync to |
