From 13cdf67f8a70242d7a422c76fbbb96a78ca26dc3 Mon Sep 17 00:00:00 2001 From: George Mount Date: Wed, 10 Mar 2021 19:02:02 +0000 Subject: [PATCH] Add support for stretch overscroll to internal ViewPager Bug: 171228096 The internal ViewPager now supports stretch overscroll. Implementation taken from androidx-platform-dev ViewPager. Test: manual testing with share sheet Change-Id: If3db565e64d38d8e03319311ee5d4c07bdd4eff6 --- .../android/internal/widget/ViewPager.java | 84 +++++++++++++++++-- 1 file changed, 75 insertions(+), 9 deletions(-) diff --git a/core/java/com/android/internal/widget/ViewPager.java b/core/java/com/android/internal/widget/ViewPager.java index 6f377b9b228a3..93cde3ddb72d8 100644 --- a/core/java/com/android/internal/widget/ViewPager.java +++ b/core/java/com/android/internal/widget/ViewPager.java @@ -353,8 +353,8 @@ public class ViewPager extends ViewGroup { mTouchSlop = configuration.getScaledPagingTouchSlop(); mMinimumVelocity = (int) (MIN_FLING_VELOCITY * density); mMaximumVelocity = configuration.getScaledMaximumFlingVelocity(); - mLeftEdge = new EdgeEffect(context); - mRightEdge = new EdgeEffect(context); + mLeftEdge = new EdgeEffect(context, attrs); + mRightEdge = new EdgeEffect(context, attrs); mFlingDistance = (int) (MIN_DISTANCE_FOR_FLING * density); mCloseEnough = (int) (CLOSE_ENOUGH * density); @@ -386,6 +386,28 @@ public class ViewPager extends ViewGroup { } } + /** + * Returns the {@link EdgeEffect#getType()} for the edge effects. + * @return the {@link EdgeEffect#getType()} for the edge effects. + * @attr ref android.R.styleable#EdgeEffect_edgeEffectType + */ + @EdgeEffect.EdgeEffectType + public int getEdgeEffectType() { + // Both left and right edge have the same edge effect type + return mLeftEdge.getType(); + } + + /** + * Sets the {@link EdgeEffect#setType(int)} for the edge effects. + * @param type The edge effect type to use for the edge effects. + * @attr ref android.R.styleable#EdgeEffect_edgeEffectType + */ + public void setEdgeEffectType(@EdgeEffect.EdgeEffectType int type) { + mLeftEdge.setType(type); + mRightEdge.setType(type); + invalidate(); + } + /** * Set a PagerAdapter that will supply views for this pager as needed. * @@ -1891,7 +1913,7 @@ public class ViewPager extends ViewGroup { } if (mIsBeingDragged) { // Scroll to follow the motion event - if (performDrag(x)) { + if (performDrag(x, y)) { postInvalidateOnAnimation(); } } @@ -1918,6 +1940,17 @@ public class ViewPager extends ViewGroup { mIsBeingDragged = true; requestParentDisallowInterceptTouchEvent(true); setScrollState(SCROLL_STATE_DRAGGING); + } else if (mLeftEdge.getDistance() != 0 + || mRightEdge.getDistance() != 0) { + // Caught the edge glow animation + mIsBeingDragged = true; + setScrollState(SCROLL_STATE_DRAGGING); + if (mLeftEdge.getDistance() != 0) { + mLeftEdge.onPullDistance(0f, 1 - mLastMotionY / getHeight()); + } + if (mRightEdge.getDistance() != 0) { + mRightEdge.onPullDistance(0f, mLastMotionY / getHeight()); + } } else { completeScroll(false); mIsBeingDragged = false; @@ -2009,7 +2042,7 @@ public class ViewPager extends ViewGroup { // Scroll to follow the motion event final int activePointerIndex = ev.findPointerIndex(mActivePointerId); final float x = ev.getX(activePointerIndex); - needsInvalidate |= performDrag(x); + needsInvalidate |= performDrag(x, ev.getY(activePointerIndex)); } break; case MotionEvent.ACTION_UP: @@ -2080,12 +2113,43 @@ public class ViewPager extends ViewGroup { } } - private boolean performDrag(float x) { + /** + * If either of the horizontal edge glows are currently active, this consumes part or all of + * deltaX on the edge glow. + * + * @param deltaX The pointer motion, in pixels, in the horizontal direction, positive + * for moving down and negative for moving up. + * @param y The vertical position of the pointer. + * @return The amount of deltaX that has been consumed by the + * edge glow. + */ + private float releaseHorizontalGlow(float deltaX, float y) { + // First allow releasing existing overscroll effect: + float consumed = 0; + float displacement = y / getHeight(); + float pullDistance = (float) deltaX / getWidth(); + if (mLeftEdge.getDistance() != 0) { + consumed = -mLeftEdge.onPullDistance(-pullDistance, 1 - displacement); + } else if (mRightEdge.getDistance() != 0) { + consumed = mRightEdge.onPullDistance(pullDistance, displacement); + } + return consumed * getWidth(); + } + + private boolean performDrag(float x, float y) { boolean needsInvalidate = false; + final float dX = mLastMotionX - x; final int width = getPaddedWidth(); - final float deltaX = mLastMotionX - x; mLastMotionX = x; + final float releaseConsumed = releaseHorizontalGlow(dX, y); + final float deltaX = dX - releaseConsumed; + if (releaseConsumed != 0) { + needsInvalidate = true; + } + if (Math.abs(deltaX) < 0.0001f) { // ignore rounding errors from releaseHorizontalGlow() + return needsInvalidate; + } final EdgeEffect startEdge; final EdgeEffect endEdge; @@ -2128,14 +2192,14 @@ public class ViewPager extends ViewGroup { if (scrollStart < startBound) { if (startAbsolute) { final float over = startBound - scrollStart; - startEdge.onPull(Math.abs(over) / width); + startEdge.onPullDistance(over / width, 1 - y / getHeight()); needsInvalidate = true; } clampedScrollStart = startBound; } else if (scrollStart > endBound) { if (endAbsolute) { final float over = scrollStart - endBound; - endEdge.onPull(Math.abs(over) / width); + endEdge.onPullDistance(over / width, y / getHeight()); needsInvalidate = true; } clampedScrollStart = endBound; @@ -2228,7 +2292,9 @@ public class ViewPager extends ViewGroup { */ private int determineTargetPage(int currentPage, float pageOffset, int velocity, int deltaX) { int targetPage; - if (Math.abs(deltaX) > mFlingDistance && Math.abs(velocity) > mMinimumVelocity) { + if (Math.abs(deltaX) > mFlingDistance && Math.abs(velocity) > mMinimumVelocity + && mLeftEdge.getDistance() == 0 // don't fling while stretched + && mRightEdge.getDistance() == 0) { targetPage = currentPage - (velocity < 0 ? mLeftIncr : 0); } else { final float truncator = currentPage >= mCurItem ? 0.4f : 0.6f;