facebook / react-native

A framework for building native applications using React
https://reactnative.dev
MIT License
119.54k stars 24.37k forks source link

Touch events in nested absolute positioned views not working on Android #27333

Open AdamGerthel opened 5 years ago

AdamGerthel commented 5 years ago

When nesting a touchable surface in absolute positioned views, Android will display the children correctly according to style rules, but not handle the touch event. It works as expected on iOS.

React Native version: 0.59

Steps To Reproduce

Create two views, both absolutely positioned (one as child to the other) and place a button or (whatever that uses a touch event) in the child view. Attempt to use the button.

Describe what you expected to happen: I would expect the touchable surfaces to work (onPress events etc.)

Snack, code example, screenshot, or link to a repository: https://snack.expo.io/@insats/android-positioning-bug

wsamipw commented 5 years ago

Facing same Problem.

emzet93 commented 5 years ago

+1. Same issue only on Android

kailask commented 5 years ago

Also been struggling with this problem for a while now. Problem occurs even after setting zIndex and if parent view is not absolute positioned.

hirbod commented 4 years ago

Can confirm this too

AdamGerthel commented 4 years ago

Possible workaround: Use pointerEvents attribute set to box-none on the absolute positioned view. Worked in my use-case (not in my snack though 🤷🏼‍♂️).

chukwuezi commented 4 years ago

Make sure you set the height and the width in the parent view. This will fix the Android issue.

smisaacs commented 4 years ago

same issue.

lucas-pessoa commented 4 years ago

+1 same issue happening only on Android

tagraha commented 4 years ago

same issue. happening on android only

ManuEpi commented 4 years ago

Same issue only on android too

HunteRoi commented 4 years ago

Same here ✋

mathbalduino commented 4 years ago

Same here

yastrebovb commented 4 years ago

Same issue, android only, any solutions?

fabOnReact commented 4 years ago

https://github.com/facebook/react-native/blob/9a532edaaf6b80b10ce73eb8815e7f75a8c94bec/ReactAndroid/src/main/java/com/facebook/react/ReactRootView.java#L59-L70

RootView delegates touch event to childs

https://github.com/facebook/react-native/blob/9a532edaaf6b80b10ce73eb8815e7f75a8c94bec/ReactAndroid/src/main/java/com/facebook/react/ReactRootView.java#L201-L208

ReactViewGroup get the touch event from the RootView and should pass it back to javascript TouchableOpacity

https://github.com/facebook/react-native/blob/9a532edaaf6b80b10ce73eb8815e7f75a8c94bec/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewGroup.java#L220-L232

I believe JS is invoked with this function https://github.com/facebook/react-native/blob/9a532edaaf6b80b10ce73eb8815e7f75a8c94bec/ReactAndroid/src/main/java/com/facebook/react/ReactRootView.java#L263-L281

https://github.com/facebook/react-native/blob/9a532edaaf6b80b10ce73eb8815e7f75a8c94bec/ReactAndroid/src/main/java/com/facebook/react/uimanager/JSTouchDispatcher.java#L53-L66

I'll keep working on this tomorrow

sjonchhe commented 4 years ago

Has anybody found anything on this??

fabOnReact commented 4 years ago

I know the solution for this. I'm preparing a pull request

reactTargetView resolves to ReactViewGroup instance with id 0x3 (The Red View Group).

https://github.com/facebook/react-native/blob/120ff7ccdebebdf814e478259ba1e4a8ef6f513e/ReactAndroid/src/main/java/com/facebook/react/uimanager/TouchTargetHelper.java#L85

Then the only children of that ViewGroup is the red TouchableOpacity

https://github.com/facebook/react-native/blob/120ff7ccdebebdf814e478259ba1e4a8ef6f513e/ReactAndroid/src/main/java/com/facebook/react/uimanager/TouchTargetHelper.java#L90

fabOnReact commented 4 years ago

I believe your issue is based on a WorkAround similar to negative margin https://github.com/facebook/react-native/issues/25441#issuecomment-635787940, which should not be used with Android Design.

Related issues https://github.com/facebook/react-native/issues/25441 https://github.com/facebook/react-native/issues/28694

The view you created has the following properties

<View style={{
  position: 'absolute',
  bottom: 0,
  right: 0,
  left: 0,
  zIndex: 2
}}>
    { touchableOpacity }
</View>

This breaks the following Java logic

https://github.com/facebook/react-native/blob/0060b5de559cd9c785a2a2a6c66f58088fea4dd2/ReactAndroid/src/main/java/com/facebook/react/uimanager/TouchTargetHelper.java#L153

As the view has height = 0 and bottom = 0. The getTop returns 2022 (the bottom of my android screen).

For this reason the touchY which is positioned at { y: 2009 } is not included in that view container and touch is passed to the red component.. LONG STORY SHORT Make sure you absolute container contains your Touchable Opacity as explained in hitSlop

I believe the functionality you expect does not exist in react native. I could consider working on it, but seems to me more like a consistent feature request.

AdamGerthel commented 4 years ago

@fabriziobertoglio1987 Even if you're right, I'm going to quote this comment from the issue you linked to and say this:

The documentation says we can use CSS with React Native (except for the names changing to camelCase).

So either the documentation is wrong and CSS is not supported, or this issue is valid and negative margins should be supported as in CSS. In any case I found this issue after many hours of debugging following the documentation so others will find this issue as well unless something is changed.

If position: 'bottom' without a specified position: 'top' value is not allowed, then this should be either fixed or documented imho.

Pat-Gekoski commented 3 years ago

