React-native: [Android] onPress not work in Android with overlapped components

Created on 23 Nov 2018  路  38Comments  路  Source: facebook/react-native

Environment

React Native Environment Info:
System:
OS: macOS High Sierra 10.13.6
CPU: x64 Intel(R) Core(TM) i5-5575R CPU @ 2.80GHz
Memory: 34.23 MB / 8.00 GB
Shell: 3.2.57 - /bin/bash
Binaries:
Node: 8.11.3 - /usr/local/bin/node
Yarn: 1.10.1 - /usr/local/bin/yarn
npm: 5.6.0 - /usr/local/bin/npm
Watchman: 4.9.0 - /usr/local/bin/watchman
SDKs:
iOS SDK:
Platforms: iOS 12.0, macOS 10.14, tvOS 12.0, watchOS 5.0
Android SDK:
Build Tools: 26.0.3, 27.0.3, 28.0.1
API Levels: 25, 26, 27, 28
IDEs:
Android Studio: 3.1 AI-173.4907809
Xcode: 10.0/10A255 - /usr/bin/xcodebuild
npmPackages:
react: ^16.6.0-alpha.8af6728 => 16.6.0
react-native: ^0.57.4 => 0.57.4
npmGlobalPackages:
react-native-cli: 2.0.1

Description

I have a TouchableOpacity overlapping on an Image component, but onPress can't be fired on Android. In iOS works fine. The problem occurs in real devices and emulators of both platforms.

Inspector on Android:
46508273_908184669375588_7967715880895250432_n

Inspector on Ios:
simulator screen shot - iphone 6 - 2018-11-23 at 12 54 35

Reproducible Demo

Overlap button:
<View style={{ elevation: 4 }} > <TouchableOpacity style={{ width: 50, height: 50, left: scale(200), top: scale(200), right: 0, bottom: 0 }} onPress={this.showLabel} > <View> <Animated.Image source={image} style={{ width: scale(35), height: scale(35) }} /> </View> </TouchableOpacity> </View>
Image:
<Image source={images[imgteste]} style={{ width: width, height: verticalScale(512), resizeMode: "contain" }} />

Bug Android Locked

Most helpful comment

I used the TouchableOpacity from react-native instead of react-native-gesture-handler and it worked. God knows why.

All 38 comments

Could you turn that reproducible demo into a snack or a snippet that is self contained? If you are not able to reproduce the issue using base views Image/Text and without specific scaling (react-native-size-matters?) add your specific context step by step until you start being able to reproduce it. It's easier to find an issue when debugging from simple to complex, instead of the other way around. Try to reduce noise for others looking at your issue, so that we can easily see if this is a bug in RN vs an implementation problem.

@bartolkaruza link to snack where I reproduce the same error using only react-native components. The same behavior repeat, works on ios but nothing happens in android.

I have a feeling this is related to the negative top value of the TouchableOpacity. On Android, a view is not able to render outside of its parent, but it is on iOS. Why do you need a negative absolute position?

Well, i keep testing on snack different ways of render my views and find this way, works on Android and iOS. I really don't understand why is working and the other way not. You know why @bartolkaruza ? And thank you for the help :)

When you give a view negative margin or negative position, it is basically outside of the parent view. Although the view renders in the case of iOS even if it is not inside the parent, on Android the view is simply clipped to the part that is in the parent.

viewbounds

This is true for display of the view and for touch handling.

I am using react-native 0.57 and the issue with clipping child seems to be solved, but onPress events still don't work... Is there any way I can make it work as it works on IOS? Or maybe 0.58 solves this problem?

When you give a view negative margin or negative position, it is basically outside of the parent view. Although the view renders in the case of iOS even if it is not inside the parent, on Android the view is simply clipped to the part that is in the parent.

viewbounds

This is true for display of the view and for touch handling.

@Zver64 Did you solve it? I've got the same problem... On Android i cannot click on the TouchableOpacity (for the portion outside of the parent view)

Just make the parent at least as big as the touch area.

Just make the parent at least as big as the touch area.

It's not the best solution. Especially in some cases. For example I have header and I want to have a small button with a dropdown that can overlap my header.

Try making the header absolute positioned, offset the content by the height of the header. Now you can make the header parent view any arbitrary height and it will draw over the content.

