Refactor predictFinalScrollPosition method to the helper class

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
This commit is contained in:
Xin Chen
2021-12-06 09:40:57 -08:00
committed by Facebook GitHub Bot
parent 1c1945569f
commit 66243271a7
3 changed files with 69 additions and 56 deletions
@@ -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;
@@ -216,10 +215,10 @@ public class ReactHorizontalScrollView extends HorizontalScrollView
}
public void setDecelerationRate(float decelerationRate) {
mDecelerationRate = decelerationRate;
getReactScrollViewScrollState().setDecelerationRate(decelerationRate);
if (mScroller != null) {
mScroller.setFriction(1.0f - mDecelerationRate);
mScroller.setFriction(1.0f - decelerationRate);
}
}
@@ -802,32 +801,9 @@ public class ReactHorizontalScrollView extends HorizontalScrollView
}
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;
}
/**
@@ -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;
@@ -191,10 +190,10 @@ public class ReactScrollView extends ScrollView
}
public void setDecelerationRate(float decelerationRate) {
mDecelerationRate = decelerationRate;
getReactScrollViewScrollState().setDecelerationRate(decelerationRate);
if (mScroller != null) {
mScroller.setFriction(1.0f - mDecelerationRate);
mScroller.setFriction(1.0f - decelerationRate);
}
}
@@ -591,32 +590,9 @@ public class ReactScrollView extends ScrollView
}
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() {
@@ -15,6 +15,7 @@ import android.view.View;
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;
@@ -222,6 +223,7 @@ public class ReactScrollViewHelper {
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;
@@ -291,6 +293,17 @@ public class ReactScrollViewHelper {
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;
}
}
/**
@@ -491,6 +504,54 @@ public class ReactScrollViewHelper {
});
}
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();