Merge "Handle DOWN/MOVE/UP gestures with fling. Bug: 5265814"
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 05d4f05..d7fb7a0 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -2771,15 +2771,21 @@
}
}
- private boolean startScrollIfNeeded(int deltaY) {
+ private boolean startScrollIfNeeded(int y) {
// Check if we have moved far enough that it looks more like a
// scroll than a tap
+ final int deltaY = y - mMotionY;
final int distance = Math.abs(deltaY);
final boolean overscroll = mScrollY != 0;
if (overscroll || distance > mTouchSlop) {
createScrollingCache();
- mTouchMode = overscroll ? TOUCH_MODE_OVERSCROLL : TOUCH_MODE_SCROLL;
- mMotionCorrection = deltaY;
+ if (overscroll) {
+ mTouchMode = TOUCH_MODE_OVERSCROLL;
+ mMotionCorrection = 0;
+ } else {
+ mTouchMode = TOUCH_MODE_SCROLL;
+ mMotionCorrection = deltaY > 0 ? mTouchSlop : -mTouchSlop;
+ }
final Handler handler = getHandler();
// Handler should not be null unless the AbsListView is not attached to a
// window, which would make it very hard to scroll it... but the monkeys
@@ -2799,12 +2805,176 @@
if (parent != null) {
parent.requestDisallowInterceptTouchEvent(true);
}
+ scrollIfNeeded(y);
return true;
}
return false;
}
+ private void scrollIfNeeded(int y) {
+ final int rawDeltaY = y - mMotionY;
+ final int deltaY = rawDeltaY - mMotionCorrection;
+ int incrementalDeltaY = mLastY != Integer.MIN_VALUE ? y - mLastY : deltaY;
+
+ if (mTouchMode == TOUCH_MODE_SCROLL) {
+ if (PROFILE_SCROLLING) {
+ if (!mScrollProfilingStarted) {
+ Debug.startMethodTracing("AbsListViewScroll");
+ mScrollProfilingStarted = true;
+ }
+ }
+
+ if (mScrollStrictSpan == null) {
+ // If it's non-null, we're already in a scroll.
+ mScrollStrictSpan = StrictMode.enterCriticalSpan("AbsListView-scroll");
+ }
+
+ if (y != mLastY) {
+ // We may be here after stopping a fling and continuing to scroll.
+ // If so, we haven't disallowed intercepting touch events yet.
+ // Make sure that we do so in case we're in a parent that can intercept.
+ if ((mGroupFlags & FLAG_DISALLOW_INTERCEPT) == 0 &&
+ Math.abs(rawDeltaY) > mTouchSlop) {
+ final ViewParent parent = getParent();
+ if (parent != null) {
+ parent.requestDisallowInterceptTouchEvent(true);
+ }
+ }
+
+ final int motionIndex;
+ if (mMotionPosition >= 0) {
+ motionIndex = mMotionPosition - mFirstPosition;
+ } else {
+ // If we don't have a motion position that we can reliably track,
+ // pick something in the middle to make a best guess at things below.
+ motionIndex = getChildCount() / 2;
+ }
+
+ int motionViewPrevTop = 0;
+ View motionView = this.getChildAt(motionIndex);
+ if (motionView != null) {
+ motionViewPrevTop = motionView.getTop();
+ }
+
+ // No need to do all this work if we're not going to move anyway
+ boolean atEdge = false;
+ if (incrementalDeltaY != 0) {
+ atEdge = trackMotionScroll(deltaY, incrementalDeltaY);
+ }
+
+ // Check to see if we have bumped into the scroll limit
+ motionView = this.getChildAt(motionIndex);
+ if (motionView != null) {
+ // Check if the top of the motion view is where it is
+ // supposed to be
+ final int motionViewRealTop = motionView.getTop();
+ if (atEdge) {
+ // Apply overscroll
+
+ int overscroll = -incrementalDeltaY -
+ (motionViewRealTop - motionViewPrevTop);
+ overScrollBy(0, overscroll, 0, mScrollY, 0, 0,
+ 0, mOverscrollDistance, true);
+ if (Math.abs(mOverscrollDistance) == Math.abs(mScrollY)) {
+ // Don't allow overfling if we're at the edge.
+ if (mVelocityTracker != null) {
+ mVelocityTracker.clear();
+ }
+ }
+
+ final int overscrollMode = getOverScrollMode();
+ if (overscrollMode == OVER_SCROLL_ALWAYS ||
+ (overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS &&
+ !contentFits())) {
+ mDirection = 0; // Reset when entering overscroll.
+ mTouchMode = TOUCH_MODE_OVERSCROLL;
+ if (rawDeltaY > 0) {
+ mEdgeGlowTop.onPull((float) overscroll / getHeight());
+ if (!mEdgeGlowBottom.isFinished()) {
+ mEdgeGlowBottom.onRelease();
+ }
+ } else if (rawDeltaY < 0) {
+ mEdgeGlowBottom.onPull((float) overscroll / getHeight());
+ if (!mEdgeGlowTop.isFinished()) {
+ mEdgeGlowTop.onRelease();
+ }
+ }
+ }
+ }
+ mMotionY = y;
+ invalidate();
+ }
+ mLastY = y;
+ }
+ } else if (mTouchMode == TOUCH_MODE_OVERSCROLL) {
+ if (y != mLastY) {
+ final int oldScroll = mScrollY;
+ final int newScroll = oldScroll - incrementalDeltaY;
+ int newDirection = y > mLastY ? 1 : -1;
+
+ if (mDirection == 0) {
+ mDirection = newDirection;
+ }
+
+ int overScrollDistance = -incrementalDeltaY;
+ if ((newScroll < 0 && oldScroll >= 0) || (newScroll > 0 && oldScroll <= 0)) {
+ overScrollDistance = -oldScroll;
+ incrementalDeltaY += overScrollDistance;
+ } else {
+ incrementalDeltaY = 0;
+ }
+
+ if (overScrollDistance != 0) {
+ overScrollBy(0, overScrollDistance, 0, mScrollY, 0, 0,
+ 0, mOverscrollDistance, true);
+ final int overscrollMode = getOverScrollMode();
+ if (overscrollMode == OVER_SCROLL_ALWAYS ||
+ (overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS &&
+ !contentFits())) {
+ if (rawDeltaY > 0) {
+ mEdgeGlowTop.onPull((float) overScrollDistance / getHeight());
+ if (!mEdgeGlowBottom.isFinished()) {
+ mEdgeGlowBottom.onRelease();
+ }
+ } else if (rawDeltaY < 0) {
+ mEdgeGlowBottom.onPull((float) overScrollDistance / getHeight());
+ if (!mEdgeGlowTop.isFinished()) {
+ mEdgeGlowTop.onRelease();
+ }
+ }
+ invalidate();
+ }
+ }
+
+ if (incrementalDeltaY != 0) {
+ // Coming back to 'real' list scrolling
+ mScrollY = 0;
+ invalidateParentIfNeeded();
+
+ // No need to do all this work if we're not going to move anyway
+ if (incrementalDeltaY != 0) {
+ trackMotionScroll(incrementalDeltaY, incrementalDeltaY);
+ }
+
+ mTouchMode = TOUCH_MODE_SCROLL;
+
+ // We did not scroll the full amount. Treat this essentially like the
+ // start of a new touch scroll
+ final int motionPosition = findClosestMotionRow(y);
+
+ mMotionCorrection = 0;
+ View motionView = getChildAt(motionPosition - mFirstPosition);
+ mMotionViewOriginalTop = motionView != null ? motionView.getTop() : 0;
+ mMotionY = y;
+ mMotionPosition = motionPosition;
+ }
+ mLastY = y;
+ mDirection = newDirection;
+ }
+ }
+ }
+
public void onTouchModeChanged(boolean isInTouchMode) {
if (isInTouchMode) {
// Get rid of the selection when we enter touch mode
@@ -2856,7 +3026,6 @@
final int action = ev.getAction();
View v;
- int deltaY;
initVelocityTrackerIfNotExists();
mVelocityTracker.addMovement(ev);
@@ -2935,183 +3104,19 @@
mActivePointerId = ev.getPointerId(pointerIndex);
}
final int y = (int) ev.getY(pointerIndex);
- deltaY = y - mMotionY;
switch (mTouchMode) {
case TOUCH_MODE_DOWN:
case TOUCH_MODE_TAP:
case TOUCH_MODE_DONE_WAITING:
// Check if we have moved far enough that it looks more like a
// scroll than a tap
- startScrollIfNeeded(deltaY);
+ startScrollIfNeeded(y);
break;
case TOUCH_MODE_SCROLL:
- if (PROFILE_SCROLLING) {
- if (!mScrollProfilingStarted) {
- Debug.startMethodTracing("AbsListViewScroll");
- mScrollProfilingStarted = true;
- }
- }
-
- if (mScrollStrictSpan == null) {
- // If it's non-null, we're already in a scroll.
- mScrollStrictSpan = StrictMode.enterCriticalSpan("AbsListView-scroll");
- }
-
- if (y != mLastY) {
- // We may be here after stopping a fling and continuing to scroll.
- // If so, we haven't disallowed intercepting touch events yet.
- // Make sure that we do so in case we're in a parent that can intercept.
- if ((mGroupFlags & FLAG_DISALLOW_INTERCEPT) == 0 &&
- Math.abs(deltaY) > mTouchSlop) {
- final ViewParent parent = getParent();
- if (parent != null) {
- parent.requestDisallowInterceptTouchEvent(true);
- }
- }
-
- final int rawDeltaY = deltaY;
- deltaY -= mMotionCorrection;
- int incrementalDeltaY = mLastY != Integer.MIN_VALUE ? y - mLastY : deltaY;
-
- final int motionIndex;
- if (mMotionPosition >= 0) {
- motionIndex = mMotionPosition - mFirstPosition;
- } else {
- // If we don't have a motion position that we can reliably track,
- // pick something in the middle to make a best guess at things below.
- motionIndex = getChildCount() / 2;
- }
-
- int motionViewPrevTop = 0;
- View motionView = this.getChildAt(motionIndex);
- if (motionView != null) {
- motionViewPrevTop = motionView.getTop();
- }
-
- // No need to do all this work if we're not going to move anyway
- boolean atEdge = false;
- if (incrementalDeltaY != 0) {
- atEdge = trackMotionScroll(deltaY, incrementalDeltaY);
- }
-
- // Check to see if we have bumped into the scroll limit
- motionView = this.getChildAt(motionIndex);
- if (motionView != null) {
- // Check if the top of the motion view is where it is
- // supposed to be
- final int motionViewRealTop = motionView.getTop();
- if (atEdge) {
- // Apply overscroll
-
- int overscroll = -incrementalDeltaY -
- (motionViewRealTop - motionViewPrevTop);
- overScrollBy(0, overscroll, 0, mScrollY, 0, 0,
- 0, mOverscrollDistance, true);
- if (Math.abs(mOverscrollDistance) == Math.abs(mScrollY)) {
- // Don't allow overfling if we're at the edge.
- if (mVelocityTracker != null) {
- mVelocityTracker.clear();
- }
- }
-
- final int overscrollMode = getOverScrollMode();
- if (overscrollMode == OVER_SCROLL_ALWAYS ||
- (overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS &&
- !contentFits())) {
- mDirection = 0; // Reset when entering overscroll.
- mTouchMode = TOUCH_MODE_OVERSCROLL;
- if (rawDeltaY > 0) {
- mEdgeGlowTop.onPull((float) overscroll / getHeight());
- if (!mEdgeGlowBottom.isFinished()) {
- mEdgeGlowBottom.onRelease();
- }
- } else if (rawDeltaY < 0) {
- mEdgeGlowBottom.onPull((float) overscroll / getHeight());
- if (!mEdgeGlowTop.isFinished()) {
- mEdgeGlowTop.onRelease();
- }
- }
- }
- }
- mMotionY = y;
- invalidate();
- }
- mLastY = y;
- }
- break;
-
case TOUCH_MODE_OVERSCROLL:
- if (y != mLastY) {
- final int rawDeltaY = deltaY;
- deltaY -= mMotionCorrection;
- int incrementalDeltaY = mLastY != Integer.MIN_VALUE ? y - mLastY : deltaY;
-
- final int oldScroll = mScrollY;
- final int newScroll = oldScroll - incrementalDeltaY;
- int newDirection = y > mLastY ? 1 : -1;
-
- if (mDirection == 0) {
- mDirection = newDirection;
- }
-
- int overScrollDistance = -incrementalDeltaY;
- if ((newScroll < 0 && oldScroll >= 0) || (newScroll > 0 && oldScroll <= 0)) {
- overScrollDistance = -oldScroll;
- incrementalDeltaY += overScrollDistance;
- } else {
- incrementalDeltaY = 0;
- }
-
- if (overScrollDistance != 0) {
- overScrollBy(0, overScrollDistance, 0, mScrollY, 0, 0,
- 0, mOverscrollDistance, true);
- final int overscrollMode = getOverScrollMode();
- if (overscrollMode == OVER_SCROLL_ALWAYS ||
- (overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS &&
- !contentFits())) {
- if (rawDeltaY > 0) {
- mEdgeGlowTop.onPull((float) overScrollDistance / getHeight());
- if (!mEdgeGlowBottom.isFinished()) {
- mEdgeGlowBottom.onRelease();
- }
- } else if (rawDeltaY < 0) {
- mEdgeGlowBottom.onPull((float) overScrollDistance / getHeight());
- if (!mEdgeGlowTop.isFinished()) {
- mEdgeGlowTop.onRelease();
- }
- }
- invalidate();
- }
- }
-
- if (incrementalDeltaY != 0) {
- // Coming back to 'real' list scrolling
- mScrollY = 0;
- invalidateParentIfNeeded();
-
- // No need to do all this work if we're not going to move anyway
- if (incrementalDeltaY != 0) {
- trackMotionScroll(incrementalDeltaY, incrementalDeltaY);
- }
-
- mTouchMode = TOUCH_MODE_SCROLL;
-
- // We did not scroll the full amount. Treat this essentially like the
- // start of a new touch scroll
- final int motionPosition = findClosestMotionRow(y);
-
- mMotionCorrection = 0;
- View motionView = getChildAt(motionPosition - mFirstPosition);
- mMotionViewOriginalTop = motionView != null ? motionView.getTop() : 0;
- mMotionY = y;
- mMotionPosition = motionPosition;
- }
- mLastY = y;
- mDirection = newDirection;
- }
+ scrollIfNeeded(y);
break;
}
-
break;
}
@@ -3542,7 +3547,7 @@
final int y = (int) ev.getY(pointerIndex);
initVelocityTrackerIfNotExists();
mVelocityTracker.addMovement(ev);
- if (startScrollIfNeeded(y - mMotionY)) {
+ if (startScrollIfNeeded(y)) {
return true;
}
break;