Skip to content

Commit

Permalink
Support override predict final scroll position with custom fling anim…
Browse files Browse the repository at this point in the history
…ator

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
  • Loading branch information
ryancat authored and facebook-github-bot committed Dec 7, 2021
1 parent 39a35fe commit fe6277a
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

/**
Expand Down Expand Up @@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down Expand Up @@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);
}
}

0 comments on commit fe6277a

Please sign in to comment.