Skip to content

Commit

Permalink
Improve multi-pointer behavior (with ScrollView) on Android (#2551)
Browse files Browse the repository at this point in the history
## 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))
  • Loading branch information
j-piasecki authored Sep 18, 2023
1 parent 95854bf commit 8c41b09
Show file tree
Hide file tree
Showing 3 changed files with 11 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,8 @@ class GestureHandlerOrchestrator(
return parent === wrapperView
}

fun isAnyHandlerActive() = gestureHandlers.any { it?.state == GestureHandler.STATE_ACTIVE }

/**
* Transforms an event in the coordinates of wrapperView into the coordinate space of the received view.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import android.view.View
import android.view.ViewConfiguration
import android.view.ViewGroup
import android.widget.ScrollView
import com.facebook.react.views.scroll.ReactScrollView
import com.facebook.react.views.swiperefresh.ReactSwipeRefreshLayout
import com.facebook.react.views.textinput.ReactEditText

Expand Down Expand Up @@ -75,6 +76,7 @@ class NativeViewGestureHandler : GestureHandler<NativeViewGestureHandler>() {
is NativeViewGestureHandlerHook -> this.hook = view
is ReactEditText -> this.hook = EditTextHook(this, view)
is ReactSwipeRefreshLayout -> this.hook = SwipeRefreshLayoutHook(this, view)
is ReactScrollView -> this.hook = ScrollViewHook()
}
}

Expand Down Expand Up @@ -248,4 +250,8 @@ class NativeViewGestureHandler : GestureHandler<NativeViewGestureHandler>() {
// oh well ¯\_(ツ)_/¯
}
}

private class ScrollViewHook : NativeViewGestureHandlerHook {
override fun shouldCancelRootViewGestureHandlerIfNecessary() = true
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,9 @@ class RNGestureHandlerRootHelper(private val context: ReactContext, wrappedView:
private inner class RootViewGestureHandler : GestureHandler<RootViewGestureHandler>() {
override fun onHandle(event: MotionEvent, sourceEvent: MotionEvent) {
val currentState = state
if (currentState == STATE_UNDETERMINED) {
// we shouldn't stop intercepting events when there is an active handler already, which could happen when
// adding a new pointer to the screen after a handler activates
if (currentState == STATE_UNDETERMINED && (!shouldIntercept || orchestrator?.isAnyHandlerActive() != true)) {
begin()
shouldIntercept = false
}
Expand Down

0 comments on commit 8c41b09

Please sign in to comment.