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

Touchables translated into view are not pressable on android #28894

Closed
bdrobinson opened this issue May 14, 2020 · 26 comments
Closed

Touchables translated into view are not pressable on android #28894

bdrobinson opened this issue May 14, 2020 · 26 comments
Labels
Needs: Triage 🔍 Platform: Android Android applications. Stale There has been a lack of activity on this issue and it may be closed soon.

Comments

@bdrobinson
Copy link

bdrobinson commented May 14, 2020

Description

When a touchable is rendered offscreen (or possibly just outside the bounds of its parent) but moved into view using translateX/translateY, it cannot be pressed. This bug is only present on Android, and works fine on iOS.

There's been a few issues related to this that have been closed due to inactivity, so I'm opening a new one. They are:

(@AdamGerthel and @lehresman I'd be interested to know if you got any further debugging these issues?)

React Native version:

0.61.2

Steps To Reproduce

Provide a detailed list of steps that reproduce the issue.

  1. Open the snack https://snack.expo.io/@bdrobinson/android-offscreen-touchable-bug on an android device

Expected Results

When you press the left/right buttons, both should be expected to respond to your touches (you can run on iOS to verify this). However, on Android only the left one responds to touches, whereas the right doesn't.

Snack, code example, screenshot, or link to a repository:

https://snack.expo.io/@bdrobinson/android-offscreen-touchable-bug (copy+pasted from #26219)

@AdamGerthel
Copy link

(@AdamGerthel and @lehresman I'd be interested to know if you got any further debugging these issues?)

I've used workarounds to solve it in the different cases I've had this issue:

@skay97
Copy link

skay97 commented Jun 17, 2020

@AdamGerthel I am facing a similar issue and wanted to see if you could provide some more clarification. By putting an invisible view behind the touchable surface, do you mean you wrapped the touchable opacity in a view that had no styles?
In my case, I am translating my FAB, however due to the transformations, I can no longer click the opacity. Due to which, I am having to use the top and bottom properties instead. I would have no issue utilizing those properties, it's just that I also can't use native driver without transforming.

@AdamGerthel
Copy link

@skay97 My code is too obscure for it to make immediate sense and it would take me too much time to rewrite it as a re-usable sample. Basically I first render this view: <View style={{ height: x }} /> where x is the height of the view that is un-clickable on Android. I use onLayout on the Animated View to get the height of the view, and then pass it to the other view (which is placed "behind" the animated one). Exactly how to use these values depend on what you're trying to achieve, which transforms you're using etc.

@fabOnReact
Copy link
Contributor

This line does not take in consideration the transformation effect. It calculates the number of child present on the screen before applying the transformation (#27333 (comment))

private static View findTouchTargetView(float[] eventCoords, ViewGroup viewGroup) {
int childrenCount = viewGroup.getChildCount();

The number of child i is used to loop around each children of the viewGroup and check if the touch was done inside that View, but as the value childIndex was calculated before the transformation, the button View is not included in this loop

for (int i = childrenCount - 1; i >= 0; i--) {
int childIndex =
zIndexedViewGroup != null ? zIndexedViewGroup.getZIndexMappedChildIndex(i) : i;
View child = viewGroup.getChildAt(childIndex);

this is the logic checking that click x, y was performed inside the Button View

if (isTransformedTouchPointInView(
eventCoords[0], eventCoords[1], viewGroup, child, childPoint)) {
// If it is contained within the child View, the childPoint value will contain the view
// coordinates relative to the child
// We need to store the existing X,Y for the viewGroup away as it is possible this child
// will not actually be the target and so we restore them if not
float restoreX = eventCoords[0];
float restoreY = eventCoords[1];
eventCoords[0] = childPoint.x;
eventCoords[1] = childPoint.y;
View targetView = findTouchTargetViewWithPointerEvents(eventCoords, child);

I need to apply the transformation before calculation the number of childrens

I'll try to prepare a pull request tomorrow

Thanks a lot
I wish you a good day
Fabrizio Bertoglio

@fabOnReact
Copy link
Contributor

fabOnReact commented Jul 14, 2020

private static void setTransformProperty(@NonNull View view, ReadableArray transforms) {

The transformation is not yet applied and getChildCount will return 1. The loop runs and does not check that onTouchEvent x, y coordinates are in the button area.

int childrenCount = viewGroup.getChildCount();
// Consider z-index when determining the touch target.
ReactZIndexedViewGroup zIndexedViewGroup =
viewGroup instanceof ReactZIndexedViewGroup ? (ReactZIndexedViewGroup) viewGroup : null;
for (int i = childrenCount - 1; i >= 0; i--) {

I can detect that the child child has been transformed like this

        Matrix childMatrix = child.getMatrix();
        Log.w("TESTING::TouchTargetHelper", "childMatrix.isIdentity(): " + childMatrix.isIdentity());
        // Logs false for ViewGroup id 0x17 (23) - a transformation has been applied and not taked in consideration

image

calling setTranslationX and re-executing the process could fix this issue, I'll be testing this afternoon.

another option is triggering with a callback this process after transformation is applied, instead of duplicating the logic

@fabOnReact
Copy link
Contributor

fabOnReact commented Jul 14, 2020

My opinion is that issue #28894 and #27333 created by @bdrobinson and @AdamGerthel should be closed.

Both issues do not include acceptable reproducible examples.

@AdamGerthel in #27333 should have just applied { height: integer } as his container has height = 0. The TouchableOpacity inside a container with height = 0 can not be clicked.

@bdrobinson you can just fix #28894 by specifying the correct width of the View you want to transform. The View is not clickable because you apply width: Dimensions.get('window').width() and then shift the View 50% to the left. After transform your View Width is 50% of the Screen.

Unluckily same and probably a duplicate of #27333 (comment)

const styles = StyleSheet.create({
  outerContainer: {
  },
  innerContainer: {
    flexDirection: 'row',
    width:1800,
    transform: [{translateX: -200}]
  },
});

If you want the ViewGroup to be clickable with a transform, you need to make sure it has the correct width. I invested a lot of time in both issues to find this mistake, so I would be thankful if you could close both issues.

Thanks
I wish you a good day
Fabrizio Bertoglio

@bdrobinson
Copy link
Author

Thanks for looking into it so closely @fabriziobertoglio1987! You clearly understand the problem space a lot better than I do so I'm happy to defer to your opinion to make sure that this issue doesn't cause unnecessary noise for maintainers.

However, I would question whether this is indeed an "invalid example" since it works on iOS and fails on Android?

@stale
Copy link

stale bot commented Oct 12, 2020

Hey there, it looks like there has been no activity on this issue recently. Has the issue been fixed, or does it still require the community's attention? This issue may be closed if no further activity occurs. You may also label this issue as a "Discussion" or add it to the "Backlog" and I will leave it open. Thank you for your contributions.

@stale stale bot added the Stale There has been a lack of activity on this issue and it may be closed soon. label Oct 12, 2020
@chaselal
Copy link

This is still an issue. Use translateX to position a view outside the viewport -> animate the view into the viewport using translateX -> the view does not receive touch. Touches pass through to the views behind the animated-in view.

@stale stale bot removed the Stale There has been a lack of activity on this issue and it may be closed soon. label Nov 11, 2020
@chaselal
Copy link

One use case is:

  • show a list of items
  • touch an item
  • open a detail view that slides in from the right edge of the screen, with an animation such that the view slides into view instead of just appearing
  • the view does not receive touch on Android

@AdamGerthel
Copy link

AdamGerthel commented Nov 12, 2020

I agree that this is still an issue, and I don't agree with what @fabriziobertoglio1987 is saying either. The fact that a view is interpreted as having height: 0 does not mean that I assigned height: 0 to it (which I clearly haven't). My sample works in both iOS and Web, but not Android, and I'm following very basic CSS principles, which is what is expected when using React Native. The fault lies with RN/Android, not my code.

Basically, my take on it is that the height of the View should be calculated by the native Android code rather than being calculated by hand in JS. Assigning a fixed height prevents dynamic content from being placed in the view. Doing that is what I consider to be a bad workaround for an obvious bug. If position: 'absolute', bottom: 0 causes height: 0 on Android, but not in Web or iOS, then I think the fault lies with RN/Android.

See #27333 (comment) as well.

@hellogerard
Copy link

I tried all the ideas mentioned above and nothing worked.

...I found replacement touchables from react-native-gesture-handler and it worked right away. Drop-in replacement. Just replace your react-native imports with:

import {
  TouchableNativeFeedback,
  TouchableHighlight,
  TouchableOpacity,
  TouchableWithoutFeedback,
} from 'react-native-gesture-handler';

@AnnaSearl
Copy link

I get the same issue, I want to make a dropdown,in IOS everything is OK, but android is not work, Who can help me...

@mrousavy
Copy link
Contributor

mrousavy commented Dec 3, 2020

I'm experiencing the same issue. It's a real bummer, because I've created a custom horizontal swiper (carousel), and <Pressable>/<TouchableWithoutFeedback> components are not pressable on android at all.
The only workaround I've found so far is by using the TouchableWithoutFeedback export from react-native-gesture-handler since that does not use the JS touch responder system, but I'd feel pretty uncomfortable using that workaround, because I'd have to replace all my app's screens' touchables and I can't use the Pressable API anymore.

@fabriziobertoglio1987 you mentioned you'd prepare a pull request - did you find a native fix for this?

EDIT: Just read the comment about providing valid widths - do you mean the root view should have a width of SCREEN_WIDTH * numberOfHorizontalPages? That's not a good solution. As many users (including me) rely on the removeClippedSubviews property, and that requires views to be offscreen when the parent has overflow: 'hidden', which is redundant if the parent container view is as wide as the whole carousel. (= bad performance)
The parent container should always have a viewport of exactly the phone's dimensions and only the horizontal carousel gets translated on the X axis, so RCTView can optimize offscreen views away. Otherwise those views would have to be rendered, even if they're not visible. Imagine the Snapchat homescreen - you don't want to have all 5 screens rendered at the same time, only the screen(s) that are within the viewport should get rendered.

@fabOnReact
Copy link
Contributor

fabOnReact commented Dec 3, 2020

@mrousavy thanks for the reply

If I still remember well my research explained in #28894 (comment) made me conclude that ReactAndroid will restrict touch events to parent view area available, so for example if you transform the parent view or set height/width = 0, then you can not touch the TouchableWithoutFeedback

This is included in the ReactNative Touchable docs as I explained in #27333 (comment)

regarding TouchableWithoutFeedback from react-native-gesture-handler, they probably don't use the same Android API included above... so that may be the reason you don't have this limitation..

Probably reading their sourcecode may help us understand how to remove this limitation, but I believe the change will be complex

I may be wrong as I did not work for long time on this issue, once I will have more free time I will have a further look at the issue. Thanks a lot.

@paulduthoit
Copy link

Hi ! Has anyone found a solution while waiting for the problem to be resolved ? Thanks.

@lelukas
Copy link

lelukas commented Sep 15, 2021

Also, don't if anyone noticed it but the same happens with FlatLists

@stale
Copy link

stale bot commented Jan 9, 2022

Hey there, it looks like there has been no activity on this issue recently. Has the issue been fixed, or does it still require the community's attention? This issue may be closed if no further activity occurs. You may also label this issue as a "Discussion" or add it to the "Backlog" and I will leave it open. Thank you for your contributions.

@stale stale bot added the Stale There has been a lack of activity on this issue and it may be closed soon. label Jan 9, 2022
@TomasSestak
Copy link

Still valid issue i think

@hotaryuzaki
Copy link

I tried all the ideas mentioned above and nothing worked.

...I found replacement touchables from react-native-gesture-handler and it worked right away. Drop-in replacement. Just replace your react-native imports with:

import {
  TouchableNativeFeedback,
  TouchableHighlight,
  TouchableOpacity,
  TouchableWithoutFeedback,
} from 'react-native-gesture-handler';

yes its works.
but its has other issue, if underneath the icon has onpress so it will fire also.

BamMironov added a commit to ChildMindInstitute/mindlogger-app-refactor that referenced this issue Apr 25, 2023
There is an open issue on React Native side: facebook/react-native#28894 that cannot be fixed now. That is why it has been decided to remove animations that use translateX styles.

https://mindlogger.atlassian.net/browse/M2-1500
@github-actions github-actions bot removed the Stale There has been a lack of activity on this issue and it may be closed soon. label Apr 28, 2023
@github-actions
Copy link

This issue is stale because it has been open 180 days with no activity. Remove stale label or comment or this will be closed in 7 days.

@github-actions github-actions bot added the Stale There has been a lack of activity on this issue and it may be closed soon. label Oct 26, 2023
Copy link

github-actions bot commented Nov 2, 2023

This issue was closed because it has been stalled for 7 days with no activity.

@github-actions github-actions bot closed this as completed Nov 2, 2023
@fabOnReact fabOnReact reopened this Dec 30, 2023
@github-actions github-actions bot removed the Stale There has been a lack of activity on this issue and it may be closed soon. label Dec 31, 2023
@fabOnReact
Copy link
Contributor

I'm organizing my next tasks. Are you still interested in adopting a fix for this issue? Or have you already implemented a workaround? If you are interested, I will work on a solution. Thanks

@hotaryuzaki
Copy link

I'm organizing my next tasks. Are you still interested in adopting a fix for this issue? Or have you already implemented a workaround? If you are interested, I will work on a solution. Thanks

I think this issue is an important one, because it will be raised to anyone who has a bigger icon in the middle of the bottom tab bar

@react-native-bot
Copy link
Collaborator

This issue is stale because it has been open 180 days with no activity. Remove stale label or comment or this will be closed in 7 days.

@react-native-bot react-native-bot added the Stale There has been a lack of activity on this issue and it may be closed soon. label Jul 27, 2024
@react-native-bot
Copy link
Collaborator

This issue was closed because it has been stalled for 7 days with no activity.

@react-native-bot react-native-bot closed this as not planned Won't fix, can't repro, duplicate, stale Aug 3, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Needs: Triage 🔍 Platform: Android Android applications. Stale There has been a lack of activity on this issue and it may be closed soon.
Projects
None yet
Development

No branches or pull requests