diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.java b/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.java index 100e70853ad05a..d033e0687122cc 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.java @@ -803,7 +803,16 @@ public void run() { private int predictFinalScrollPosition(int velocityX) { // predict where a fling would end up so we can scroll to the nearest snap offset final int maximumOffset = Math.max(0, computeHorizontalScrollRange() - getWidth()); - return ReactScrollViewHelper.predictFinalScrollPosition(this, velocityX, 0, maximumOffset, 0).x; + // TODO(T106335409): Existing prediction still uses overscroller. Consider change this to use + // fling animator instead. + return getFlingAnimator() == DEFAULT_FLING_ANIMATOR + ? ReactScrollViewHelper.predictFinalScrollPosition(this, velocityX, 0, maximumOffset, 0).x + : ReactScrollViewHelper.getNextFlingStartValue( + this, + getScrollX(), + getReactScrollViewScrollState().getFinalAnimatedPositionScroll().x, + velocityX) + + getFlingExtrapolatedDistance(velocityX); } /** @@ -1224,4 +1233,13 @@ public void startFlingAnimator(int start, int end) { public ValueAnimator getFlingAnimator() { return DEFAULT_FLING_ANIMATOR; } + + @Override + public int getFlingExtrapolatedDistance(int velocityX) { + // The DEFAULT_FLING_ANIMATOR uses AccelerateDecelerateInterpolator, which is not depending on + // the init velocity. We use the overscroller to decide the fling distance. + return ReactScrollViewHelper.predictFinalScrollPosition( + this, velocityX, 0, Math.max(0, computeHorizontalScrollRange() - getWidth()), 0) + .x; + } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollView.java b/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollView.java index 89bdbf47e1c555..652ec951301a1a 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollView.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollView.java @@ -591,8 +591,16 @@ public void run() { private int predictFinalScrollPosition(int velocityY) { // predict where a fling would end up so we can scroll to the nearest snap offset - return ReactScrollViewHelper.predictFinalScrollPosition(this, 0, velocityY, 0, getMaxScrollY()) - .y; + // TODO(T106335409): Existing prediction still uses overscroller. Consider change this to use + // fling animator instead. + return getFlingAnimator() == DEFAULT_FLING_ANIMATOR + ? ReactScrollViewHelper.predictFinalScrollPosition(this, 0, velocityY, 0, getMaxScrollY()).y + : ReactScrollViewHelper.getNextFlingStartValue( + this, + getScrollY(), + getReactScrollViewScrollState().getFinalAnimatedPositionScroll().y, + velocityY) + + getFlingExtrapolatedDistance(velocityY); } private View getContentView() { @@ -1088,4 +1096,12 @@ public void startFlingAnimator(int start, int end) { public ValueAnimator getFlingAnimator() { return DEFAULT_FLING_ANIMATOR; } + + @Override + public int getFlingExtrapolatedDistance(int velocityY) { + // The DEFAULT_FLING_ANIMATOR uses AccelerateDecelerateInterpolator, which is not depending on + // the init velocity. We use the overscroller to decide the fling distance. + return ReactScrollViewHelper.predictFinalScrollPosition(this, 0, velocityY, 0, getMaxScrollY()) + .y; + } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewHelper.java b/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewHelper.java index 067d844f92efe8..e9d1e59fa2297f 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewHelper.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewHelper.java @@ -510,10 +510,10 @@ public void onAnimationRepeat(Animator animator) {} & HasFlingAnimator> Point predictFinalScrollPosition( final T scrollView, - int velocityX, - int velocityY, - int maximumOffsetX, - int maximumOffsetY) { + final int velocityX, + final int velocityY, + final int maximumOffsetX, + final int maximumOffsetY) { final ReactScrollViewScrollState scrollState = scrollView.getReactScrollViewScrollState(); // ScrollView can *only* scroll for 250ms when using smoothScrollTo and there's // no way to customize the scroll duration. So, we create a temporary OverScroller @@ -566,5 +566,8 @@ public interface HasFlingAnimator { /** Get the fling animator that is reused for the ScrollView to handle fling animation. */ ValueAnimator getFlingAnimator(); + + /** Get the fling distance with current velocity for prediction */ + int getFlingExtrapolatedDistance(int velocity); } }