Merge "Handle DOWN/MOVE/UP gestures with fling. Bug: 5265814"

This commit is contained in:
Jeff Brown
2011-09-12 11:26:58 -07:00
committed by Android (Google) Code Review

View File

@@ -2771,15 +2771,21 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
}
}
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 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
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 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
final int action = ev.getAction();
View v;
int deltaY;
initVelocityTrackerIfNotExists();
mVelocityTracker.addMovement(ev);
@@ -2935,183 +3104,19 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
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 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
final int y = (int) ev.getY(pointerIndex);
initVelocityTrackerIfNotExists();
mVelocityTracker.addMovement(ev);
if (startScrollIfNeeded(y - mMotionY)) {
if (startScrollIfNeeded(y)) {
return true;
}
break;