Skip to content

Commit

Permalink
Add scrollEventThrottle prop support in Android
Browse files Browse the repository at this point in the history
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
  • Loading branch information
ryancat authored and facebook-github-bot committed Apr 20, 2022
1 parent dd6325b commit cf55fd5
Show file tree
Hide file tree
Showing 6 changed files with 108 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -114,4 +114,7 @@ public static boolean doesUseOverflowInset() {
public static boolean enableSpannableCache = false;

public static boolean dispatchPointerEvents = false;

/** Feature Flag to control RN Android scrollEventThrottle prop. */
public static boolean enableScrollEventThrottle = false;
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
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;
Expand All @@ -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();
Expand Down Expand Up @@ -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();

Expand Down Expand Up @@ -1272,4 +1276,24 @@ public void setPointerEvents(PointerEvents pointerEvents) {
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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -325,4 +325,9 @@ public void setContentOffset(ReactHorizontalScrollView view, ReadableMap value)
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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
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;
Expand All @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -1168,4 +1172,24 @@ public void setPointerEvents(PointerEvents pointerEvents) {
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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
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;
Expand Down Expand Up @@ -68,34 +69,49 @@ void onScroll(
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) {
Expand Down Expand Up @@ -129,6 +145,7 @@ private static void emitScrollEvent(
contentView.getHeight(),
scrollView.getWidth(),
scrollView.getHeight()));
scrollView.setLastScrollDispatchTime(now);
}
}

Expand Down Expand Up @@ -469,7 +486,7 @@ public WritableMap getStateUpdate() {
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,
Expand Down Expand Up @@ -579,4 +596,21 @@ public interface HasFlingAnimator {
/** 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();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -367,4 +367,9 @@ public static Map<String, Object> createExportedCustomDirectEventTypeConstants()
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);
}
}

0 comments on commit cf55fd5

Please sign in to comment.