Try making the header absolute positioned, offset the content by the height of the header. Now you can make the header parent view any arbitrary height and it will draw over the content.

It draws over them, but the OnPress event of Top elements are not working...
Edit: My issue had something to do with React native router drawable sidebar component. Still found no fix.

I have the same issue - with any number of touchables absolutely positioned on top of each other, the lowest one in the dom is triggered regardless of elevation/zIndex on android.

We've had the same issue when wrapping a react-native-elements <Icon> with a TouchableOpacity. We were able to work around the issue by adding hitSlop={{ top: 10, left: 10, bottom: 10, right: 10 }} to the TouchableOpacity component, but this isn't a very satisfying fix. I would love to work out what the root cause of this issue is.

Is there any news on this issue? I am using the following style properties on a view, cotaining a TouchableWithoutFeedback:

buttonContainerStyle: {
position: 'absolute',
top: -45,
width: '100%',
height: '100%',
alignItems: 'flex-end',
paddingRight: 15,
},

It is working perfectly on iOS, but I cannot access the Touchable on android (onPress not working). I do require the View containing the Touchable be removed from its parent View. zIndex and hitSlop did not solve the issue.

Is there any news on this issue? I am using the following style properties on a view, cotaining a TouchableWithoutFeedback:

buttonContainerStyle: {
position: 'absolute',
top: -45,
width: '100%',
height: '100%',
alignItems: 'flex-end',
paddingRight: 15,
},

It is working perfectly on iOS, but I cannot access the Touchable on android (onPress not working). I do require the View containing the Touchable be removed from its parent View. zIndex and hitSlop did not solve the issue.

try to removed backgroudColor of the parent View. in my case , It works.

has this been solved? i am having similar issue with a the touch being triggered for the component below the overlapping component

When two components are overlapped the touchableopacity is not working in android. We are using react native version 0.57. Is there any solution for this.

