ScrollView and HorizontalScrollView stretch overscroll

Bug: 178807038

Modify ScrollView and HorizontalScrollView to support
stretch overscroll.

Test: I18820794f2511ff4b7df1c5c6a9d7a2d5fafeaa0
Change-Id: I1370b702ce7331269744033c6acb61b84519c853
This commit is contained in:
George Mount
2021-02-11 01:10:34 +00:00
parent f1de89b16c
commit 97f04fe7d3
3 changed files with 74 additions and 19 deletions

View File

@@ -22164,6 +22164,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* and hardware acceleration.
*/
boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) {
// Clear the overscroll effect:
// TODO: Use internal API instead of overriding the existing RenderEffect
setRenderEffect(null);
final boolean hardwareAcceleratedCanvas = canvas.isHardwareAccelerated();
/* If an attached view draws to a HW canvas, it may use its RenderNode + DisplayList.
*

View File

@@ -89,7 +89,7 @@ public class HorizontalScrollView extends FrameLayout {
*/
@NonNull
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 124053130)
private EdgeEffect mEdgeGlowLeft = new EdgeEffect(getContext());
private EdgeEffect mEdgeGlowLeft;
/**
* Tracks the state of the bottom edge glow.
@@ -98,7 +98,7 @@ public class HorizontalScrollView extends FrameLayout {
* setting it via reflection and they need to keep working until they target Q.
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 124052619)
private EdgeEffect mEdgeGlowRight = new EdgeEffect(getContext());
private EdgeEffect mEdgeGlowRight;
/**
* Position of the last motion event.
@@ -186,6 +186,8 @@ public class HorizontalScrollView extends FrameLayout {
public HorizontalScrollView(
Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
mEdgeGlowLeft = new EdgeEffect(context, attrs);
mEdgeGlowRight = new EdgeEffect(context, attrs);
initScrollView();
final TypedArray a = context.obtainStyledAttributes(
@@ -631,7 +633,15 @@ public class HorizontalScrollView extends FrameLayout {
* otherwise don't. mScroller.isFinished should be false when
* being flinged.
*/
mIsBeingDragged = !mScroller.isFinished();
mIsBeingDragged = !mScroller.isFinished() || !mEdgeGlowLeft.isFinished()
|| !mEdgeGlowRight.isFinished();
// Catch the edge effect if it is active.
if (!mEdgeGlowLeft.isFinished()) {
mEdgeGlowLeft.onPullDistance(0f, 1f - ev.getY() / getHeight());
}
if (!mEdgeGlowRight.isFinished()) {
mEdgeGlowRight.onPullDistance(0f, ev.getY() / getHeight());
}
break;
}
@@ -675,7 +685,8 @@ public class HorizontalScrollView extends FrameLayout {
if (getChildCount() == 0) {
return false;
}
if ((mIsBeingDragged = !mScroller.isFinished())) {
if ((mIsBeingDragged = !mScroller.isFinished() || !mEdgeGlowRight.isFinished()
|| !mEdgeGlowLeft.isFinished())) {
final ViewParent parent = getParent();
if (parent != null) {
parent.requestDisallowInterceptTouchEvent(true);
@@ -721,12 +732,26 @@ public class HorizontalScrollView extends FrameLayout {
mLastMotionX = x;
final int oldX = mScrollX;
final int oldY = mScrollY;
final int range = getScrollRange();
final int overscrollMode = getOverScrollMode();
final boolean canOverscroll = overscrollMode == OVER_SCROLL_ALWAYS ||
(overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && range > 0);
final float displacement = ev.getY(activePointerIndex) / getHeight();
if (canOverscroll) {
int consumed = 0;
if (deltaX < 0 && mEdgeGlowRight.getDistance() != 0f) {
consumed = Math.round(getHeight()
* mEdgeGlowRight.onPullDistance((float) deltaX / getWidth(),
displacement));
} else if (deltaX > 0 && mEdgeGlowLeft.getDistance() != 0f) {
consumed = Math.round(-getHeight()
* mEdgeGlowLeft.onPullDistance((float) -deltaX / getWidth(),
1 - displacement));
}
deltaX -= consumed;
}
// Calling overScrollBy will call onOverScrolled, which
// calls onScrollChanged if applicable.
if (overScrollBy(deltaX, 0, mScrollX, 0, range, 0,
@@ -735,17 +760,17 @@ public class HorizontalScrollView extends FrameLayout {
mVelocityTracker.clear();
}
if (canOverscroll) {
if (canOverscroll && deltaX != 0f) {
final int pulledToX = oldX + deltaX;
if (pulledToX < 0) {
mEdgeGlowLeft.onPull((float) deltaX / getWidth(),
1.f - ev.getY(activePointerIndex) / getHeight());
mEdgeGlowLeft.onPullDistance((float) -deltaX / getWidth(),
1.f - displacement);
if (!mEdgeGlowRight.isFinished()) {
mEdgeGlowRight.onRelease();
}
} else if (pulledToX > range) {
mEdgeGlowRight.onPull((float) deltaX / getWidth(),
ev.getY(activePointerIndex) / getHeight());
mEdgeGlowRight.onPullDistance((float) deltaX / getWidth(),
displacement);
if (!mEdgeGlowLeft.isFinished()) {
mEdgeGlowLeft.onRelease();
}

View File

@@ -98,7 +98,7 @@ public class ScrollView extends FrameLayout {
*/
@NonNull
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123768600)
private EdgeEffect mEdgeGlowTop = new EdgeEffect(getContext());
private EdgeEffect mEdgeGlowTop;
/**
* Tracks the state of the bottom edge glow.
@@ -108,7 +108,7 @@ public class ScrollView extends FrameLayout {
*/
@NonNull
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123769386)
private EdgeEffect mEdgeGlowBottom = new EdgeEffect(getContext());
private EdgeEffect mEdgeGlowBottom;
/**
* Position of the last motion event.
@@ -213,6 +213,8 @@ public class ScrollView extends FrameLayout {
public ScrollView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
mEdgeGlowTop = new EdgeEffect(context, attrs);
mEdgeGlowBottom = new EdgeEffect(context, attrs);
initScrollView();
final TypedArray a = context.obtainStyledAttributes(
@@ -679,7 +681,15 @@ public class ScrollView extends FrameLayout {
* isFinished() is correct.
*/
mScroller.computeScrollOffset();
mIsBeingDragged = !mScroller.isFinished();
mIsBeingDragged = !mScroller.isFinished() || !mEdgeGlowBottom.isFinished()
|| !mEdgeGlowTop.isFinished();
// Catch the edge effect if it is active.
if (!mEdgeGlowTop.isFinished()) {
mEdgeGlowTop.onPullDistance(0f, ev.getX() / getWidth());
}
if (!mEdgeGlowBottom.isFinished()) {
mEdgeGlowBottom.onPullDistance(0f, 1f - ev.getX() / getWidth());
}
if (mIsBeingDragged && mScrollStrictSpan == null) {
mScrollStrictSpan = StrictMode.enterCriticalSpan("ScrollView-scroll");
}
@@ -732,7 +742,8 @@ public class ScrollView extends FrameLayout {
if (getChildCount() == 0) {
return false;
}
if ((mIsBeingDragged = !mScroller.isFinished())) {
if ((mIsBeingDragged = !mScroller.isFinished() || !mEdgeGlowTop.isFinished()
|| !mEdgeGlowBottom.isFinished())) {
final ViewParent parent = getParent();
if (parent != null) {
parent.requestDisallowInterceptTouchEvent(true);
@@ -793,6 +804,21 @@ public class ScrollView extends FrameLayout {
boolean canOverscroll = overscrollMode == OVER_SCROLL_ALWAYS ||
(overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && range > 0);
final float displacement = ev.getX(activePointerIndex) / getWidth();
if (canOverscroll) {
int consumed = 0;
if (deltaY < 0 && mEdgeGlowBottom.getDistance() != 0f) {
consumed = Math.round(getHeight()
* mEdgeGlowBottom.onPullDistance((float) deltaY / getHeight(),
1 - displacement));
} else if (deltaY > 0 && mEdgeGlowTop.getDistance() != 0f) {
consumed = Math.round(-getHeight()
* mEdgeGlowTop.onPullDistance((float) -deltaY / getHeight(),
displacement));
}
deltaY -= consumed;
}
// Calling overScrollBy will call onOverScrolled, which
// calls onScrollChanged if applicable.
if (overScrollBy(0, deltaY, 0, mScrollY, 0, range, 0, mOverscrollDistance, true)
@@ -807,17 +833,17 @@ public class ScrollView extends FrameLayout {
mLastMotionY -= mScrollOffset[1];
vtev.offsetLocation(0, mScrollOffset[1]);
mNestedYOffset += mScrollOffset[1];
} else if (canOverscroll) {
} else if (canOverscroll && deltaY != 0f) {
final int pulledToY = oldY + deltaY;
if (pulledToY < 0) {
mEdgeGlowTop.onPull((float) deltaY / getHeight(),
ev.getX(activePointerIndex) / getWidth());
mEdgeGlowTop.onPullDistance((float) -deltaY / getHeight(),
displacement);
if (!mEdgeGlowBottom.isFinished()) {
mEdgeGlowBottom.onRelease();
}
} else if (pulledToY > range) {
mEdgeGlowBottom.onPull((float) deltaY / getHeight(),
1.f - ev.getX(activePointerIndex) / getWidth());
mEdgeGlowBottom.onPullDistance((float) deltaY / getHeight(),
1.f - displacement);
if (!mEdgeGlowTop.isFinished()) {
mEdgeGlowTop.onRelease();
}