From 728713258ffef4df053b85e0a3488334cbcba24c Mon Sep 17 00:00:00 2001 From: Chet Haase Date: Tue, 26 Feb 2013 16:12:13 -0700 Subject: [PATCH] DO NOT MERGE: ListView transient state fix ListView child views with transientState (setHasTransientState(true)) are not handled correctly when the data set changes, such as when an item is added or removed. The problem is that the transient views are cached by their position, but this position is out of sync between the ListView and the adapter until the ListView layout process is complete. A better way, which unfortunately only works on ListViews with stable IDs, is to cache the views by their itemID instead, and to use that ID to determine when and where to reuse/retrieve a transient view during the ListView layout. Issue #8254775 View.setHasTransient state has side-effects when deleting content in ListView Change-Id: I2fc25e71ed6655af30b9c3f47fdf014e9b667616 --- core/java/android/view/View.java | 3 +- core/java/android/widget/AbsListView.java | 72 ++++++++++++++++++++++++------- 2 files changed, 58 insertions(+), 17 deletions(-) diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 3d50fc896a5b..2fa9484b6f4a 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -6075,8 +6075,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mTransientStateCount = 0; Log.e(VIEW_LOG_TAG, "hasTransientState decremented below 0: " + "unmatched pair of setHasTransientState calls"); - } - if ((hasTransientState && mTransientStateCount == 1) || + } else if ((hasTransientState && mTransientStateCount == 1) || (!hasTransientState && mTransientStateCount == 0)) { // update flag if we've just incremented up from 0 or decremented down to 0 mPrivateFlags2 = (mPrivateFlags2 & ~PFLAG2_HAS_TRANSIENT_STATE) | diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index 396fd6821cde..d659110b1266 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -6167,6 +6167,7 @@ public abstract class AbsListView extends AdapterView implements Te private ArrayList mSkippedScrap; private SparseArray mTransientStateViews; + private LongSparseArray mTransientStateViewsById; public void setViewTypeCount(int viewTypeCount) { if (viewTypeCount < 1) { @@ -6205,6 +6206,12 @@ public abstract class AbsListView extends AdapterView implements Te mTransientStateViews.valueAt(i).forceLayout(); } } + if (mTransientStateViewsById != null) { + final int count = mTransientStateViewsById.size(); + for (int i = 0; i < count; i++) { + mTransientStateViewsById.valueAt(i).forceLayout(); + } + } } public boolean shouldRecycleViewType(int viewType) { @@ -6234,6 +6241,9 @@ public abstract class AbsListView extends AdapterView implements Te if (mTransientStateViews != null) { mTransientStateViews.clear(); } + if (mTransientStateViewsById != null) { + mTransientStateViewsById.clear(); + } } /** @@ -6281,16 +6291,21 @@ public abstract class AbsListView extends AdapterView implements Te } View getTransientStateView(int position) { - if (mTransientStateViews == null) { - return null; + if (mAdapter != null && mAdapterHasStableIds && mTransientStateViewsById != null) { + long id = mAdapter.getItemId(position); + View result = mTransientStateViewsById.get(id); + mTransientStateViewsById.remove(id); + return result; } - final int index = mTransientStateViews.indexOfKey(position); - if (index < 0) { - return null; + if (mTransientStateViews != null) { + final int index = mTransientStateViews.indexOfKey(position); + if (index >= 0) { + View result = mTransientStateViews.valueAt(index); + mTransientStateViews.removeAt(index); + return result; + } } - final View result = mTransientStateViews.valueAt(index); - mTransientStateViews.removeAt(index); - return result; + return null; } /** @@ -6300,6 +6315,9 @@ public abstract class AbsListView extends AdapterView implements Te if (mTransientStateViews != null) { mTransientStateViews.clear(); } + if (mTransientStateViewsById != null) { + mTransientStateViewsById.clear(); + } } /** @@ -6342,11 +6360,18 @@ public abstract class AbsListView extends AdapterView implements Te mSkippedScrap.add(scrap); } if (scrapHasTransientState) { - if (mTransientStateViews == null) { - mTransientStateViews = new SparseArray(); - } scrap.dispatchStartTemporaryDetach(); - mTransientStateViews.put(position, scrap); + if (mAdapter != null && mAdapterHasStableIds) { + if (mTransientStateViewsById == null) { + mTransientStateViewsById = new LongSparseArray(); + } + mTransientStateViewsById.put(lp.itemId, scrap); + } else { + if (mTransientStateViews == null) { + mTransientStateViews = new SparseArray(); + } + mTransientStateViews.put(position, scrap); + } } return; } @@ -6405,10 +6430,18 @@ public abstract class AbsListView extends AdapterView implements Te removeDetachedView(victim, false); } if (scrapHasTransientState) { - if (mTransientStateViews == null) { - mTransientStateViews = new SparseArray(); + if (mAdapter != null && mAdapterHasStableIds) { + if (mTransientStateViewsById == null) { + mTransientStateViewsById = new LongSparseArray(); + } + long id = mAdapter.getItemId(mFirstActivePosition + i); + mTransientStateViewsById.put(id, victim); + } else { + if (mTransientStateViews == null) { + mTransientStateViews = new SparseArray(); + } + mTransientStateViews.put(mFirstActivePosition + i, victim); } - mTransientStateViews.put(mFirstActivePosition + i, victim); } continue; } @@ -6457,6 +6490,15 @@ public abstract class AbsListView extends AdapterView implements Te } } } + if (mTransientStateViewsById != null) { + for (int i = 0; i < mTransientStateViewsById.size(); i++) { + final View v = mTransientStateViewsById.valueAt(i); + if (!v.hasTransientState()) { + mTransientStateViewsById.removeAt(i); + i--; + } + } + } } /** -- 2.11.0