Repository navigation
Commit 0f8b2ba
authored
[Android] Cancel detector gestures when a native view above them takes over the touch (#4582)
## Description
On Android, a v3 gesture below a native view that takes over the touch
keeps running and activates on lift. That native view might be a
`ScrollView`/`FlatList` that starts dragging, or one that catches a
fling on DOWN. So scrolling from a list row, or tapping a row to stop a
fling, fires the row's tap. The list also loses its fling, because the
root starts intercepting when the tap activates.
Fixes #4581.
The scroller's `requestDisallowInterceptTouchEvent` only bubbles up, and
the deferred sweep from #4441 only handles buttons. This PR extends that
sweep to detector gestures:
- The detector remembers the down time of the last DOWN it got through
native dispatch.
- When a native view takes over, pending detector gestures (native ones
included) are cancelled in two cases: the takeover happened after DOWN,
or their detector never got the DOWN (a fling catch).
- Eager requests on DOWN that still let the event through, like a nested
pager (#2383) or a `TextInput`, leave gestures that got the DOWN alone.
That includes gestures held by another finger.
- Active gestures are left alone, and buttons keep their own check.
Like buttons since #4441 (and legacy handlers), this is stricter than
`main` in two cases:
- A pending gesture under one finger is cancelled when another finger
starts scrolling. The root doesn't know which view took over or which
pointers it took, so the sweep can't be narrower.
- A touch in a gesture's `hitSlop`, outside its detector, is cancelled
if the view under it requests on DOWN (e.g. a `TextInput`).
Known limits, same as before:
- If an ancestor already requested disallow-intercept on DOWN, later
requests from below stop there. So swiping a nested pager that started
on a tap still fires the tap. Buttons have the same limit.
- An ancestor taking over on a later pointer's POINTER_DOWN isn't caught
for gestures on earlier pointers. That's the same trade-off as in #4441,
and none of the stock scrollers I checked do it.
## Test plan
I compared `main` with this branch on an Android 16 emulator using the
`expo-example` debug build, 5 tries each, driving the two-finger cases
through the emulator console. The screen is the [repro
gist](https://gist.github.com/JacquesLeupin/0a30f72fcc02c82144f4b374e55d19a3)
plus the multitouch repro from review.
| Scenario | `main` | This PR |
| --- | --- | --- |
| Drag-scroll from a list row, lift | Phantom tap 4/5, list flings 1/5 |
No tap, list flings 5/5 |
| Tap a row to stop a fling | Phantom tap 5/5 | No tap |
| Tap or swipe a row at rest | Works 5/5 | Works 5/5 |
| Tap or long press inside nested `PagerView`s | Works 5/5 | Works 5/5 |
| Swipe the inner pager starting on a tap | Phantom tap 5/5 | Phantom
tap 5/5 (known limit) |
| Hold a tap; a second finger taps a nested pager or a `TextInput`, or
stops a list fling | Tap fires 5/5 | Tap fires 5/5 |
| Hold a tap; a second finger drags another list | Tap fires 5/5 |
Cancelled (stricter) |
| `useNativeGesture` on a `Text` row: drag-scroll / tap to stop a fling
| Activates 3/5 / 5/5 | No activation |
| Gesture Handler `ScrollView` carousel catches a fling, then sideways
drift | Takes over 5/5 | List keeps scrolling |
| `RectButton` touched once, then taps to stop a fling | Presses 5/5 |
No press |
| `Touchable` / `Pressable` rows | No change | No change |
| Tap in a `hitSlop` over a plain `View` / over a `TextInput` | Fires /
fires | Fires / cancelled (stricter) |
`yarn lint:android` passes. We hit this in a transaction list in our app
(React Native 0.87.1), where we carry an earlier version of this fix as
a patch on 3.3.0.1 parent cba84fe commit 0f8b2ba
3 files changed
Lines changed: 35 additions & 7 deletions
File tree
- packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler
- core
- react
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
390 | 390 | | |
391 | 391 | | |
392 | 392 | | |
393 | | - | |
| 393 | + | |
| 394 | + | |
| 395 | + | |
394 | 396 | | |
395 | | - | |
396 | | - | |
| 397 | + | |
| 398 | + | |
| 399 | + | |
| 400 | + | |
| 401 | + | |
| 402 | + | |
| 403 | + | |
397 | 404 | | |
398 | 405 | | |
399 | 406 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | 3 | | |
| 4 | + | |
4 | 5 | | |
5 | 6 | | |
6 | 7 | | |
| |||
22 | 23 | | |
23 | 24 | | |
24 | 25 | | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
25 | 30 | | |
26 | 31 | | |
27 | 32 | | |
| |||
44 | 49 | | |
45 | 50 | | |
46 | 51 | | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
47 | 68 | | |
48 | 69 | | |
49 | 70 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
143 | 143 | | |
144 | 144 | | |
145 | 145 | | |
146 | | - | |
147 | | - | |
148 | | - | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
149 | 149 | | |
150 | 150 | | |
151 | 151 | | |
| |||
156 | 156 | | |
157 | 157 | | |
158 | 158 | | |
159 | | - | |
| 159 | + | |
160 | 160 | | |
161 | 161 | | |
162 | 162 | | |
| |||
0 commit comments