Add nested pre-fling for nested scrolling
Nested pre-fling allows a nested scrolling parent to consume a fling
operation before the child view does. If a parent has been consuming
nested pre-scroll events in a particular direction, this is generally
a good indication that it should also consume the resulting fling at
the end.
Bug 15538504
Change-Id: I88e8753a96c9b41815c3be530cafab8345164e64
diff --git a/api/current.txt b/api/current.txt
index f799671..8a99dac 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -33524,6 +33524,7 @@
method public boolean dispatchKeyEventPreIme(android.view.KeyEvent);
method public boolean dispatchKeyShortcutEvent(android.view.KeyEvent);
method public boolean dispatchNestedFling(float, float, boolean);
+ method public boolean dispatchNestedPreFling(float, float);
method public boolean dispatchNestedPreScroll(int, int, int[], int[]);
method public boolean dispatchNestedScroll(int, int, int, int, int[]);
method public boolean dispatchPopulateAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
@@ -34334,6 +34335,7 @@
method public boolean onInterceptTouchEvent(android.view.MotionEvent);
method protected abstract void onLayout(boolean, int, int, int, int);
method public boolean onNestedFling(android.view.View, float, float, boolean);
+ method public boolean onNestedPreFling(android.view.View, float, float);
method public void onNestedPreScroll(android.view.View, int, int, int[]);
method public void onNestedScroll(android.view.View, int, int, int, int);
method public void onNestedScrollAccepted(android.view.View, android.view.View, int);
@@ -34479,6 +34481,7 @@
method public abstract boolean isTextDirectionResolved();
method public abstract void notifySubtreeAccessibilityStateChanged(android.view.View, android.view.View, int);
method public abstract boolean onNestedFling(android.view.View, float, float, boolean);
+ method public abstract boolean onNestedPreFling(android.view.View, float, float);
method public abstract void onNestedPreScroll(android.view.View, int, int, int[]);
method public abstract void onNestedScroll(android.view.View, int, int, int, int);
method public abstract void onNestedScrollAccepted(android.view.View, android.view.View, int);
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index a3e2c96..7bc8bc5 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -18445,6 +18445,43 @@
}
/**
+ * Dispatch a fling to a nested scrolling parent before it is processed by this view.
+ *
+ * <p>Nested pre-fling events are to nested fling events what touch intercept is to touch
+ * and what nested pre-scroll is to nested scroll. <code>dispatchNestedPreFling</code>
+ * offsets an opportunity for the parent view in a nested fling to fully consume the fling
+ * before the child view consumes it. If this method returns <code>true</code>, a nested
+ * parent view consumed the fling and this view should not scroll as a result.</p>
+ *
+ * <p>For a better user experience, only one view in a nested scrolling chain should consume
+ * the fling at a time. If a parent view consumed the fling this method will return false.
+ * Custom view implementations should account for this in two ways:</p>
+ *
+ * <ul>
+ * <li>If a custom view is paged and needs to settle to a fixed page-point, do not
+ * call <code>dispatchNestedPreFling</code>; consume the fling and settle to a valid
+ * position regardless.</li>
+ * <li>If a nested parent does consume the fling, this view should not scroll at all,
+ * even to settle back to a valid idle position.</li>
+ * </ul>
+ *
+ * <p>Views should also not offer fling velocities to nested parent views along an axis
+ * where scrolling is not currently supported; a {@link android.widget.ScrollView ScrollView}
+ * should not offer a horizontal fling velocity to its parents since scrolling along that
+ * axis is not permitted and carrying velocity along that motion does not make sense.</p>
+ *
+ * @param velocityX Horizontal fling velocity in pixels per second
+ * @param velocityY Vertical fling velocity in pixels per second
+ * @return true if a nested scrolling parent consumed the fling
+ */
+ public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
+ if (isNestedScrollingEnabled() && mNestedScrollingParent != null) {
+ return mNestedScrollingParent.onNestedPreFling(this, velocityX, velocityY);
+ }
+ return false;
+ }
+
+ /**
* Gets a scale factor that determines the distance the view should scroll
* vertically in response to {@link MotionEvent#ACTION_SCROLL}.
* @return The vertical scroll scale factor.
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index ac70066..04c8b0b 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -6066,6 +6066,14 @@
}
/**
+ * @inheritDoc
+ */
+ @Override
+ public boolean onNestedPreFling(View target, float velocityX, float velocityY) {
+ return false;
+ }
+
+ /**
* Return the current axes of nested scrolling for this ViewGroup.
*
* <p>A ViewGroup returning something other than {@link #SCROLL_AXIS_NONE} is currently
diff --git a/core/java/android/view/ViewParent.java b/core/java/android/view/ViewParent.java
index 588b9cd..87a37f4 100644
--- a/core/java/android/view/ViewParent.java
+++ b/core/java/android/view/ViewParent.java
@@ -523,10 +523,32 @@
* parent instead. The parent may optionally consume the fling or observe a child fling.</p>
*
* @param target View that initiated the nested scroll
- * @param velocityX Horizontal velocity in pixels per second.
+ * @param velocityX Horizontal velocity in pixels per second
* @param velocityY Vertical velocity in pixels per second
* @param consumed true if the child consumed the fling, false otherwise
* @return true if this parent consumed or otherwise reacted to the fling
*/
public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed);
+
+ /**
+ * React to a nested fling before the target view consumes it.
+ *
+ * <p>This method siginfies that a nested scrolling child has detected a fling with the given
+ * velocity along each axis. Generally this means that a touch scroll has ended with a
+ * {@link VelocityTracker velocity} in the direction of scrolling that meets or exceeds
+ * the {@link ViewConfiguration#getScaledMinimumFlingVelocity() minimum fling velocity}
+ * along a scrollable axis.</p>
+ *
+ * <p>If a nested scrolling parent is consuming motion as part of a
+ * {@link #onNestedPreScroll(View, int, int, int[]) pre-scroll}, it may be appropriate for
+ * it to also consume the pre-fling to complete that same motion. By returning
+ * <code>true</code> from this method, the parent indicates that the child should not
+ * fling its own internal content as well.</p>
+ *
+ * @param target View that initiated the nested scroll
+ * @param velocityX Horizontal velocity in pixels per second
+ * @param velocityY Vertical velocity in pixels per second
+ * @return true if this parent consumed the fling ahead of the target view
+ */
+ public boolean onNestedPreFling(View target, float velocityX, float velocityY);
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index a8d3f99..d22415e 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -6291,6 +6291,11 @@
return false;
}
+ @Override
+ public boolean onNestedPreFling(View target, float velocityX, float velocityY) {
+ return false;
+ }
+
void changeCanvasOpacity(boolean opaque) {
Log.d(TAG, "changeCanvasOpacity: opaque=" + opaque);
if (mAttachInfo.mHardwareRenderer != null) {