diff options
| -rw-r--r-- | core/java/android/widget/AbsListView.java | 194 |
1 files changed, 129 insertions, 65 deletions
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index e740cc2b1f7a..1afa54d9717f 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -715,7 +715,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te */ @NonNull @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123769408) - private EdgeEffect mEdgeGlowTop = new EdgeEffect(mContext); + private EdgeEffect mEdgeGlowTop; /** * Tracks the state of the bottom edge glow. @@ -725,7 +725,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te */ @NonNull @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123768444) - private EdgeEffect mEdgeGlowBottom = new EdgeEffect(mContext); + private EdgeEffect mEdgeGlowBottom; /** * An estimate of how many pixels are between the top of the list and @@ -847,6 +847,8 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te public AbsListView(Context context) { super(context); + mEdgeGlowBottom = new EdgeEffect(context); + mEdgeGlowTop = new EdgeEffect(context); initAbsListView(); mOwnerThread = Thread.currentThread(); @@ -867,6 +869,8 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te public AbsListView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); + mEdgeGlowBottom = new EdgeEffect(context, attrs); + mEdgeGlowTop = new EdgeEffect(context, attrs); initAbsListView(); mOwnerThread = Thread.currentThread(); @@ -3584,6 +3588,9 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te mLastY != Integer.MIN_VALUE ? y - mLastY + scrollConsumedCorrection : deltaY; int lastYCorrection = 0; + // First allow releasing existing overscroll effect: + incrementalDeltaY = releaseGlow(incrementalDeltaY, x); + if (mTouchMode == TOUCH_MODE_SCROLL) { if (PROFILE_SCROLLING) { if (!mScrollProfilingStarted) { @@ -3666,14 +3673,14 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te mTouchMode = TOUCH_MODE_OVERSCROLL; } if (incrementalDeltaY > 0) { - mEdgeGlowTop.onPull((float) -overscroll / getHeight(), + mEdgeGlowTop.onPullDistance((float) -overscroll / getHeight(), (float) x / getWidth()); if (!mEdgeGlowBottom.isFinished()) { mEdgeGlowBottom.onRelease(); } invalidateTopGlow(); } else if (incrementalDeltaY < 0) { - mEdgeGlowBottom.onPull((float) overscroll / getHeight(), + mEdgeGlowBottom.onPullDistance((float) overscroll / getHeight(), 1.f - (float) x / getWidth()); if (!mEdgeGlowTop.isFinished()) { mEdgeGlowTop.onRelease(); @@ -3713,14 +3720,15 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te (overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && !contentFits())) { if (rawDeltaY > 0) { - mEdgeGlowTop.onPull((float) overScrollDistance / getHeight(), + mEdgeGlowTop.onPullDistance((float) overScrollDistance / getHeight(), (float) x / getWidth()); if (!mEdgeGlowBottom.isFinished()) { mEdgeGlowBottom.onRelease(); } invalidateTopGlow(); } else if (rawDeltaY < 0) { - mEdgeGlowBottom.onPull((float) overScrollDistance / getHeight(), + mEdgeGlowBottom.onPullDistance( + (float) -overScrollDistance / getHeight(), 1.f - (float) x / getWidth()); if (!mEdgeGlowTop.isFinished()) { mEdgeGlowTop.onRelease(); @@ -3757,6 +3765,44 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } } + /** + * If the edge glow is currently active, this consumes part or all of deltaY + * on the edge glow. + * + * @param deltaY The pointer motion, in pixels, in the vertical direction, positive + * for moving down and negative for moving up. + * @param x The horizontal position of the pointer. + * @return The remainder of <code>deltaY</code> that has not been consumed by the + * edge glow. + */ + private int releaseGlow(int deltaY, int x) { + // First allow releasing existing overscroll effect: + float consumed = 0; + if (mEdgeGlowTop.getDistance() != 0) { + consumed = mEdgeGlowTop.onPullDistance((float) deltaY / getHeight(), + (float) x / getWidth()); + if (consumed != 0f) { + invalidateTopGlow(); + } + } else if (mEdgeGlowBottom.getDistance() != 0) { + consumed = -mEdgeGlowBottom.onPullDistance((float) -deltaY / getHeight(), + 1f - (float) x / getWidth()); + if (consumed != 0f) { + invalidateBottomGlow(); + } + } + int pixelsConsumed = Math.round(consumed * getHeight()); + return deltaY - pixelsConsumed; + } + + /** + * @return <code>true</code> if either the top or bottom edge glow is currently active or + * <code>false</code> if it has no value to release. + */ + private boolean isGlowActive() { + return mEdgeGlowBottom.getDistance() != 0 || mEdgeGlowTop.getDistance() != 0; + } + private void invalidateTopGlow() { if (!shouldDisplayEdgeEffects()) { return; @@ -3926,7 +3972,9 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te if (mTouchMode == TOUCH_MODE_OVERFLING) { // Stopped the fling. It is a scroll. - mFlingRunnable.endFling(); + if (mFlingRunnable != null) { + mFlingRunnable.endFling(); + } if (mPositionScroller != null) { mPositionScroller.stop(); } @@ -3936,6 +3984,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te mLastY = mMotionY; mMotionCorrection = 0; mDirection = 0; + stopEdgeGlowRecede(ev.getX()); } else { final int x = (int) ev.getX(); final int y = (int) ev.getY(); @@ -3948,7 +3997,10 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te mTouchMode = TOUCH_MODE_SCROLL; mMotionCorrection = 0; motionPosition = findMotionRow(y); - mFlingRunnable.flywheelTouch(); + if (mFlingRunnable != null) { + mFlingRunnable.flywheelTouch(); + } + stopEdgeGlowRecede(x); } else if ((motionPosition >= 0) && getAdapter().isEnabled(motionPosition)) { // User clicked on an actual view (and was not stopping a // fling). It might be a click or a scroll. Assume it is a @@ -3984,6 +4036,15 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } } + private void stopEdgeGlowRecede(float x) { + if (mEdgeGlowTop.getDistance() != 0) { + mEdgeGlowTop.onPullDistance(0, x / getWidth()); + } + if (mEdgeGlowBottom.getDistance() != 0) { + mEdgeGlowBottom.onPullDistance(0, x / getWidth()); + } + } + private void onTouchMove(MotionEvent ev, MotionEvent vtev) { if (mHasPerformedLongPress) { // Consume all move events following a successful long press. @@ -4489,73 +4550,76 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } switch (actionMasked) { - case MotionEvent.ACTION_DOWN: { - int touchMode = mTouchMode; - if (touchMode == TOUCH_MODE_OVERFLING || touchMode == TOUCH_MODE_OVERSCROLL) { - mMotionCorrection = 0; - return true; - } - - final int x = (int) ev.getX(); - final int y = (int) ev.getY(); - mActivePointerId = ev.getPointerId(0); + case MotionEvent.ACTION_DOWN: { + int touchMode = mTouchMode; + if (touchMode == TOUCH_MODE_OVERFLING || touchMode == TOUCH_MODE_OVERSCROLL) { + mMotionCorrection = 0; + return true; + } - int motionPosition = findMotionRow(y); - if (touchMode != TOUCH_MODE_FLING && motionPosition >= 0) { - // User clicked on an actual view (and was not stopping a fling). - // Remember where the motion event started - v = getChildAt(motionPosition - mFirstPosition); - mMotionViewOriginalTop = v.getTop(); - mMotionX = x; - mMotionY = y; - mMotionPosition = motionPosition; - mTouchMode = TOUCH_MODE_DOWN; - clearScrollingCache(); - } - mLastY = Integer.MIN_VALUE; - initOrResetVelocityTracker(); - mVelocityTracker.addMovement(ev); - mNestedYOffset = 0; - startNestedScroll(SCROLL_AXIS_VERTICAL); - if (touchMode == TOUCH_MODE_FLING) { - return true; - } - break; - } + final int x = (int) ev.getX(); + final int y = (int) ev.getY(); + mActivePointerId = ev.getPointerId(0); - case MotionEvent.ACTION_MOVE: { - switch (mTouchMode) { - case TOUCH_MODE_DOWN: - int pointerIndex = ev.findPointerIndex(mActivePointerId); - if (pointerIndex == -1) { - pointerIndex = 0; - mActivePointerId = ev.getPointerId(pointerIndex); + int motionPosition = findMotionRow(y); + if (isGlowActive()) { + // Pressed during edge effect, so this is considered the same as a fling catch. + mTouchMode = TOUCH_MODE_FLING; + } else if (touchMode != TOUCH_MODE_FLING && motionPosition >= 0) { + // User clicked on an actual view (and was not stopping a fling). + // Remember where the motion event started + v = getChildAt(motionPosition - mFirstPosition); + mMotionViewOriginalTop = v.getTop(); + mMotionX = x; + mMotionY = y; + mMotionPosition = motionPosition; + mTouchMode = TOUCH_MODE_DOWN; + clearScrollingCache(); } - final int y = (int) ev.getY(pointerIndex); - initVelocityTrackerIfNotExists(); + mLastY = Integer.MIN_VALUE; + initOrResetVelocityTracker(); mVelocityTracker.addMovement(ev); - if (startScrollIfNeeded((int) ev.getX(pointerIndex), y, null)) { + mNestedYOffset = 0; + startNestedScroll(SCROLL_AXIS_VERTICAL); + if (touchMode == TOUCH_MODE_FLING) { return true; } break; } - break; - } - case MotionEvent.ACTION_CANCEL: - case MotionEvent.ACTION_UP: { - mTouchMode = TOUCH_MODE_REST; - mActivePointerId = INVALID_POINTER; - recycleVelocityTracker(); - reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE); - stopNestedScroll(); - break; - } + case MotionEvent.ACTION_MOVE: { + switch (mTouchMode) { + case TOUCH_MODE_DOWN: + int pointerIndex = ev.findPointerIndex(mActivePointerId); + if (pointerIndex == -1) { + pointerIndex = 0; + mActivePointerId = ev.getPointerId(pointerIndex); + } + final int y = (int) ev.getY(pointerIndex); + initVelocityTrackerIfNotExists(); + mVelocityTracker.addMovement(ev); + if (startScrollIfNeeded((int) ev.getX(pointerIndex), y, null)) { + return true; + } + break; + } + break; + } - case MotionEvent.ACTION_POINTER_UP: { - onSecondaryPointerUp(ev); - break; - } + case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_UP: { + mTouchMode = TOUCH_MODE_REST; + mActivePointerId = INVALID_POINTER; + recycleVelocityTracker(); + reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE); + stopNestedScroll(); + break; + } + + case MotionEvent.ACTION_POINTER_UP: { + onSecondaryPointerUp(ev); + break; + } } return false; |