Add scrollEventThrottle prop support in Android

Summary:
This diff adds `scrollEventThrottle` prop to Android platform. See [public doc](https://reactnative.dev/docs/scrollview#scrolleventthrottle-ios) for this prop. Currently this is only supported in iOS.

The throttling logic is [following](https://github.com/facebook/react-native/blob/main/React/Views/ScrollView/RCTScrollView.m#L650) iOS existing one.

Changelog:
[Android][Added] - Add scrollEventThrottle prop support in Android

Reviewed By: mdvacca

Differential Revision: D35735978

fbshipit-source-id: 97b73b4d5fbb93696555917b9252e95fd79ca581
This commit is contained in:
Xin Chen
2022-04-19 20:28:17 -07:00
committed by Facebook GitHub Bot
parent dd6325bafe
commit cf55fd587e
6 changed files with 108 additions and 13 deletions
@@ -114,4 +114,7 @@ public class ReactFeatureFlags {
public static boolean enableSpannableCache = false;
public static boolean dispatchPointerEvents = false;
/** Feature Flag to control RN Android scrollEventThrottle prop. */
public static boolean enableScrollEventThrottle = false;
}
@@ -43,6 +43,7 @@ import com.facebook.react.uimanager.ReactOverflowViewWithInset;
import com.facebook.react.uimanager.ViewProps;
import com.facebook.react.uimanager.events.NativeGestureUtil;
import com.facebook.react.views.scroll.ReactScrollViewHelper.HasFlingAnimator;
import com.facebook.react.views.scroll.ReactScrollViewHelper.HasScrollEventThrottle;
import com.facebook.react.views.scroll.ReactScrollViewHelper.HasScrollState;
import com.facebook.react.views.scroll.ReactScrollViewHelper.ReactScrollViewScrollState;
import com.facebook.react.views.view.ReactViewBackgroundManager;
@@ -56,7 +57,8 @@ public class ReactHorizontalScrollView extends HorizontalScrollView
FabricViewStateManager.HasFabricViewStateManager,
ReactOverflowViewWithInset,
HasScrollState,
HasFlingAnimator {
HasFlingAnimator,
HasScrollEventThrottle {
private static boolean DEBUG_MODE = false && ReactBuildConfig.DEBUG;
private static String TAG = ReactHorizontalScrollView.class.getSimpleName();
@@ -103,6 +105,8 @@ public class ReactHorizontalScrollView extends HorizontalScrollView
private final ReactScrollViewScrollState mReactScrollViewScrollState;
private final ValueAnimator DEFAULT_FLING_ANIMATOR = ObjectAnimator.ofInt(this, "scrollX", 0, 0);
private PointerEvents mPointerEvents = PointerEvents.AUTO;
private long mLastScrollDispatchTime = 0;
private int mScrollEventThrottle = 0;
private final Rect mTempRect = new Rect();
@@ -1272,4 +1276,24 @@ public class ReactHorizontalScrollView extends HorizontalScrollView
public PointerEvents getPointerEvents() {
return mPointerEvents;
}
@Override
public void setScrollEventThrottle(int scrollEventThrottle) {
mScrollEventThrottle = scrollEventThrottle;
}
@Override
public int getScrollEventThrottle() {
return mScrollEventThrottle;
}
@Override
public void setLastScrollDispatchTime(long lastScrollDispatchTime) {
mLastScrollDispatchTime = lastScrollDispatchTime;
}
@Override
public long getLastScrollDispatchTime() {
return mLastScrollDispatchTime;
}
}
@@ -325,4 +325,9 @@ public class ReactHorizontalScrollViewManager extends ViewGroupManager<ReactHori
public void setPointerEvents(ReactHorizontalScrollView view, @Nullable String pointerEventsStr) {
view.setPointerEvents(PointerEvents.parsePointerEvents(pointerEventsStr));
}
@ReactProp(name = "scrollEventThrottle")
public void setScrollEventThrottle(ReactHorizontalScrollView view, int scrollEventThrottle) {
view.setScrollEventThrottle(scrollEventThrottle);
}
}
@@ -42,6 +42,7 @@ import com.facebook.react.uimanager.ReactOverflowViewWithInset;
import com.facebook.react.uimanager.ViewProps;
import com.facebook.react.uimanager.events.NativeGestureUtil;
import com.facebook.react.views.scroll.ReactScrollViewHelper.HasFlingAnimator;
import com.facebook.react.views.scroll.ReactScrollViewHelper.HasScrollEventThrottle;
import com.facebook.react.views.scroll.ReactScrollViewHelper.HasScrollState;
import com.facebook.react.views.scroll.ReactScrollViewHelper.ReactScrollViewScrollState;
import com.facebook.react.views.view.ReactViewBackgroundManager;
@@ -62,7 +63,8 @@ public class ReactScrollView extends ScrollView
FabricViewStateManager.HasFabricViewStateManager,
ReactOverflowViewWithInset,
HasScrollState,
HasFlingAnimator {
HasFlingAnimator,
HasScrollEventThrottle {
private static @Nullable Field sScrollerField;
private static boolean sTriedToGetScrollerField = false;
@@ -104,6 +106,8 @@ public class ReactScrollView extends ScrollView
new ReactScrollViewScrollState(ViewCompat.LAYOUT_DIRECTION_LTR);
private final ValueAnimator DEFAULT_FLING_ANIMATOR = ObjectAnimator.ofInt(this, "scrollY", 0, 0);
private PointerEvents mPointerEvents = PointerEvents.AUTO;
private long mLastScrollDispatchTime = 0;
private int mScrollEventThrottle = 0;
public ReactScrollView(Context context) {
this(context, null);
@@ -1168,4 +1172,24 @@ public class ReactScrollView extends ScrollView
public PointerEvents getPointerEvents() {
return mPointerEvents;
}
@Override
public void setScrollEventThrottle(int scrollEventThrottle) {
mScrollEventThrottle = scrollEventThrottle;
}
@Override
public int getScrollEventThrottle() {
return mScrollEventThrottle;
}
@Override
public void setLastScrollDispatchTime(long lastScrollDispatchTime) {
mLastScrollDispatchTime = lastScrollDispatchTime;
}
@Override
public long getLastScrollDispatchTime() {
return mLastScrollDispatchTime;
}
}
@@ -22,6 +22,7 @@ import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.WritableNativeMap;
import com.facebook.react.common.build.ReactBuildConfig;
import com.facebook.react.config.ReactFeatureFlags;
import com.facebook.react.uimanager.FabricViewStateManager;
import com.facebook.react.uimanager.PixelUtil;
import com.facebook.react.uimanager.UIManagerHelper;
@@ -68,34 +69,49 @@ public class ReactScrollViewHelper {
private static boolean mSmoothScrollDurationInitialized = false;
/** Shared by {@link ReactScrollView} and {@link ReactHorizontalScrollView}. */
public static void emitScrollEvent(ViewGroup scrollView, float xVelocity, float yVelocity) {
public static <T extends ViewGroup & HasScrollEventThrottle> void emitScrollEvent(
T scrollView, float xVelocity, float yVelocity) {
emitScrollEvent(scrollView, ScrollEventType.SCROLL, xVelocity, yVelocity);
}
public static void emitScrollBeginDragEvent(ViewGroup scrollView) {
public static <T extends ViewGroup & HasScrollEventThrottle> void emitScrollBeginDragEvent(
T scrollView) {
emitScrollEvent(scrollView, ScrollEventType.BEGIN_DRAG);
}
public static void emitScrollEndDragEvent(
ViewGroup scrollView, float xVelocity, float yVelocity) {
public static <T extends ViewGroup & HasScrollEventThrottle> void emitScrollEndDragEvent(
T scrollView, float xVelocity, float yVelocity) {
emitScrollEvent(scrollView, ScrollEventType.END_DRAG, xVelocity, yVelocity);
}
public static void emitScrollMomentumBeginEvent(
ViewGroup scrollView, int xVelocity, int yVelocity) {
public static <T extends ViewGroup & HasScrollEventThrottle> void emitScrollMomentumBeginEvent(
T scrollView, int xVelocity, int yVelocity) {
emitScrollEvent(scrollView, ScrollEventType.MOMENTUM_BEGIN, xVelocity, yVelocity);
}
public static void emitScrollMomentumEndEvent(ViewGroup scrollView) {
public static <T extends ViewGroup & HasScrollEventThrottle> void emitScrollMomentumEndEvent(
T scrollView) {
emitScrollEvent(scrollView, ScrollEventType.MOMENTUM_END);
}
private static void emitScrollEvent(ViewGroup scrollView, ScrollEventType scrollEventType) {
private static <T extends ViewGroup & HasScrollEventThrottle> void emitScrollEvent(
T scrollView, ScrollEventType scrollEventType) {
emitScrollEvent(scrollView, scrollEventType, 0, 0);
}
private static void emitScrollEvent(
ViewGroup scrollView, ScrollEventType scrollEventType, float xVelocity, float yVelocity) {
private static <T extends ViewGroup & HasScrollEventThrottle> void emitScrollEvent(
T scrollView, ScrollEventType scrollEventType, float xVelocity, float yVelocity) {
long now = System.currentTimeMillis();
// Throttle the scroll event if scrollEventThrottle is set to be equal or more than 17 ms.
// We limit the delta to 17ms so that small throttles intended to enable 60fps updates will not
// inadvertently filter out any scroll events.
if (ReactFeatureFlags.enableScrollEventThrottle
&& scrollView.getScrollEventThrottle()
>= Math.max(17, now - scrollView.getLastScrollDispatchTime())) {
// Scroll events are throttled.
return;
}
View contentView = scrollView.getChildAt(0);
if (contentView == null) {
@@ -129,6 +145,7 @@ public class ReactScrollViewHelper {
contentView.getHeight(),
scrollView.getWidth(),
scrollView.getHeight()));
scrollView.setLastScrollDispatchTime(now);
}
}
@@ -469,7 +486,7 @@ public class ReactScrollViewHelper {
public static <
T extends
ViewGroup & FabricViewStateManager.HasFabricViewStateManager & HasScrollState
& HasFlingAnimator>
& HasFlingAnimator & HasScrollEventThrottle>
void updateStateOnScrollChanged(
final T scrollView, final float xVelocity, final float yVelocity) {
// Race an UpdateState with every onScroll. This makes it more likely that, in Fabric,
@@ -579,4 +596,21 @@ public class ReactScrollViewHelper {
/** Get the fling distance with current velocity for prediction */
int getFlingExtrapolatedDistance(int velocity);
}
public interface HasScrollEventThrottle {
/**
* Set the scroll event throttle in ms. This number is used to throttle the scroll events. The
* default value is zero, which means the scroll events are sent with no throttle.
*/
void setScrollEventThrottle(int scrollEventThrottle);
/** Get the scroll event throttle in ms. */
int getScrollEventThrottle();
/** Set the scroll view's last dispatch time for throttling */
void setLastScrollDispatchTime(long lastScrollDispatchTime);
/** Get the scroll view dispatch time for throttling */
long getLastScrollDispatchTime();
}
}
@@ -367,4 +367,9 @@ public class ReactScrollViewManager extends ViewGroupManager<ReactScrollView>
public void setPointerEvents(ReactScrollView view, @Nullable String pointerEventsStr) {
view.setPointerEvents(PointerEvents.parsePointerEvents(pointerEventsStr));
}
@ReactProp(name = "scrollEventThrottle")
public void setScrollEventThrottle(ReactScrollView view, int scrollEventThrottle) {
view.setScrollEventThrottle(scrollEventThrottle);
}
}