Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Android: Children don't properly cancel a press when pager starts scrolling #954

Open
gpp-0 opened this issue Jan 7, 2025 · 0 comments
Open

Comments

@gpp-0
Copy link

gpp-0 commented Jan 7, 2025

Environment

react-native: 0.76.5
react-native-pager-view: 6.6.1
Tested on Pixel 8

Description

I believe that when the user swipes to scroll the pager and the pager takes control of the gesture, any children of the pager that were listening to the gesture should be sent a touchcancel event, and they should stop receiving subsequent events. This is for example how it works with FlatList.
Currently PagerView does not work like this. When a child component is pressed in and then a pager scroll is performed, the child receives a press out event but it never receives a cancel event, and instead it keeps listening to touch events, which leads to faulty behavior.
I have confirmed that in a native Android app ViewPager2 works as expected, an ACTION_CANCEL event is sent to the child, and the pager takes ownership of the gesture when a scroll begins.

Reproducible Demo

Here is a snack demo, although this is easier to see on a real device
https://snack.expo.dev/@the_cetus/pagerview-touch?platform=android

Steps to reproduce:

  • Open your logs
  • Staying on the first page, scroll up and down the list of buttons. You should see that whatever button you started the drag from receives a few touch events, but when you drag far enough eventually it receives a cancel event and it stops responding to further events, as expected.
  • Now scroll horizontally to engage the pager. Keeping your finger down, you will see that the button keeps receiving move events indefinitely, even after it's received a press out and it looks like it should be ignoring events.
  • Now for an actually visible bug, it's possible to do a very fast, very brief horizontal swipe in such a way that the pager scrolls to the next page, and a button also gets pressed. This is more easily done on a real device, but it requires precise timing so it doesn't happen often.
video2.mp4

Here is what a typical log looks like when this happens

{"timestamp": 55806165, "type": "PRESS IN", "x": 115.992, "y": 105.986}
{"timestamp": 55806165, "type": "START", "x": 115.992, "y": 105.986}
{"timestamp": 55806175, "type": "MOVE", "x": 129.065, "y": 105.986}
{"timestamp": 55806206, "type": "MOVE", "x": 161.656, "y": 105.986}
{"timestamp": 55806206, "type": "PRESSED", "x": 148.989, "y": 105.986}
{"timestamp": 55806206, "type": "END", "x": 148.989, "y": 105.986}
{"timestamp": 55806206, "type": "PRESS OUT", "x": 148.989, "y": 105.986}

touchend happens at the same moment as the touchmove where dx is big enough that it's supposed to cancel the press, and because it doesn't get cancelled the press goes through.

For comparison, here's what the logs look like when the same thing is attempted for the vertical FlatList:

{"timestamp": 56652026, "type": "PRESS IN", "x": 218.982, "y": 10.294}
{"timestamp": 56465045, "type": "START", "x": 218.982, "y": 10.294}
{"timestamp": 56465059, "type": "MOVE", "x": 222.219, "y": 16.065}
{"timestamp": 56465075, "type": "MOVE", "x": 226.821, "y": 31.488}
{"timestamp": 56465075, "type": "CANCEL", "x": 226.821, "y": 31.488}
{"timestamp": 56465075, "type": "PRESS OUT", "x": 226.821, "y": 31.488}

Once dy is big enough, a touchcancel is fired and it's impossible to get a press.

Maybe this specific example is a bit contrived, but I suspect that this bug is the source of other, more visible issues when the complexity is higher (especially when react-native-gesture-handler is involved?)

By the way, the issue is not with the nested scrollable, the same happens when the child view only contains a single button (replace the ButtonLists with TogglingButtons in the sample). I also tried completely removing any trace of the NestedScrollableHost class in case there was something wrong with its interceptTouchEvent function, but that changed nothing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant