Skip to content

Commit fe6277a

Browse files
ryancatfacebook-github-bot
authored andcommitted
Support override predict final scroll position with custom fling animator
Summary: This diff add custom prediction for fling distance support. This is needed for customize fling animator to calculate predicted fling distance, instead of using the overscroller that may not be used by the animator. More context on this -- when fling happens, our code will first predict the final fling position `p`, apply the snapping logic to decide the expected snapping position `pSnapping` given `p`, scroll velocity and children layout, then trigger the overscroller (existing) or custom fling animator to finish the fling. Currently, the prediction logic is done with overscroller, and custom fling animator has no control over how the predicted fling distance should be. Changes in this diff allow the animator to override `getExtrapolatedDistance` method and provide that information. Changelog: [Android][Added] - Add new API for custom fling animator to provide predicted travel distance for its fling animation. Reviewed By: mdvacca Differential Revision: D32571734 fbshipit-source-id: d34b969206f8b6cb5c68d2f50a18749bfebbc97e
1 parent 39a35fe commit fe6277a

File tree

3 files changed

+44
-7
lines changed

3 files changed

+44
-7
lines changed

ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.java

+19-1
Original file line numberDiff line numberDiff line change
@@ -803,7 +803,16 @@ public void run() {
803803
private int predictFinalScrollPosition(int velocityX) {
804804
// predict where a fling would end up so we can scroll to the nearest snap offset
805805
final int maximumOffset = Math.max(0, computeHorizontalScrollRange() - getWidth());
806-
return ReactScrollViewHelper.predictFinalScrollPosition(this, velocityX, 0, maximumOffset, 0).x;
806+
// TODO(T106335409): Existing prediction still uses overscroller. Consider change this to use
807+
// fling animator instead.
808+
return getFlingAnimator() == DEFAULT_FLING_ANIMATOR
809+
? ReactScrollViewHelper.predictFinalScrollPosition(this, velocityX, 0, maximumOffset, 0).x
810+
: ReactScrollViewHelper.getNextFlingStartValue(
811+
this,
812+
getScrollX(),
813+
getReactScrollViewScrollState().getFinalAnimatedPositionScroll().x,
814+
velocityX)
815+
+ getFlingExtrapolatedDistance(velocityX);
807816
}
808817

809818
/**
@@ -1224,4 +1233,13 @@ public void startFlingAnimator(int start, int end) {
12241233
public ValueAnimator getFlingAnimator() {
12251234
return DEFAULT_FLING_ANIMATOR;
12261235
}
1236+
1237+
@Override
1238+
public int getFlingExtrapolatedDistance(int velocityX) {
1239+
// The DEFAULT_FLING_ANIMATOR uses AccelerateDecelerateInterpolator, which is not depending on
1240+
// the init velocity. We use the overscroller to decide the fling distance.
1241+
return ReactScrollViewHelper.predictFinalScrollPosition(
1242+
this, velocityX, 0, Math.max(0, computeHorizontalScrollRange() - getWidth()), 0)
1243+
.x;
1244+
}
12271245
}

ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollView.java

+18-2
Original file line numberDiff line numberDiff line change
@@ -591,8 +591,16 @@ public void run() {
591591

592592
private int predictFinalScrollPosition(int velocityY) {
593593
// predict where a fling would end up so we can scroll to the nearest snap offset
594-
return ReactScrollViewHelper.predictFinalScrollPosition(this, 0, velocityY, 0, getMaxScrollY())
595-
.y;
594+
// TODO(T106335409): Existing prediction still uses overscroller. Consider change this to use
595+
// fling animator instead.
596+
return getFlingAnimator() == DEFAULT_FLING_ANIMATOR
597+
? ReactScrollViewHelper.predictFinalScrollPosition(this, 0, velocityY, 0, getMaxScrollY()).y
598+
: ReactScrollViewHelper.getNextFlingStartValue(
599+
this,
600+
getScrollY(),
601+
getReactScrollViewScrollState().getFinalAnimatedPositionScroll().y,
602+
velocityY)
603+
+ getFlingExtrapolatedDistance(velocityY);
596604
}
597605

598606
private View getContentView() {
@@ -1088,4 +1096,12 @@ public void startFlingAnimator(int start, int end) {
10881096
public ValueAnimator getFlingAnimator() {
10891097
return DEFAULT_FLING_ANIMATOR;
10901098
}
1099+
1100+
@Override
1101+
public int getFlingExtrapolatedDistance(int velocityY) {
1102+
// The DEFAULT_FLING_ANIMATOR uses AccelerateDecelerateInterpolator, which is not depending on
1103+
// the init velocity. We use the overscroller to decide the fling distance.
1104+
return ReactScrollViewHelper.predictFinalScrollPosition(this, 0, velocityY, 0, getMaxScrollY())
1105+
.y;
1106+
}
10911107
}

ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewHelper.java

+7-4
Original file line numberDiff line numberDiff line change
@@ -510,10 +510,10 @@ public void onAnimationRepeat(Animator animator) {}
510510
& HasFlingAnimator>
511511
Point predictFinalScrollPosition(
512512
final T scrollView,
513-
int velocityX,
514-
int velocityY,
515-
int maximumOffsetX,
516-
int maximumOffsetY) {
513+
final int velocityX,
514+
final int velocityY,
515+
final int maximumOffsetX,
516+
final int maximumOffsetY) {
517517
final ReactScrollViewScrollState scrollState = scrollView.getReactScrollViewScrollState();
518518
// ScrollView can *only* scroll for 250ms when using smoothScrollTo and there's
519519
// no way to customize the scroll duration. So, we create a temporary OverScroller
@@ -566,5 +566,8 @@ public interface HasFlingAnimator {
566566

567567
/** Get the fling animator that is reused for the ScrollView to handle fling animation. */
568568
ValueAnimator getFlingAnimator();
569+
570+
/** Get the fling distance with current velocity for prediction */
571+
int getFlingExtrapolatedDistance(int velocity);
569572
}
570573
}

0 commit comments

Comments
 (0)