I'm having the same problem today......I guess not too much has changed in the past 2 years......

LitileXueZha commented 3 years ago

I don't know my solution is the answer or not, here it is: Make sure there are not elements after your absoluted view.

For example:

function App() {
    return (
        <View>
            <AbsolutedView>
                <Touchable>...</Touchable>
            </AbsolutedView>
            <View></View> // <== One view after the absoluted view,
                                 it may break your touch events.

            /**
             * Change their orders will work.
             */
            <View></View>
            <AbsolutedView></AbsolutedView> // <== Place it at the last.
        </View>
    );
}
Syphonjr commented 2 years ago

Same problem still.

My touchable has an absolute wrapper, but touchevents just pass through everything.

ghiculescualexandru commented 2 years ago

Same problem here. I cannot work around this by adding specific widths or heights. Any updates on this?

Syphonjr commented 2 years ago

I worked around it by putting the absolute view in the parent view that actually has a width/height. And then use onlayout to position it

pedrobadm7 commented 2 years ago

Same problem here, thouch doesn't work on android application (everything ok on iOS)

ghiculescualexandru commented 2 years ago

I worked it around by putting a height on the parent view and updating the height dynamically. Still, it's just an workaround. iOS works fine.

pedrobadm7 commented 2 years ago

I worked it around by putting a height on the parent view and updating the height dynamically. Still, it's just an workaround. iOS works fine.

I realy don't know why any touch events are not working for me on android, I already tried all workarounds, but nothing helped me. It's very weird once is everything ok on iOS

W-Lawless commented 2 years ago

I am experiencing this issue on iOS , simply explained I have a parent view and a state flag on which I am displaying a view with position absolute when flag is truthy.

Touch events pass through the overlayed, absolute view and click-thru to elements underneath.

                <View style={styles.contentWrap}>

                   . . . elements 

                    { currentTrack !== null ? 
                        <Pressable onPress={onPress}>
                            <AudioPlayer /> 
                        </Pressable>
                        : <View></View> 
                    }
                </View>

onPress never fires. Audioplayer is a simple View with a close button inside it at this point.

leolusoli commented 2 years ago

+1

This must be fixed somehow! We can't upgrade react-native to 0.66.x because of this. Please help

github-actions[bot] commented 1 year ago

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.

mathbalduino commented 1 year ago

Still happening...

manosKas commented 1 year ago

+1

wizapp commented 1 year ago

Same issue too today +1

Van-peterson commented 1 year ago

I don't know my solution is the answer or not, here it is: Make sure there are not elements after your absoluted view.

For example:

function App() {
    return (
        <View>
            <AbsolutedView>
                <Touchable>...</Touchable>
            </AbsolutedView>
            <View></View> // <== One view after the absoluted view,
                                 it may break your touch events.

            /**
             * Change their orders will work.
             */
            <View></View>
            <AbsolutedView></AbsolutedView> // <== Place it at the last.
        </View>
    );
}

This solution works for me. Thanks a lot. @LitileXueZha

fabOnReact commented 10 months ago

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

ashishsingh101 commented 5 months ago

adding pointerEvents=none to layout fixed this issue for me, pointerEvents can be NONE or #none depending on the language used

PiewDev commented 4 months ago

I'm having the same problem ,when the touchable in absolute view appears I can still touch through it to the inputs below.

return (
    <View style={styles.container}>
      <InputIngredient onSearch={handleSearch} newValue={ingredientValue} />
      {filteredIngredients?.length > 0 && (
      <FlatList //FlatList is the absolute view, i try use a View and is the same
        style= {styles.ingredientsList}
        data={filteredIngredients}
        renderItem={({ item }) => (
          <TouchableOpacity style={styles.ingredient} onPress={() => handlePress(item)}>
            <View style={styles.ingredient}>
              <Image style={styles.image} source={{ uri: item.image }} />
              <View style={styles.bodyData}>
                <Text style={styles.text}>{item.name}</Text>
                <MacrosViewer macros={item.macros} textStyle={styles.macroText} />
              </View>
            </View>
          </TouchableOpacity>
        )}
      />
      )}

    </View>
  );
};

const styles = StyleSheet.create({
  bodyData: {
    alignItems: 'flex-start'
  },
  container: {
    alignItems: 'flex-start',
    justifyContent: 'center',
    marginBottom: 10
  },
  image: {
    height: 50,
    marginRight: 5,
    width: 50
  },
  ingredient: {
    borderBottomColor: 'gray',
    borderWidth: 0.5,
    elevation: (Platform.OS === 'android') ? 50 : 0,
    flexDirection: 'row',
    pointerEvents: 'box-only',
    position: 'relative',
    zIndex: 10
  },
  ingredientsList: {
    backgroundColor: 'rgba(0, 0, 0, 0.5)',
    borderBottomColor: 'gray',
    borderWidth: 0.5,
    elevation: (Platform.OS === 'android') ? 50 : 0,
    left: 0,
    position: 'absolute',
    right: 0,
    top: 50,
    width: 320,
    zIndex: 2
  },
  macroText: {
    color: 'rgba(225, 225, 225, 1)',
    fontSize: 12
  },
  text: {
    color: 'rgba(240, 240, 240, 1)',
    elevation: (Platform.OS === 'android') ? 40 : 0,
    fontSize: 14,
    fontWeight: 'bold',
    marginTop: 5,
    position: 'relative',

    textAlign: 'center'
  }
});

In my Main

const Main = () => {
  return (
    <SafeAreaView style={styles.container}>
      <IngredientSelector />
      <IngredientSelector />
    </SafeAreaView>
  );
};