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
This commit is contained in:
George Mount
2021-03-10 19:02:02 +00:00
parent af1e8b4ea9
commit 13cdf67f8a

View File

@@ -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 <code>deltaX</code> 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;