I had same problems but I found
(https://github.com/kmagiera/react-native-gesture-handler)
I just import TouchableOpacity from it and it works as on IOS

I had same problems but I found
(https://github.com/kmagiera/react-native-gesture-handler)
I just import TouchableOpacity from it and it works as on IOS

Are you doing anything special? I'm trying to use TouchableOpacity and still have the same problem (overlapping an element causes the top element to be pressed instead), plus a lot of layout problems of its own.

I used the TouchableOpacity from react-native instead of react-native-gesture-handler and it worked. God knows why.

I had same problems but I found
(https://github.com/kmagiera/react-native-gesture-handler)
I just import TouchableOpacity from it and it works as on IOS

Are you doing anything special? I'm trying to use TouchableOpacity and still have the same problem (overlapping an element causes the top element to be pressed instead), plus a lot of layout problems of its own.

there are some really big problems on touchable in android, zIndex works differently in android, and also you should pay attention to "position" style, you can wrap it in a View or you can play with it, and you will find out some strange logic or at least it will work somehow :D. have a nice day

right!Only on Android. Is this the cause of position: 'absolute'? I also have the same issue

I had the same issue while working with KeyboardAvoidingView with position behavior. Changing it to padding fixed the issue.

I had same problems but I found
(https://github.com/kmagiera/react-native-gesture-handler)
I just import TouchableOpacity from it and it works as on IOS

Awesome! This worked for my.
I was trying to make a bigger button on a bottom tab-bar, but I got this problem with Android.
That TouchableOpacity solved the problem.
Thanks!

I had same problems but I found
(https://github.com/kmagiera/react-native-gesture-handler)
I just import TouchableOpacity from it and it works as on IOS

Awesome! This worked for my.
I was trying to make a bigger button on a bottom tab-bar, but I got this problem with Android.
That TouchableOpacity solved the problem.
Thanks!

Nice, but it only solved part of the problem, we could trigger the event in the foreground, but if the backdrop has Touchable as well. The application will receive both of them

I used the TouchableOpacity from react-native instead of react-native-gesture-handler and it worked. God knows why.

Life Saver! 馃憤

For me this was resolved after I forced a height/width on the parent TouchableOpacity which has to match (or more) the containing Image's height/width

Same problem here. TouchableOpacity does not fire onPress event on Android when it is inside a view with absolute positioning and parent view has a sibling. Please see snack: https://snack.expo.io/@sorinbogdan/working-maybe

Buttons 2, 3, 4 onPress is not called.
Please reopen issue.

TouchableOpacity works great!! However, if you don't need any UI feedback, just set the activeOpacity prop to 1.

<TouchableOpacity activeOpacity={1}/>

To not make mistake described by @mayooresan again, added ESLint rule which will show error when it happens: https://github.com/kmagiera/react-native-gesture-handler/issues/578#issuecomment-558599996

Facing an issue while using a position: absolute, TouchableOpacity onPress is not working in Android Only. it works fine in iOS.

I'm try to use the TouchableOpacity from react-native but i does not seems to work.

Having a similar issue (Android only, work in iOS) but I'm not using 'absolute' style tag anywhere. Using the TouchableOpacity from 'react-native-gesture-handler' seems to do the trick though (any way around using this?) .

I've had problems with Touchable's in RN for the longest time. Thanks to @Bonfs for filing this very meaningful bug which shows the kind of simple things that are so problematic with React Native still, and worst yet it's not acknowledged.

For those who want a mini version of what doesn't work, please try this:

import React from 'react';
import {Text, TouchableOpacity, View} from 'react-native';

export default class TouchableBug extends React.Component {
    static URL = '/demos/touchable-bugs';

    constructor(props) {
        super(props);
        this.state = {
        };
    }
    onPress = () => {
        console.log('Touchable pressed');
    };

    render() {
        return (
            <View style={{ width: 200, height: 200, borderWidth: 1, position: 'relative' }}>
                <TouchableOpacity onPress={this.onPress}>
                    <View style={{ height: 40, width: 50, borderWidth: 1 }}>
                        <Text>Hi - clickable</Text>
                    </View>
                </TouchableOpacity>

                <TouchableOpacity onPress={this.onPress}>
                    <View style={{ height: 40, width: 50, borderWidth: 1, marginLeft: 300 }}>
                        <Text>Hi - unclickable</Text>
                    </View>
                </TouchableOpacity>

                <TouchableOpacity onPress={this.onPress}>
                    <View style={{ height: 40, width: 50, borderWidth: 1, top: 100 }}>
                        <Text>Hi - unclickable</Text>
                    </View>
                </TouchableOpacity>

                <TouchableOpacity onPress={this.onPress}>
                    <View style={{ height: 40, width: 50, borderWidth: 1, position: 'absolute', right: 30 }}>
                        <Text>Hi - unclickable</Text>
                    </View>
                </TouchableOpacity>
            </View>
        );
    }
}

These are 4 touchables, anytime you set position: 'absolute' or move out of the parent visual boundary, they stop responding to touch events. zIndex does not fix the problem.

The only way to get your elements to respond to touch are the following:

  1. No absolute positioning. Don't even use the top, left, right, bottom attributes
  2. Touchable should not go outside the visual boundary of its parent / ancestor. If it is, add an extra parent element to the top of the tree and move the touchable out to be a direct descendent of it.

Will try to add more later.

@mayankmehtani @Christopher2K @mylittlemate @zeh : might wanna try this solution

On IoS i have no problem with TouchableOpacity inside any dialog (positioned) component.
On Android, with no positioning set it works fine (either library, react-native, or react-native-gesture-handler). BUT, on Android, put inside an Overlay, Modal, etc. and touchable stops functioning and have not found any solution so far. Disappointed to see this issue is marked closed.

This is definitely an issue. Strangely, in the previous version of Expo (35) which used RN 0.59 this wasn't an issue. And in fact it's not documented anywhere that this is an issue and I discovered in on accident.

Issue for me also. Why was this closed?

Btw, here's another hack in case you want to absolute position something on the top right of a view / image. Use flex direction row and specify negative margin.

<View style={{ display: 'flex', flexDirection: 'row', alignItems: 'flex-start' }}>
    <Image style={{ width: 20, height: 20 }} src={photo} />
    <View style={{ marginLeft: 40, marginTop: 20 }}>
        <TouchableOpacity onPress={() => {}}>
            <Text>Hi</Text>
        </TouchableOpacity>
    </View>
</View>

Needless to say all of this would not be required if React Native team would also prioritize Touchable bugs and fix them.

Was this page helpful?
0 / 5 - 0 ratings