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

Fix Pressables not being tappable in PanResponder Views on Android #29533

Closed
wants to merge 4 commits into from

Conversation

hsource
Copy link
Contributor

@hsource hsource commented Jul 30, 2020

Summary

This fix makes Touchables and Pressables touchable, even if they're inside another view with a PanResponder/GestureResponder that handles touches.

I have an app that had a TouchableWithoutFeedback inside of a View with a PanResponder attached. The TouchableWithoutFeedback received touches as expected on iOS, but not on Android.

Debugging

  1. Added example to Pressable
  2. Compared behaviour on Android and iOS - identified the different events being emitted and set a debug in Pressability.js onResponderTerminationRequest
  3. Looked at the root event causing the Pressable to lose control of touches ("topTouchMove")
  4. Searched for that in Android code to identify the code path that was emitting it
  5. Added console.logs to the Android MotionEvent handler

Root cause

After debugging, I realized this is because on Android (tested on Pixel 2 device):

  1. As soon as a touch starts, a topTouchMove event is emitted from native code, even if the user doesn't move their finger at all
  2. The topTouchMove triggers the parent PanResponder
  3. This causes the touch on the Pressable to be canceled

This is unintuitive. Instead, the topTouchMove event should only be emitted when some actual move happens, like on iOS.

Fix

To fix this, I added code to only emit a touchMove event if the user's touch has moved at least 10 units away from where it started. This code was based on other code in Pressability that also use this threshold.

Changelog

[Android] [Fixed] - Fix Pressables not being tappable in PanResponder Views on Android

Test Plan

Added a test case in RNTesterApp's Pressable screen:

iOS Android (before) Android (after)
Pressable Android before Android after

@react-native-bot react-native-bot added Bug Platform: Android Android applications. labels Jul 30, 2020
@facebook-github-bot facebook-github-bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Jul 30, 2020
@analysis-bot
Copy link

analysis-bot commented Jul 30, 2020

Platform Engine Arch Size (bytes) Diff
android hermes arm64-v8a 6,769,543 759
android hermes armeabi-v7a 6,436,389 753
android hermes x86 7,157,686 760
android hermes x86_64 7,047,413 765
android jsc arm64-v8a 8,939,746 406
android jsc armeabi-v7a 8,598,999 418
android jsc x86 8,771,022 419
android jsc x86_64 9,346,382 435

Base commit: d48f7ba

@analysis-bot
Copy link

analysis-bot commented Jul 30, 2020

Platform Engine Arch Size (bytes) Diff
ios - universal n/a --

Base commit: d48f7ba

@hsource
Copy link
Contributor Author

hsource commented Aug 6, 2020

On further experimenting, it looks like iOS has the same behaviour as the existing (pre-PR) Android behaviour. I'm going to close this. I think we're supposed to just put this logic in the PanResponder

@hsource hsource closed this Aug 6, 2020
@seancheung
Copy link

seancheung commented Mar 18, 2021

Thanks for this!
I was experimenting every possible way to solve the "Pressable not working in PanResponder on Android" issue.

The solution is simple on js side:

PanResponder.create({
  onMoveShouldSetPanResponder: Platform.select({
    default: () => true,
    android: (e: GestureResponderEvent, state: PanResponderGestureState) =>
      Math.abs(state.dx) > 10 || Math.abs(state.dy) > 10
  })
})

@shimu12
Copy link

shimu12 commented Aug 26, 2021

Thanks for this!
I was experimenting every possible way to solve the "Pressable not working in PanResponder on Android" issue.

The solution is simple on js side:

PanResponder.create({
  onMoveShouldSetPanResponder: Platform.select({
    default: () => true,
    android: (e: GestureResponderEvent, state: PanResponderGestureState) =>
      Math.abs(state.dx) > 10 || Math.abs(state.dy) > 10
  })
})

thanks, it solves my problem

@unit-002
Copy link

PanResponder.create({
  onMoveShouldSetPanResponder: Platform.select({
    default: () => true,
    android: (e: GestureResponderEvent, state: PanResponderGestureState) =>
      Math.abs(state.dx) > 10 || Math.abs(state.dy) > 10
  })
})

I would like to try this because I am having the same issue with onPress inside Animated view using pan responder but on vscode I am having error saying

type annotation can only be used on typescript files

Currently I am using .js file. How to change this to js format? thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. Platform: Android Android applications.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants