Skip to content

Commit

Permalink
Refactor predictFinalScrollPosition method to the helper class
Browse files Browse the repository at this point in the history
Summary:
This diff refactors method `predictFinalScrollPosition` in `ReactScrollView` and `ReactHorizontalScrollView` to the helper class. This will make future changes to the prediction logic easier.

Changelog:
[Internal]

Reviewed By: javache

Differential Revision: D32571735

fbshipit-source-id: 7e7e21ac51f929a017cd43de094ed39478fe4032
  • Loading branch information
ryancat authored and facebook-github-bot committed Dec 6, 2021
1 parent 1c19455 commit 6624327
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 56 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,6 @@ public class ReactHorizontalScrollView extends HorizontalScrollView
private int mEndFillColor = Color.TRANSPARENT;
private boolean mDisableIntervalMomentum = false;
private int mSnapInterval = 0;
private float mDecelerationRate = 0.985f;
private @Nullable List<Integer> mSnapOffsets;
private boolean mSnapToStart = true;
private boolean mSnapToEnd = true;
Expand Down Expand Up @@ -216,10 +215,10 @@ public void setPagingEnabled(boolean pagingEnabled) {
}

public void setDecelerationRate(float decelerationRate) {
mDecelerationRate = decelerationRate;
getReactScrollViewScrollState().setDecelerationRate(decelerationRate);

if (mScroller != null) {
mScroller.setFriction(1.0f - mDecelerationRate);
mScroller.setFriction(1.0f - decelerationRate);
}
}

Expand Down Expand Up @@ -802,32 +801,9 @@ public void run() {
}

private int predictFinalScrollPosition(int velocityX) {
// 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
// so we can predict where a fling would land and snap to nearby that point.
OverScroller scroller = new OverScroller(getContext());
scroller.setFriction(1.0f - mDecelerationRate);

// predict where a fling would end up so we can scroll to the nearest snap offset
int maximumOffset = Math.max(0, computeHorizontalScrollRange() - getWidth());
int width = getWidth() - ViewCompat.getPaddingStart(this) - ViewCompat.getPaddingEnd(this);
scroller.fling(
ReactScrollViewHelper.getNextFlingStartValue(
this,
getScrollX(),
getReactScrollViewScrollState().getFinalAnimatedPositionScroll().x,
velocityX), // startX
getScrollY(), // startY
velocityX, // velocityX
0, // velocityY
0, // minX
maximumOffset, // maxX
0, // minY
0, // maxY
width / 2, // overX
0 // overY
);
return scroller.getFinalX();
final int maximumOffset = Math.max(0, computeHorizontalScrollRange() - getWidth());
return ReactScrollViewHelper.predictFinalScrollPosition(this, velocityX, 0, maximumOffset, 0).x;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,6 @@ public class ReactScrollView extends ScrollView
private int mEndFillColor = Color.TRANSPARENT;
private boolean mDisableIntervalMomentum = false;
private int mSnapInterval = 0;
private float mDecelerationRate = 0.985f;
private @Nullable List<Integer> mSnapOffsets;
private boolean mSnapToStart = true;
private boolean mSnapToEnd = true;
Expand Down Expand Up @@ -191,10 +190,10 @@ public void setPagingEnabled(boolean pagingEnabled) {
}

public void setDecelerationRate(float decelerationRate) {
mDecelerationRate = decelerationRate;
getReactScrollViewScrollState().setDecelerationRate(decelerationRate);

if (mScroller != null) {
mScroller.setFriction(1.0f - mDecelerationRate);
mScroller.setFriction(1.0f - decelerationRate);
}
}

Expand Down Expand Up @@ -591,32 +590,9 @@ public void run() {
}

private int predictFinalScrollPosition(int velocityY) {
// 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
// so we can predict where a fling would land and snap to nearby that point.
OverScroller scroller = new OverScroller(getContext());
scroller.setFriction(1.0f - mDecelerationRate);

// predict where a fling would end up so we can scroll to the nearest snap offset
int maximumOffset = getMaxScrollY();
int height = getHeight() - getPaddingBottom() - getPaddingTop();
scroller.fling(
getScrollX(), // startX
ReactScrollViewHelper.getNextFlingStartValue(
this,
getScrollY(),
getReactScrollViewScrollState().getFinalAnimatedPositionScroll().y,
velocityY), // startY
0, // velocityX
velocityY, // velocityY
0, // minX
0, // maxX
0, // minY
maximumOffset, // maxY
0, // overX
height / 2 // overY
);
return scroller.getFinalY();
return ReactScrollViewHelper.predictFinalScrollPosition(this, 0, velocityY, 0, getMaxScrollY())
.y;
}

private View getContentView() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import android.view.ViewGroup;
import android.widget.OverScroller;
import androidx.annotation.Nullable;
import androidx.core.view.ViewCompat;
import com.facebook.common.logging.FLog;
import com.facebook.react.bridge.JSApplicationIllegalArgumentException;
import com.facebook.react.bridge.ReactContext;
Expand Down Expand Up @@ -222,6 +223,7 @@ public static class ReactScrollViewScrollState {
private final Point mLastStateUpdateScroll = new Point(-1, -1);
private boolean mIsCanceled = false;
private boolean mIsFinished = true;
private float mDecelerationRate = 0.985f;

public ReactScrollViewScrollState(final int layoutDirection) {
mLayoutDirection = layoutDirection;
Expand Down Expand Up @@ -291,6 +293,17 @@ public ReactScrollViewScrollState setIsFinished(boolean isFinished) {
mIsFinished = isFinished;
return this;
}

/** Get true if previous animation was finished */
public float getDecelerationRate() {
return mDecelerationRate;
}

/** Set the state of current animation is finished or not */
public ReactScrollViewScrollState setDecelerationRate(float decelerationRate) {
mDecelerationRate = decelerationRate;
return this;
}
}

/**
Expand Down Expand Up @@ -491,6 +504,54 @@ public void onAnimationRepeat(Animator animator) {}
});
}

public static <
T extends
ViewGroup & FabricViewStateManager.HasFabricViewStateManager & HasScrollState
& HasFlingAnimator>
Point predictFinalScrollPosition(
final T scrollView,
int velocityX,
int velocityY,
int maximumOffsetX,
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
// so we can predict where a fling would land and snap to nearby that point.
OverScroller scroller = new OverScroller(scrollView.getContext());
scroller.setFriction(1.0f - scrollState.getDecelerationRate());

// predict where a fling would end up so we can scroll to the nearest snap offset
int width =
scrollView.getWidth()
- ViewCompat.getPaddingStart(scrollView)
- ViewCompat.getPaddingEnd(scrollView);
int height =
scrollView.getHeight() - scrollView.getPaddingBottom() - scrollView.getPaddingTop();
Point finalAnimatedPositionScroll = scrollState.getFinalAnimatedPositionScroll();
scroller.fling(
getNextFlingStartValue(
scrollView,
scrollView.getScrollX(),
finalAnimatedPositionScroll.x,
velocityX), // startX
getNextFlingStartValue(
scrollView,
scrollView.getScrollY(),
finalAnimatedPositionScroll.y,
velocityY), // startY
velocityX, // velocityX
velocityY, // velocityY
0, // minX
maximumOffsetX, // maxX
0, // minY
maximumOffsetY, // maxY
width / 2, // overX
height / 2 // overY
);
return new Point(scroller.getFinalX(), scroller.getFinalY());
}

public interface HasScrollState {
/** Get the scroll state for the current ScrollView */
ReactScrollViewScrollState getReactScrollViewScrollState();
Expand Down

0 comments on commit 6624327

Please sign in to comment.