[Android] Fix taps triggering while swiping (#7459)

* [Android] Fix taps triggering while swiping

* Revert "[Android] Try to disambiguate taps from swipes (#7448)"

This reverts commit 96054f4add.

* Update patch to match callstack/react-native-pager-view#961

* Make it symmetrical

---------

Co-authored-by: Dan Abramov <dan.abramov@gmail.com>
This commit is contained in:
gpp
2025-01-17 04:02:59 +02:00
committed by GitHub
parent 9e3f2f4374
commit c6935d241e
2 changed files with 74 additions and 40 deletions
@@ -0,0 +1,72 @@
diff --git a/node_modules/react-native-pager-view/android/build/tmp/kotlin-classes/debug/com/reactnativepagerview/NestedScrollableHost.class b/node_modules/react-native-pager-view/android/build/tmp/kotlin-classes/debug/com/reactnativepagerview/NestedScrollableHost.class
new file mode 100644
index 0000000..b64fccc
Binary files /dev/null and b/node_modules/react-native-pager-view/android/build/tmp/kotlin-classes/debug/com/reactnativepagerview/NestedScrollableHost.class differ
diff --git a/node_modules/react-native-pager-view/android/src/main/java/com/reactnativepagerview/NestedScrollableHost.kt b/node_modules/react-native-pager-view/android/src/main/java/com/reactnativepagerview/NestedScrollableHost.kt
index 91d9946..87b58d0 100644
--- a/node_modules/react-native-pager-view/android/src/main/java/com/reactnativepagerview/NestedScrollableHost.kt
+++ b/node_modules/react-native-pager-view/android/src/main/java/com/reactnativepagerview/NestedScrollableHost.kt
@@ -8,6 +8,7 @@ import android.view.ViewConfiguration
import android.widget.FrameLayout
import androidx.viewpager2.widget.ViewPager2
import androidx.viewpager2.widget.ViewPager2.ORIENTATION_HORIZONTAL
+import com.facebook.react.uimanager.events.NativeGestureUtil
import kotlin.math.absoluteValue
import kotlin.math.sign
@@ -27,6 +28,7 @@ class NestedScrollableHost : FrameLayout {
private var touchSlop = 0
private var initialX = 0f
private var initialY = 0f
+ private var nativeGestureStarted: Boolean = false
private val parentViewPager: ViewPager2?
get() {
var v: View? = parent as? View
@@ -57,17 +59,14 @@ class NestedScrollableHost : FrameLayout {
}
private fun handleInterceptTouchEvent(e: MotionEvent) {
- val orientation = parentViewPager?.orientation ?: return
-
- // Early return if child can't scroll in same direction as parent
- if (!canChildScroll(orientation, -1f) && !canChildScroll(orientation, 1f)) {
- return
- }
+ val orientation = parentViewPager?.orientation
if (e.action == MotionEvent.ACTION_DOWN) {
initialX = e.x
initialY = e.y
- parent.requestDisallowInterceptTouchEvent(true)
+ if (orientation != null) {
+ parent.requestDisallowInterceptTouchEvent(true)
+ }
} else if (e.action == MotionEvent.ACTION_MOVE) {
val dx = e.x - initialX
val dy = e.y - initialY
@@ -78,6 +77,10 @@ class NestedScrollableHost : FrameLayout {
val scaledDy = dy.absoluteValue * if (isVpHorizontal) 1f else .5f
if (scaledDx > touchSlop || scaledDy > touchSlop) {
+ NativeGestureUtil.notifyNativeGestureStarted(this, e)
+ nativeGestureStarted = true
+
+ if (orientation == null) return
if (isVpHorizontal == (scaledDy > scaledDx)) {
// Gesture is perpendicular, allow all parents to intercept
parent.requestDisallowInterceptTouchEvent(false)
@@ -94,4 +97,14 @@ class NestedScrollableHost : FrameLayout {
}
}
}
+
+ override fun onTouchEvent(e: MotionEvent): Boolean {
+ if (e.actionMasked == MotionEvent.ACTION_UP) {
+ if (nativeGestureStarted) {
+ NativeGestureUtil.notifyNativeGestureEnded(this, e)
+ nativeGestureStarted = false
+ }
+ }
+ return super.onTouchEvent(e)
+ }
}
+2 -40
View File
@@ -1,4 +1,4 @@
import React, {Children, forwardRef, useCallback, useContext} from 'react'
import React, {forwardRef, useCallback, useContext} from 'react'
import {View} from 'react-native'
import {DrawerGestureContext} from 'react-native-drawer-layout'
import {Gesture, GestureDetector} from 'react-native-gesture-handler'
@@ -17,7 +17,6 @@ import Animated, {
} from 'react-native-reanimated'
import {useFocusEffect} from '@react-navigation/native'
import {isAndroid} from '#/platform/detection'
import {useSetDrawerSwipeDisabled} from '#/state/shell'
import {atoms as a, native} from '#/alf'
@@ -149,11 +148,7 @@ export const Pager = forwardRef<PagerRef, React.PropsWithChildren<Props>>(
style={[a.flex_1]}
initialPage={initialPage}
onPageScroll={handlePageScroll}>
{isAndroid
? Children.map(children, child => (
<CaptureSwipesAndroid>{child}</CaptureSwipesAndroid>
))
: children}
{children}
</AnimatedPagerView>
</GestureDetector>
</View>
@@ -161,39 +156,6 @@ export const Pager = forwardRef<PagerRef, React.PropsWithChildren<Props>>(
},
)
// HACK.
// This works around https://github.com/callstack/react-native-pager-view/issues/960.
// It appears that the Pressables inside the pager get confused if there's enough work
// happening on the JS thread, and mistakingly interpret a pager swipe as a tap.
// We can prevent this by stealing all horizontal movements from the tree inside.
function CaptureSwipesAndroid({children}: {children: React.ReactNode}) {
const lastTouchStart = React.useRef<{x: number; y: number} | null>(null)
return (
<View
onTouchStart={e => {
lastTouchStart.current = {
x: e.nativeEvent.pageX,
y: e.nativeEvent.pageY,
}
}}
onMoveShouldSetResponderCapture={e => {
const coords = lastTouchStart.current
if (!coords) {
return false
}
const dx = Math.abs(e.nativeEvent.pageX - coords.x)
if (dx > 0) {
// This is a horizontal movement and will result in a swipe.
// Prevent pager children from receiving this touch.
return true
}
return false
}}>
{children}
</View>
)
}
function usePagerHandlers(
handlers: {
onPageScroll: (e: PagerViewOnPageScrollEventData) => void