Skip to content

Commit cf55fd5

Browse files
ryancatfacebook-github-bot
authored andcommitted
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
1 parent dd6325b commit cf55fd5

6 files changed

+108
-13
lines changed

ReactAndroid/src/main/java/com/facebook/react/config/ReactFeatureFlags.java

+3
Original file line numberDiff line numberDiff line change
@@ -114,4 +114,7 @@ public static boolean doesUseOverflowInset() {
114114
public static boolean enableSpannableCache = false;
115115

116116
public static boolean dispatchPointerEvents = false;
117+
118+
/** Feature Flag to control RN Android scrollEventThrottle prop. */
119+
public static boolean enableScrollEventThrottle = false;
117120
}

ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.java

+25-1
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
import com.facebook.react.uimanager.ViewProps;
4444
import com.facebook.react.uimanager.events.NativeGestureUtil;
4545
import com.facebook.react.views.scroll.ReactScrollViewHelper.HasFlingAnimator;
46+
import com.facebook.react.views.scroll.ReactScrollViewHelper.HasScrollEventThrottle;
4647
import com.facebook.react.views.scroll.ReactScrollViewHelper.HasScrollState;
4748
import com.facebook.react.views.scroll.ReactScrollViewHelper.ReactScrollViewScrollState;
4849
import com.facebook.react.views.view.ReactViewBackgroundManager;
@@ -56,7 +57,8 @@ public class ReactHorizontalScrollView extends HorizontalScrollView
5657
FabricViewStateManager.HasFabricViewStateManager,
5758
ReactOverflowViewWithInset,
5859
HasScrollState,
59-
HasFlingAnimator {
60+
HasFlingAnimator,
61+
HasScrollEventThrottle {
6062

6163
private static boolean DEBUG_MODE = false && ReactBuildConfig.DEBUG;
6264
private static String TAG = ReactHorizontalScrollView.class.getSimpleName();
@@ -103,6 +105,8 @@ public class ReactHorizontalScrollView extends HorizontalScrollView
103105
private final ReactScrollViewScrollState mReactScrollViewScrollState;
104106
private final ValueAnimator DEFAULT_FLING_ANIMATOR = ObjectAnimator.ofInt(this, "scrollX", 0, 0);
105107
private PointerEvents mPointerEvents = PointerEvents.AUTO;
108+
private long mLastScrollDispatchTime = 0;
109+
private int mScrollEventThrottle = 0;
106110

107111
private final Rect mTempRect = new Rect();
108112

@@ -1272,4 +1276,24 @@ public void setPointerEvents(PointerEvents pointerEvents) {
12721276
public PointerEvents getPointerEvents() {
12731277
return mPointerEvents;
12741278
}
1279+
1280+
@Override
1281+
public void setScrollEventThrottle(int scrollEventThrottle) {
1282+
mScrollEventThrottle = scrollEventThrottle;
1283+
}
1284+
1285+
@Override
1286+
public int getScrollEventThrottle() {
1287+
return mScrollEventThrottle;
1288+
}
1289+
1290+
@Override
1291+
public void setLastScrollDispatchTime(long lastScrollDispatchTime) {
1292+
mLastScrollDispatchTime = lastScrollDispatchTime;
1293+
}
1294+
1295+
@Override
1296+
public long getLastScrollDispatchTime() {
1297+
return mLastScrollDispatchTime;
1298+
}
12751299
}

ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollViewManager.java

+5
Original file line numberDiff line numberDiff line change
@@ -325,4 +325,9 @@ public void setContentOffset(ReactHorizontalScrollView view, ReadableMap value)
325325
public void setPointerEvents(ReactHorizontalScrollView view, @Nullable String pointerEventsStr) {
326326
view.setPointerEvents(PointerEvents.parsePointerEvents(pointerEventsStr));
327327
}
328+
329+
@ReactProp(name = "scrollEventThrottle")
330+
public void setScrollEventThrottle(ReactHorizontalScrollView view, int scrollEventThrottle) {
331+
view.setScrollEventThrottle(scrollEventThrottle);
332+
}
328333
}

ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollView.java

+25-1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
import com.facebook.react.uimanager.ViewProps;
4343
import com.facebook.react.uimanager.events.NativeGestureUtil;
4444
import com.facebook.react.views.scroll.ReactScrollViewHelper.HasFlingAnimator;
45+
import com.facebook.react.views.scroll.ReactScrollViewHelper.HasScrollEventThrottle;
4546
import com.facebook.react.views.scroll.ReactScrollViewHelper.HasScrollState;
4647
import com.facebook.react.views.scroll.ReactScrollViewHelper.ReactScrollViewScrollState;
4748
import com.facebook.react.views.view.ReactViewBackgroundManager;
@@ -62,7 +63,8 @@ public class ReactScrollView extends ScrollView
6263
FabricViewStateManager.HasFabricViewStateManager,
6364
ReactOverflowViewWithInset,
6465
HasScrollState,
65-
HasFlingAnimator {
66+
HasFlingAnimator,
67+
HasScrollEventThrottle {
6668

6769
private static @Nullable Field sScrollerField;
6870
private static boolean sTriedToGetScrollerField = false;
@@ -104,6 +106,8 @@ public class ReactScrollView extends ScrollView
104106
new ReactScrollViewScrollState(ViewCompat.LAYOUT_DIRECTION_LTR);
105107
private final ValueAnimator DEFAULT_FLING_ANIMATOR = ObjectAnimator.ofInt(this, "scrollY", 0, 0);
106108
private PointerEvents mPointerEvents = PointerEvents.AUTO;
109+
private long mLastScrollDispatchTime = 0;
110+
private int mScrollEventThrottle = 0;
107111

108112
public ReactScrollView(Context context) {
109113
this(context, null);
@@ -1168,4 +1172,24 @@ public void setPointerEvents(PointerEvents pointerEvents) {
11681172
public PointerEvents getPointerEvents() {
11691173
return mPointerEvents;
11701174
}
1175+
1176+
@Override
1177+
public void setScrollEventThrottle(int scrollEventThrottle) {
1178+
mScrollEventThrottle = scrollEventThrottle;
1179+
}
1180+
1181+
@Override
1182+
public int getScrollEventThrottle() {
1183+
return mScrollEventThrottle;
1184+
}
1185+
1186+
@Override
1187+
public void setLastScrollDispatchTime(long lastScrollDispatchTime) {
1188+
mLastScrollDispatchTime = lastScrollDispatchTime;
1189+
}
1190+
1191+
@Override
1192+
public long getLastScrollDispatchTime() {
1193+
return mLastScrollDispatchTime;
1194+
}
11711195
}

ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewHelper.java

+45-11
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import com.facebook.react.bridge.WritableMap;
2323
import com.facebook.react.bridge.WritableNativeMap;
2424
import com.facebook.react.common.build.ReactBuildConfig;
25+
import com.facebook.react.config.ReactFeatureFlags;
2526
import com.facebook.react.uimanager.FabricViewStateManager;
2627
import com.facebook.react.uimanager.PixelUtil;
2728
import com.facebook.react.uimanager.UIManagerHelper;
@@ -68,34 +69,49 @@ void onScroll(
6869
private static boolean mSmoothScrollDurationInitialized = false;
6970

7071
/** Shared by {@link ReactScrollView} and {@link ReactHorizontalScrollView}. */
71-
public static void emitScrollEvent(ViewGroup scrollView, float xVelocity, float yVelocity) {
72+
public static <T extends ViewGroup & HasScrollEventThrottle> void emitScrollEvent(
73+
T scrollView, float xVelocity, float yVelocity) {
7274
emitScrollEvent(scrollView, ScrollEventType.SCROLL, xVelocity, yVelocity);
7375
}
7476

75-
public static void emitScrollBeginDragEvent(ViewGroup scrollView) {
77+
public static <T extends ViewGroup & HasScrollEventThrottle> void emitScrollBeginDragEvent(
78+
T scrollView) {
7679
emitScrollEvent(scrollView, ScrollEventType.BEGIN_DRAG);
7780
}
7881

79-
public static void emitScrollEndDragEvent(
80-
ViewGroup scrollView, float xVelocity, float yVelocity) {
82+
public static <T extends ViewGroup & HasScrollEventThrottle> void emitScrollEndDragEvent(
83+
T scrollView, float xVelocity, float yVelocity) {
8184
emitScrollEvent(scrollView, ScrollEventType.END_DRAG, xVelocity, yVelocity);
8285
}
8386

84-
public static void emitScrollMomentumBeginEvent(
85-
ViewGroup scrollView, int xVelocity, int yVelocity) {
87+
public static <T extends ViewGroup & HasScrollEventThrottle> void emitScrollMomentumBeginEvent(
88+
T scrollView, int xVelocity, int yVelocity) {
8689
emitScrollEvent(scrollView, ScrollEventType.MOMENTUM_BEGIN, xVelocity, yVelocity);
8790
}
8891

89-
public static void emitScrollMomentumEndEvent(ViewGroup scrollView) {
92+
public static <T extends ViewGroup & HasScrollEventThrottle> void emitScrollMomentumEndEvent(
93+
T scrollView) {
9094
emitScrollEvent(scrollView, ScrollEventType.MOMENTUM_END);
9195
}
9296

93-
private static void emitScrollEvent(ViewGroup scrollView, ScrollEventType scrollEventType) {
97+
private static <T extends ViewGroup & HasScrollEventThrottle> void emitScrollEvent(
98+
T scrollView, ScrollEventType scrollEventType) {
9499
emitScrollEvent(scrollView, scrollEventType, 0, 0);
95100
}
96101

97-
private static void emitScrollEvent(
98-
ViewGroup scrollView, ScrollEventType scrollEventType, float xVelocity, float yVelocity) {
102+
private static <T extends ViewGroup & HasScrollEventThrottle> void emitScrollEvent(
103+
T scrollView, ScrollEventType scrollEventType, float xVelocity, float yVelocity) {
104+
long now = System.currentTimeMillis();
105+
// Throttle the scroll event if scrollEventThrottle is set to be equal or more than 17 ms.
106+
// We limit the delta to 17ms so that small throttles intended to enable 60fps updates will not
107+
// inadvertently filter out any scroll events.
108+
if (ReactFeatureFlags.enableScrollEventThrottle
109+
&& scrollView.getScrollEventThrottle()
110+
>= Math.max(17, now - scrollView.getLastScrollDispatchTime())) {
111+
// Scroll events are throttled.
112+
return;
113+
}
114+
99115
View contentView = scrollView.getChildAt(0);
100116

101117
if (contentView == null) {
@@ -129,6 +145,7 @@ private static void emitScrollEvent(
129145
contentView.getHeight(),
130146
scrollView.getWidth(),
131147
scrollView.getHeight()));
148+
scrollView.setLastScrollDispatchTime(now);
132149
}
133150
}
134151

@@ -469,7 +486,7 @@ public WritableMap getStateUpdate() {
469486
public static <
470487
T extends
471488
ViewGroup & FabricViewStateManager.HasFabricViewStateManager & HasScrollState
472-
& HasFlingAnimator>
489+
& HasFlingAnimator & HasScrollEventThrottle>
473490
void updateStateOnScrollChanged(
474491
final T scrollView, final float xVelocity, final float yVelocity) {
475492
// Race an UpdateState with every onScroll. This makes it more likely that, in Fabric,
@@ -579,4 +596,21 @@ public interface HasFlingAnimator {
579596
/** Get the fling distance with current velocity for prediction */
580597
int getFlingExtrapolatedDistance(int velocity);
581598
}
599+
600+
public interface HasScrollEventThrottle {
601+
/**
602+
* Set the scroll event throttle in ms. This number is used to throttle the scroll events. The
603+
* default value is zero, which means the scroll events are sent with no throttle.
604+
*/
605+
void setScrollEventThrottle(int scrollEventThrottle);
606+
607+
/** Get the scroll event throttle in ms. */
608+
int getScrollEventThrottle();
609+
610+
/** Set the scroll view's last dispatch time for throttling */
611+
void setLastScrollDispatchTime(long lastScrollDispatchTime);
612+
613+
/** Get the scroll view dispatch time for throttling */
614+
long getLastScrollDispatchTime();
615+
}
582616
}

ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewManager.java

+5
Original file line numberDiff line numberDiff line change
@@ -367,4 +367,9 @@ public static Map<String, Object> createExportedCustomDirectEventTypeConstants()
367367
public void setPointerEvents(ReactScrollView view, @Nullable String pointerEventsStr) {
368368
view.setPointerEvents(PointerEvents.parsePointerEvents(pointerEventsStr));
369369
}
370+
371+
@ReactProp(name = "scrollEventThrottle")
372+
public void setScrollEventThrottle(ReactScrollView view, int scrollEventThrottle) {
373+
view.setScrollEventThrottle(scrollEventThrottle);
374+
}
370375
}

0 commit comments

Comments
 (0)