Skip to content

Commit 8c41b09

Browse files
authored
Improve multi-pointer behavior (with ScrollView) on Android (#2551)
## Description This PR solves two problems: - `ScrollView` wrapped with `NativeViewGestureHandler` wasn't canceling the root view handler, which in turn meant that [`shouldIntercept` flag was never set](https://github.com/software-mansion/react-native-gesture-handler/blob/355a82fd3cf39b1bd4a57af958f040b563a1823e/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerRootHelper.kt#L71) and event was [dispatched to both, the handler and the view](https://github.com/software-mansion/react-native-gesture-handler/blob/355a82fd3cf39b1bd4a57af958f040b563a1823e/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerRootView.kt#L35-L37). - After a handler was activated and the root handler was canceled (setting `shouldIntercept` to true), and a new pointer was added to the screen, the root handler would begin and set `shouldIntercept` back to false. Again, this meant dispatching events both to the handler and the view. Fixes #1679 ## Test plan Tested on the Example app and [reproduction from the issue](#1679 (comment))
1 parent 95854bf commit 8c41b09

File tree

3 files changed

+11
-1
lines changed

3 files changed

+11
-1
lines changed

android/src/main/java/com/swmansion/gesturehandler/core/GestureHandlerOrchestrator.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,8 @@ class GestureHandlerOrchestrator(
325325
return parent === wrapperView
326326
}
327327

328+
fun isAnyHandlerActive() = gestureHandlers.any { it?.state == GestureHandler.STATE_ACTIVE }
329+
328330
/**
329331
* Transforms an event in the coordinates of wrapperView into the coordinate space of the received view.
330332
*

android/src/main/java/com/swmansion/gesturehandler/core/NativeViewGestureHandler.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import android.view.View
66
import android.view.ViewConfiguration
77
import android.view.ViewGroup
88
import android.widget.ScrollView
9+
import com.facebook.react.views.scroll.ReactScrollView
910
import com.facebook.react.views.swiperefresh.ReactSwipeRefreshLayout
1011
import com.facebook.react.views.textinput.ReactEditText
1112

@@ -75,6 +76,7 @@ class NativeViewGestureHandler : GestureHandler<NativeViewGestureHandler>() {
7576
is NativeViewGestureHandlerHook -> this.hook = view
7677
is ReactEditText -> this.hook = EditTextHook(this, view)
7778
is ReactSwipeRefreshLayout -> this.hook = SwipeRefreshLayoutHook(this, view)
79+
is ReactScrollView -> this.hook = ScrollViewHook()
7880
}
7981
}
8082

@@ -248,4 +250,8 @@ class NativeViewGestureHandler : GestureHandler<NativeViewGestureHandler>() {
248250
// oh well ¯\_(ツ)_/¯
249251
}
250252
}
253+
254+
private class ScrollViewHook : NativeViewGestureHandlerHook {
255+
override fun shouldCancelRootViewGestureHandlerIfNecessary() = true
256+
}
251257
}

android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerRootHelper.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,9 @@ class RNGestureHandlerRootHelper(private val context: ReactContext, wrappedView:
5959
private inner class RootViewGestureHandler : GestureHandler<RootViewGestureHandler>() {
6060
override fun onHandle(event: MotionEvent, sourceEvent: MotionEvent) {
6161
val currentState = state
62-
if (currentState == STATE_UNDETERMINED) {
62+
// we shouldn't stop intercepting events when there is an active handler already, which could happen when
63+
// adding a new pointer to the screen after a handler activates
64+
if (currentState == STATE_UNDETERMINED && (!shouldIntercept || orchestrator?.isAnyHandlerActive() != true)) {
6365
begin()
6466
shouldIntercept = false
6567
}

0 commit comments

Comments
 (0)