Only delay pressed feedback for Views inside a scrolling parent
Add a method on ViewGroup to determine whether it supports scrolling.
This allows us to show the pressed feedback immediately in many cases,
improving responsiveness of buttons, etc.
This patch also lengthens the timeout in order to reduce flashes
when the user is scrolling.
Change-Id: Ieb91ae7a1f8e8f7e87448f2a730381a53947996f
diff --git a/api/current.txt b/api/current.txt
index a98ffae..09881f0 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -21724,6 +21724,7 @@
method public void setOnHierarchyChangeListener(android.view.ViewGroup.OnHierarchyChangeListener);
method public void setPersistentDrawingCache(int);
method protected void setStaticTransformationsEnabled(boolean);
+ method public boolean shouldDelayChildPressedState();
method public boolean showContextMenuForChild(android.view.View);
method public android.view.ActionMode startActionModeForChild(android.view.View, android.view.ActionMode.Callback);
method public void startLayoutAnimation();
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 5af2e56..a25aa0d 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -5138,9 +5138,7 @@
(mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) &&
(event.getRepeatCount() == 0)) {
setPressed(true);
- if ((mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) {
- postCheckForLongClick(0);
- }
+ checkForLongClick(0);
return true;
}
break;
@@ -5535,12 +5533,33 @@
break;
case MotionEvent.ACTION_DOWN:
- if (mPendingCheckForTap == null) {
- mPendingCheckForTap = new CheckForTap();
- }
- mPrivateFlags |= PREPRESSED;
mHasPerformedLongPress = false;
- postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
+
+ // Walk up the hierarchy to determine if we're inside a scrolling container.
+ boolean isInScrollingContainer = false;
+ ViewParent p = getParent();
+ while (p != null && p instanceof ViewGroup) {
+ if (((ViewGroup) p).shouldDelayChildPressedState()) {
+ isInScrollingContainer = true;
+ break;
+ }
+ p = p.getParent();
+ }
+
+ // For views inside a scrolling container, delay the pressed feedback for
+ // a short period in case this is a scroll.
+ if (isInScrollingContainer) {
+ mPrivateFlags |= PREPRESSED;
+ if (mPendingCheckForTap == null) {
+ mPendingCheckForTap = new CheckForTap();
+ }
+ postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
+ } else {
+ // Not inside a scrolling container, so show the feedback right away
+ mPrivateFlags |= PRESSED;
+ refreshDrawableState();
+ checkForLongClick(0);
+ }
break;
case MotionEvent.ACTION_CANCEL:
@@ -11846,15 +11865,17 @@
}
}
- private void postCheckForLongClick(int delayOffset) {
- mHasPerformedLongPress = false;
+ private void checkForLongClick(int delayOffset) {
+ if ((mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) {
+ mHasPerformedLongPress = false;
- if (mPendingCheckForLongPress == null) {
- mPendingCheckForLongPress = new CheckForLongPress();
+ if (mPendingCheckForLongPress == null) {
+ mPendingCheckForLongPress = new CheckForLongPress();
+ }
+ mPendingCheckForLongPress.rememberWindowAttachCount();
+ postDelayed(mPendingCheckForLongPress,
+ ViewConfiguration.getLongPressTimeout() - delayOffset);
}
- mPendingCheckForLongPress.rememberWindowAttachCount();
- postDelayed(mPendingCheckForLongPress,
- ViewConfiguration.getLongPressTimeout() - delayOffset);
}
/**
@@ -12166,9 +12187,7 @@
mPrivateFlags &= ~PREPRESSED;
mPrivateFlags |= PRESSED;
refreshDrawableState();
- if ((mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) {
- postCheckForLongClick(ViewConfiguration.getTapTimeout());
- }
+ checkForLongClick(ViewConfiguration.getTapTimeout());
}
}
diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
index 94eb429..36bb046 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -95,7 +95,7 @@
* is a tap or a scroll. If the user does not move within this interval, it is
* considered to be a tap.
*/
- private static final int TAP_TIMEOUT = 115;
+ private static final int TAP_TIMEOUT = 180;
/**
* Defines the duration in milliseconds we will wait to see if a touch event
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 1a84175..d87026b 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -4973,6 +4973,19 @@
}
/**
+ * Return true if the pressed state should be delayed for children or descendants of this
+ * ViewGroup. Generally, this should be done for containers that can scroll, such as a List.
+ * This prevents the pressed state from appearing when the user is actually trying to scroll
+ * the content.
+ *
+ * The default implementation returns true for compatibility reasons. Subclasses that do
+ * not scroll should generally override this method and return false.
+ */
+ public boolean shouldDelayChildPressedState() {
+ return true;
+ }
+
+ /**
* LayoutParams are used by views to tell their parents how they want to be
* laid out. See
* {@link android.R.styleable#ViewGroup_Layout ViewGroup Layout Attributes}
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index b5d0492..7703ec7 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -1193,6 +1193,11 @@
mHTML5VideoViewProxy = null ;
}
+ @Override
+ public boolean shouldDelayChildPressedState() {
+ return true;
+ }
+
/**
* Adds accessibility APIs to JavaScript.
*
diff --git a/core/java/android/widget/AbsoluteLayout.java b/core/java/android/widget/AbsoluteLayout.java
index ac82af7..7df6aab 100644
--- a/core/java/android/widget/AbsoluteLayout.java
+++ b/core/java/android/widget/AbsoluteLayout.java
@@ -141,6 +141,11 @@
return new LayoutParams(p);
}
+ @Override
+ public boolean shouldDelayChildPressedState() {
+ return false;
+ }
+
/**
* Per-child layout information associated with AbsoluteLayout.
* See
diff --git a/core/java/android/widget/FrameLayout.java b/core/java/android/widget/FrameLayout.java
index 590a768..0659063 100644
--- a/core/java/android/widget/FrameLayout.java
+++ b/core/java/android/widget/FrameLayout.java
@@ -485,6 +485,11 @@
return new FrameLayout.LayoutParams(getContext(), attrs);
}
+ @Override
+ public boolean shouldDelayChildPressedState() {
+ return false;
+ }
+
/**
* {@inheritDoc}
*/
diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java
index dbe9288..ed913a4 100644
--- a/core/java/android/widget/LinearLayout.java
+++ b/core/java/android/widget/LinearLayout.java
@@ -201,6 +201,11 @@
mShowDividers = showDividers;
}
+ @Override
+ public boolean shouldDelayChildPressedState() {
+ return false;
+ }
+
/**
* @return A flag set indicating how dividers should be shown around items.
* @see #setShowDividers(int)
diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java
index a47359f..9069283 100644
--- a/core/java/android/widget/RelativeLayout.java
+++ b/core/java/android/widget/RelativeLayout.java
@@ -186,6 +186,11 @@
a.recycle();
}
+ @Override
+ public boolean shouldDelayChildPressedState() {
+ return false;
+ }
+
/**
* Defines which View is ignored when the gravity is applied. This setting has no
* effect if the gravity is <code>Gravity.LEFT | Gravity.TOP</code>.
diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java
index ade3a0a..27edb88 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -162,6 +162,11 @@
}
@Override
+ public boolean shouldDelayChildPressedState() {
+ return true;
+ }
+
+ @Override
protected float getTopFadingEdgeStrength() {
if (getChildCount() == 0) {
return 0.0f;
diff --git a/core/java/com/android/internal/widget/ActionBarContextView.java b/core/java/com/android/internal/widget/ActionBarContextView.java
index 70fb3b2..ccaa146 100644
--- a/core/java/com/android/internal/widget/ActionBarContextView.java
+++ b/core/java/com/android/internal/widget/ActionBarContextView.java
@@ -448,4 +448,9 @@
@Override
public void onAnimationRepeat(Animator animation) {
}
+
+ @Override
+ public boolean shouldDelayChildPressedState() {
+ return false;
+ }
}
diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java
index 0c13f7b..d035744 100644
--- a/core/java/com/android/internal/widget/ActionBarView.java
+++ b/core/java/com/android/internal/widget/ActionBarView.java
@@ -232,6 +232,11 @@
mHomeLayout.setFocusable(true);
}
+ @Override
+ public boolean shouldDelayChildPressedState() {
+ return false;
+ }
+
public void initProgress() {
mProgressView = new ProgressBar(mContext, null, 0, mProgressStyle);
mProgressView.setId(R.id.progress_horizontal);