From a440b002aa59e1455bdfa2c5a1ca51c74bbc19ac Mon Sep 17 00:00:00 2001 From: Romain Guy Date: Wed, 24 Feb 2010 15:57:54 -0800 Subject: [PATCH] Prevents ListView items children to modify properties of other children. Bug #2464502 This fix introduce a new dispatch mechanism to tell views when they are temporary detached/reattached from/to a ListView. This is very important to remove pending callbacks or cleanup temporary states. This change also modifies TextView which was relying on that callback in a very particular case: a focused EditText in a ListView. The modified code acts only when in that case, not if onStart/FinishTemporaryDetach() is called via dispatch*() (== recycled views in ListView.) --- core/java/android/view/View.java | 40 ++++++++++++++++------- core/java/android/view/ViewGroup.java | 26 +++++++++++++++ core/java/android/widget/AbsListView.java | 8 +++-- core/java/android/widget/ListView.java | 1 - core/java/android/widget/TextView.java | 18 ++++++++-- 5 files changed, 77 insertions(+), 16 deletions(-) diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 2eb633f4d4c82..f6f52356271d3 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -3650,14 +3650,27 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility } /** - * This is called when a container is going to temporarily detach a child - * that currently has focus, with + * @hide + */ + public void dispatchStartTemporaryDetach() { + onStartTemporaryDetach(); + } + + /** + * This is called when a container is going to temporarily detach a child, with * {@link ViewGroup#detachViewFromParent(View) ViewGroup.detachViewFromParent}. * It will either be followed by {@link #onFinishTemporaryDetach()} or - * {@link #onDetachedFromWindow()} when the container is done. Generally - * this is currently only done ListView for a view with focus. + * {@link #onDetachedFromWindow()} when the container is done. */ public void onStartTemporaryDetach() { + removeUnsetPressCallback(); + } + + /** + * @hide + */ + public void dispatchFinishTemporaryDetach() { + onFinishTemporaryDetach(); } /** @@ -4361,6 +4374,16 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility } } + /** + * Remove the prepress detection timer. + */ + private void removeUnsetPressCallback() { + if ((mPrivateFlags & PRESSED) != 0 && mUnsetPressedState != null) { + setPressed(false); + removeCallbacks(mUnsetPressedState); + } + } + /** * Remove the tap detection timer. */ @@ -5886,6 +5909,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * @see #onAttachedToWindow() */ protected void onDetachedFromWindow() { + removeUnsetPressCallback(); removeLongPressCallback(); destroyDrawingCache(); } @@ -8731,7 +8755,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility boolean clampedX, boolean clampedY) { // Intentionally empty. } - + /** * A MeasureSpec encapsulates the layout requirements passed from parent to child. * Each MeasureSpec represents a requirement for either the width or the height. @@ -9181,12 +9205,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility */ boolean mRecomputeGlobalAttributes; - /** - * Set to true when attributes (like mKeepScreenOn) need to be - * recomputed. - */ - boolean mAttributesChanged; - /** * Set during a traveral if any views want to keep the screen on. */ diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 066321505a7d6..aac1068373905 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -1065,6 +1065,32 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } return false; } + + /** + * {@inheritDoc} + */ + @Override + public void dispatchStartTemporaryDetach() { + super.dispatchStartTemporaryDetach(); + final int count = mChildrenCount; + final View[] children = mChildren; + for (int i = 0; i < count; i++) { + children[i].dispatchStartTemporaryDetach(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void dispatchFinishTemporaryDetach() { + super.dispatchFinishTemporaryDetach(); + final int count = mChildrenCount; + final View[] children = mChildren; + for (int i = 0; i < count; i++) { + children[i].dispatchFinishTemporaryDetach(); + } + } /** * {@inheritDoc} diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index 17d5bb75978ba..9ddfeff271476 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -1314,7 +1314,8 @@ public abstract class AbsListView extends AdapterView implements Te position, -1); } } else { - isScrap[0] = true; + isScrap[0] = true; + child.dispatchFinishTemporaryDetach(); } } else { child = mAdapter.getView(position, null, this); @@ -4145,8 +4146,10 @@ public abstract class AbsListView extends AdapterView implements Te } if (mViewTypeCount == 1) { + scrap.dispatchStartTemporaryDetach(); mCurrentScrap.add(scrap); } else { + scrap.dispatchStartTemporaryDetach(); mScrapViews[viewType].add(scrap); } @@ -4165,7 +4168,7 @@ public abstract class AbsListView extends AdapterView implements Te ArrayList scrapViews = mCurrentScrap; final int count = activeViews.length; - for (int i = 0; i < count; ++i) { + for (int i = count - 1; i >= 0; i--) { final View victim = activeViews[i]; if (victim != null) { int whichScrap = ((AbsListView.LayoutParams) victim.getLayoutParams()).viewType; @@ -4181,6 +4184,7 @@ public abstract class AbsListView extends AdapterView implements Te if (multipleScraps) { scrapViews = mScrapViews[whichScrap]; } + victim.dispatchStartTemporaryDetach(); scrapViews.add(victim); if (hasListener) { diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java index 2feed036c8d5d..8d688a559ba9f 100644 --- a/core/java/android/widget/ListView.java +++ b/core/java/android/widget/ListView.java @@ -1485,7 +1485,6 @@ public class ListView extends AbsListView { } // Clear out old views - //removeAllViewsInLayout(); detachAllViewsFromParent(); switch (mLayoutMode) { diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index cea6d3b9266dc..cb44fa8c88421 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -198,6 +198,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private boolean mFreezesText; private boolean mFrozenWithFocus; private boolean mTemporaryDetach; + private boolean mDispatchTemporaryDetach; private boolean mEatTouchRelease = false; private boolean mScrolled = false; @@ -6371,14 +6372,27 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mBlink.postAtTime(mBlink, mShowCursor + BLINK); } + @Override + public void dispatchFinishTemporaryDetach() { + mDispatchTemporaryDetach = true; + super.dispatchFinishTemporaryDetach(); + mDispatchTemporaryDetach = false; + } + @Override public void onStartTemporaryDetach() { - mTemporaryDetach = true; + super.onStartTemporaryDetach(); + // Only track when onStartTemporaryDetach() is called directly, + // usually because this instance is an editable field in a list + if (!mDispatchTemporaryDetach) mTemporaryDetach = true; } @Override public void onFinishTemporaryDetach() { - mTemporaryDetach = false; + super.onFinishTemporaryDetach(); + // Only track when onStartTemporaryDetach() is called directly, + // usually because this instance is an editable field in a list + if (!mDispatchTemporaryDetach) mTemporaryDetach = false; } @Override