diff --git a/packages/react-native/Libraries/Components/ScrollView/AndroidHorizontalScrollViewNativeComponent.js b/packages/react-native/Libraries/Components/ScrollView/AndroidHorizontalScrollViewNativeComponent.js index 7f2179f5fde9b9..f2b9ad332317a7 100644 --- a/packages/react-native/Libraries/Components/ScrollView/AndroidHorizontalScrollViewNativeComponent.js +++ b/packages/react-native/Libraries/Components/ScrollView/AndroidHorizontalScrollViewNativeComponent.js @@ -59,6 +59,7 @@ export const __INTERNAL_VIEW_CONFIG: PartialViewConfig = { process: require('../../StyleSheet/processColor').default, }, pointerEvents: true, + responderIgnoreScroll: true, }, }; diff --git a/packages/react-native/Libraries/Components/ScrollView/ScrollView.d.ts b/packages/react-native/Libraries/Components/ScrollView/ScrollView.d.ts index b6e538ddc5a1be..487840cc8b5797 100644 --- a/packages/react-native/Libraries/Components/ScrollView/ScrollView.d.ts +++ b/packages/react-native/Libraries/Components/ScrollView/ScrollView.d.ts @@ -586,6 +586,19 @@ export interface ScrollViewPropsAndroid { * Causes the scrollbars not to turn transparent when they are not in use. The default value is false. */ persistentScrollbar?: boolean | undefined; + + /** + * Emit touchcancel from native Android ScrollView, instead of letting Responder System + * terminate responder at scroll. + * + * When it's set to false, `onTouchCancel` on ScrollView children will not be invoked, but + * `onTouchEnd` will be invoked (if touch starts on it) - which aligns with iOS. + * + * The default value is true. + * + * @platform android + */ + responderIgnoreScroll?: boolean | undefined; } export interface ScrollViewProps diff --git a/packages/react-native/Libraries/Components/ScrollView/ScrollView.js b/packages/react-native/Libraries/Components/ScrollView/ScrollView.js index fdb830dc6e6244..afca032702c4e9 100644 --- a/packages/react-native/Libraries/Components/ScrollView/ScrollView.js +++ b/packages/react-native/Libraries/Components/ScrollView/ScrollView.js @@ -388,6 +388,19 @@ export type ScrollViewPropsAndroid = $ReadOnly<{ * @platform android */ fadingEdgeLength?: ?number | {start: number, end: number}, + + /** + * Emit touchcancel from native Android ScrollView, instead of letting Responder System + * terminate responder at scroll. + * + * When it's set to false, `onTouchCancel` on ScrollView children will not be invoked, but + * `onTouchEnd` will be invoked (if touch starts on it) - which aligns with iOS. + * + * The default value is true. + * + * @platform android + */ + responderIgnoreScroll?: ?boolean, }>; type StickyHeaderComponentType = component( diff --git a/packages/react-native/Libraries/Components/ScrollView/ScrollViewNativeComponent.js b/packages/react-native/Libraries/Components/ScrollView/ScrollViewNativeComponent.js index 82f9c93baf348c..9aa8b2201fb48b 100644 --- a/packages/react-native/Libraries/Components/ScrollView/ScrollViewNativeComponent.js +++ b/packages/react-native/Libraries/Components/ScrollView/ScrollViewNativeComponent.js @@ -88,6 +88,7 @@ export const __INTERNAL_VIEW_CONFIG: PartialViewConfig = }, pointerEvents: true, isInvertedVirtualizedList: true, + responderIgnoreScroll: true, }, } : { diff --git a/packages/react-native/Libraries/Components/ScrollView/ScrollViewNativeComponentType.js b/packages/react-native/Libraries/Components/ScrollView/ScrollViewNativeComponentType.js index 1feee6999541c0..8b84bea268f345 100644 --- a/packages/react-native/Libraries/Components/ScrollView/ScrollViewNativeComponentType.js +++ b/packages/react-native/Libraries/Components/ScrollView/ScrollViewNativeComponentType.js @@ -63,6 +63,7 @@ export type ScrollViewNativeProps = $ReadOnly<{ pagingEnabled?: ?boolean, persistentScrollbar?: ?boolean, pinchGestureEnabled?: ?boolean, + responderIgnoreScroll?: ?boolean, scrollEnabled?: ?boolean, scrollEventThrottle?: ?number, scrollIndicatorInsets?: ?EdgeInsetsProp, diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.java index 63491c9d1c508b..498ea4b1c575d1 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.java @@ -133,6 +133,7 @@ public class ReactHorizontalScrollView extends HorizontalScrollView private @Nullable MaintainVisibleScrollPositionHelper mMaintainVisibleContentPositionHelper; private int mFadingEdgeLengthStart = 0; private int mFadingEdgeLengthEnd = 0; + private boolean mResponderIgnoreScroll = true; public ReactHorizontalScrollView(Context context) { this(context, null); @@ -196,6 +197,7 @@ private void initView() { mMaintainVisibleContentPositionHelper = null; mFadingEdgeLengthStart = 0; mFadingEdgeLengthEnd = 0; + mResponderIgnoreScroll = true; } /* package */ void recycleView() { @@ -1722,4 +1724,12 @@ public void setLastScrollDispatchTime(long lastScrollDispatchTime) { public long getLastScrollDispatchTime() { return mLastScrollDispatchTime; } + + public void setResponderIgnoreScroll(boolean responderIgnoreScroll) { + mResponderIgnoreScroll = responderIgnoreScroll; + } + + public boolean getResponderIgnoreScroll() { + return mResponderIgnoreScroll; + } } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollViewManager.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollViewManager.kt index 5a364167cf7e1f..b9e31282e788a8 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollViewManager.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollViewManager.kt @@ -413,6 +413,14 @@ constructor(private val fpsListener: FpsListener? = null) : // Do Nothing: Align with static ViewConfigs } + @ReactProp(name = "responderIgnoreScroll") + public fun setResponderIgnoreScroll( + view: ReactHorizontalScrollView, + responderIgnoreScroll: Boolean, + ) { + view.responderIgnoreScroll = responderIgnoreScroll + } + public companion object { public const val REACT_CLASS: String = "AndroidHorizontalScrollView" } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollView.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollView.java index d98ba44be57927..3c3bd3b912bd3d 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollView.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollView.java @@ -131,6 +131,7 @@ public class ReactScrollView extends ScrollView private @Nullable MaintainVisibleScrollPositionHelper mMaintainVisibleContentPositionHelper; private int mFadingEdgeLengthStart; private int mFadingEdgeLengthEnd; + private boolean mResponderIgnoreScroll; public ReactScrollView(Context context) { this(context, null); @@ -193,6 +194,7 @@ private void initView() { mMaintainVisibleContentPositionHelper = null; mFadingEdgeLengthStart = 0; mFadingEdgeLengthEnd = 0; + mResponderIgnoreScroll = true; } /* package */ void recycleView() { @@ -1527,4 +1529,12 @@ public void setLastScrollDispatchTime(long lastScrollDispatchTime) { public long getLastScrollDispatchTime() { return mLastScrollDispatchTime; } + + public void setResponderIgnoreScroll(boolean responderIgnoreScroll) { + mResponderIgnoreScroll = responderIgnoreScroll; + } + + public boolean getResponderIgnoreScroll() { + return mResponderIgnoreScroll; + } } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewManager.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewManager.kt index 32f017cdd2aed6..889fa331ba5b76 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewManager.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewManager.kt @@ -428,6 +428,11 @@ constructor(private val fpsListener: FpsListener? = null) : } } + @ReactProp(name = "responderIgnoreScroll") + public fun setResponderIgnoreScroll(view: ReactScrollView, responderIgnoreScroll: Boolean) { + view.responderIgnoreScroll = responderIgnoreScroll + } + public companion object { public const val REACT_CLASS: String = "RCTScrollView" diff --git a/packages/react-native/ReactCommon/react/renderer/components/scrollview/platform/android/react/renderer/components/scrollview/HostPlatformScrollViewProps.cpp b/packages/react-native/ReactCommon/react/renderer/components/scrollview/platform/android/react/renderer/components/scrollview/HostPlatformScrollViewProps.cpp index 802ef8265a7b9b..ab37d0a40bc09f 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/scrollview/platform/android/react/renderer/components/scrollview/HostPlatformScrollViewProps.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/scrollview/platform/android/react/renderer/components/scrollview/HostPlatformScrollViewProps.cpp @@ -38,6 +38,15 @@ HostPlatformScrollViewProps::HostPlatformScrollViewProps( rawProps, "nestedScrollEnabled", sourceProps.nestedScrollEnabled, + true)), + responderIgnoreScroll( + ReactNativeFeatureFlags::enableCppPropsIteratorSetter() + ? sourceProps.responderIgnoreScroll + : convertRawProp( + context, + rawProps, + "responderIgnoreScroll", + sourceProps.responderIgnoreScroll, true)) {} @@ -57,6 +66,7 @@ void HostPlatformScrollViewProps::setProp( switch (hash) { RAW_SET_PROP_SWITCH_CASE_BASIC(sendMomentumEvents); RAW_SET_PROP_SWITCH_CASE_BASIC(nestedScrollEnabled); + RAW_SET_PROP_SWITCH_CASE_BASIC(responderIgnoreScroll); } } @@ -76,7 +86,11 @@ SharedDebugStringConvertibleList HostPlatformScrollViewProps::getDebugProps() debugStringConvertibleItem( "nestedScrollEnabled", nestedScrollEnabled, - defaultScrollViewProps.nestedScrollEnabled)}; + defaultScrollViewProps.nestedScrollEnabled), + debugStringConvertibleItem( + "responderIgnoreScroll", + responderIgnoreScroll, + defaultScrollViewProps.responderIgnoreScroll)}; } #endif @@ -348,6 +362,10 @@ folly::dynamic HostPlatformScrollViewProps::getDiffProps( result["nestedScrollEnabled"] = nestedScrollEnabled; } + if (responderIgnoreScroll != oldProps->responderIgnoreScroll) { + result["responderIgnoreScroll"] = responderIgnoreScroll; + } + return result; } diff --git a/packages/react-native/ReactCommon/react/renderer/components/scrollview/platform/android/react/renderer/components/scrollview/HostPlatformScrollViewProps.h b/packages/react-native/ReactCommon/react/renderer/components/scrollview/platform/android/react/renderer/components/scrollview/HostPlatformScrollViewProps.h index 4c54decb93996c..fbea4bbc7f0077 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/scrollview/platform/android/react/renderer/components/scrollview/HostPlatformScrollViewProps.h +++ b/packages/react-native/ReactCommon/react/renderer/components/scrollview/platform/android/react/renderer/components/scrollview/HostPlatformScrollViewProps.h @@ -32,6 +32,7 @@ class HostPlatformScrollViewProps final : public BaseScrollViewProps { bool sendMomentumEvents{}; bool nestedScrollEnabled{}; + bool responderIgnoreScroll{}; #pragma mark - DebugStringConvertible diff --git a/packages/react-native/ReactNativeApi.d.ts b/packages/react-native/ReactNativeApi.d.ts index 7771f1d4193c74..f4ef4835b36386 100644 --- a/packages/react-native/ReactNativeApi.d.ts +++ b/packages/react-native/ReactNativeApi.d.ts @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<575e58047a8edf6bbc016768775a258f>> + * @generated SignedSource<<339a44ddc978dd30410a496c72abd5d5>> * * This file was generated by scripts/js-api/build-types/index.js. */ @@ -4452,6 +4452,7 @@ declare type ScrollViewNativeProps = Readonly< pagingEnabled?: boolean persistentScrollbar?: boolean pinchGestureEnabled?: boolean + responderIgnoreScroll?: boolean scrollEnabled?: boolean scrollEventThrottle?: number scrollIndicatorInsets?: EdgeInsetsProp @@ -4484,6 +4485,7 @@ declare type ScrollViewPropsAndroid = { readonly nestedScrollEnabled?: boolean readonly overScrollMode?: "always" | "auto" | "never" readonly persistentScrollbar?: boolean + readonly responderIgnoreScroll?: boolean readonly scrollPerfTag?: string } declare type ScrollViewPropsIOS = { @@ -5967,8 +5969,8 @@ export { EventSubscription, // b8d084aa ExtendedExceptionData, // 5a6ccf5a FilterFunction, // bf24c0e3 - FlatList, // cbb48cbe - FlatListProps, // 451be810 + FlatList, // 7d957603 + FlatListProps, // ab1d8dcf FocusEvent, // 529b43eb FontVariant, // 7c7558bb GestureResponderEvent, // b466f6d6 @@ -6101,14 +6103,14 @@ export { ScrollToLocationParamsType, // d7ecdad1 ScrollView, // 7fb7c469 ScrollViewImperativeMethods, // eb20aa46 - ScrollViewProps, // 27986ff5 - ScrollViewPropsAndroid, // 84e2134b + ScrollViewProps, // 7b402e77 + ScrollViewPropsAndroid, // 50083685 ScrollViewPropsIOS, // d83c9733 ScrollViewScrollToOptions, // 3313411e SectionBase, // b376bddc - SectionList, // ff1193b2 + SectionList, // 4c2799db SectionListData, // 119baf83 - SectionListProps, // c9ac8e07 + SectionListProps, // 85e8c6ff SectionListRenderItem, // 1fad0435 SectionListRenderItemInfo, // 745e1992 Separators, // 6a45f7e3 @@ -6173,9 +6175,9 @@ export { ViewStyle, // c2db0e6e VirtualViewMode, // 85a69ef6 VirtualizedList, // 4d513939 - VirtualizedListProps, // a99d36db + VirtualizedListProps, // 632e6d18 VirtualizedSectionList, // 446ba0df - VirtualizedSectionListProps, // 6cd4b378 + VirtualizedSectionListProps, // c5101563 WrapperComponentProvider, // 9cf3844c codegenNativeCommands, // e16d62f7 codegenNativeComponent, // ed4c8103