Skip to content

Commit cbd97db

Browse files
authored
[android] fixed experimental pointers breaking after pan (#3801)
## Description In a project using experimental pointer events, activation of the pan gesture stopped recognition of Js pointer events. This PR should resolve this issue. ## Fix I believe the error is due to the fact that `onCancel` calls rootView.onChildStartedNativeGesture, which sets the `UNSET_CHILD_VIEW_ID` in JSPointerDispatcher.kt to current view tag, which is never cleared. This blocks pointer events from being called, as the pointer dispatcher thinks that some child is handling a native gesture. I added cleanup when all fingers have been lifted. ## Test plan Enable experimental pointer events: https://reactnative.dev/blog/2022/12/13/pointer-events-in-react-native#enable-feature-flags. Use the following code to test the component: <details> ```ts import React from 'react'; import { Text, Pressable, StyleSheet } from 'react-native'; import { Gesture, GestureDetector, GestureHandlerRootView } from 'react-native-gesture-handler'; import Animated, { useAnimatedStyle, useSharedValue, withTiming } from 'react-native-reanimated'; const END_POSITION = 200; function App(): React.JSX.Element { const [count, setCount] = React.useState(0); const [pressCount, setPressCount] = React.useState(0); const onLeft = useSharedValue(true); const position = useSharedValue(0); const panGesture = Gesture.Pan() .onUpdate((e) => { if (onLeft.value) { position.value = e.translationX; } else { position.value = END_POSITION + e.translationX; } }) .onEnd((e) => { if (position.value > END_POSITION / 2) { position.value = withTiming(END_POSITION, { duration: 100 }); onLeft.value = false; } else { position.value = withTiming(0, { duration: 100 }); onLeft.value = true; } }); const animatedStyle = useAnimatedStyle(() => ({ transform: [{ translateX: position.value }], })); return ( <GestureHandlerRootView style={styles.container}> <Pressable style={{ padding: 16, backgroundColor: '#aa0044' }} onPointerDown={() => setCount(n => n + 1)} onPressIn={() => setPressCount(n => n + 1)} > <Text style={{ color: 'black' }}>Press me</Text> </Pressable> <Text style={{ marginBottom: 16, color: 'black' }}> pointer: {count} -- press: {pressCount} </Text> <GestureDetector gesture={panGesture}> <Animated.View style={[styles.box, animatedStyle]} /> </GestureDetector> </GestureHandlerRootView> ); } const styles = StyleSheet.create({ container: { padding: 64, flex: 1, }, box: { height: 120, width: 120, backgroundColor: '#b58df1', borderRadius: 20, marginBottom: 30, }, }); export default App; ``` </details> If you have experimental pointer events turned on clicking on the upper pressable should increase both counters even after using pan gesture on the box below. Before this PR, after panning, only one counter worked.
1 parent b53b8ef commit cbd97db

File tree

2 files changed

+11
-0
lines changed

2 files changed

+11
-0
lines changed

packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandlerOrchestrator.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import android.view.MotionEvent
66
import android.view.View
77
import android.view.ViewGroup
88
import android.widget.EditText
9+
import com.facebook.react.uimanager.RootView
910
import com.swmansion.gesturehandler.react.RNGestureHandlerRootHelper
1011
import com.swmansion.gesturehandler.react.RNGestureHandlerRootView
1112
import java.util.*
@@ -14,6 +15,7 @@ class GestureHandlerOrchestrator(
1415
private val wrapperView: ViewGroup,
1516
private val handlerRegistry: GestureHandlerRegistry,
1617
private val viewConfigHelper: ViewConfigurationHelper,
18+
private val rootView: ViewGroup,
1719
) {
1820
/**
1921
* Minimum alpha (value from 0 to 1) that should be set to a view so that it can be treated as a
@@ -56,6 +58,14 @@ class GestureHandlerOrchestrator(
5658
if (finishedHandlersCleanupScheduled && handlingChangeSemaphore == 0) {
5759
cleanupFinishedHandlers()
5860
}
61+
if (action == MotionEvent.ACTION_UP ||
62+
action == MotionEvent.ACTION_CANCEL ||
63+
action == MotionEvent.ACTION_HOVER_EXIT
64+
) {
65+
if (gestureHandlers.isEmpty() && rootView is RootView) {
66+
rootView.onChildEndedNativeGesture(rootView, event)
67+
}
68+
}
5969
return true
6070
}
6171

packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerRootHelper.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ class RNGestureHandlerRootHelper(private val context: ReactContext, wrappedView:
3636
wrappedView,
3737
registry,
3838
RNViewConfigurationHelper(),
39+
rootView,
3940
).apply {
4041
minimumAlphaForTraversal = MIN_ALPHA_FOR_TOUCH
4142
}

0 commit comments

Comments
 (0)