react-navigation / react-navigation/react-navigation

Absolute position element doesn't not respond to touch events on Android

Abierto
#8,897 4 comentarios 4 reacciones 0 asignados Ver en GitHub

Nadie ha tomado este issue todavía.

Lenguaje dominante
TypeScript
Estrellas
24.5k
Forks
5.1k
Merge medio
1 d 5 h
PR fusionados (30 d)
18

Descripción

I tried to implement toast view by using absolute position element and found this issue. The element doesn't respond to touch when I put it outside of stack screen but float it on top of stack screen. And the issue doesn't repro when I put the element inside the screen. I put the toast view outside of screen because I would like to share it across multiple screens.
Refer below example. The local toast view responses to touch but global toast view does not.
The issue only repro in Android. It works fine in IOS.


import React, { useState, useEffect, useCallback, useRef } from 'react';
import { Platform, Animated, TouchableWithoutFeedback, View, Text } from 'react-native';
import { createStackNavigator } from '@react-navigation/stack';
import { NavigationContainer } from '@react-navigation/native';

const Stack = createStackNavigator();

function Home() {
  const styles = getStyles();
  const dismissHandler = useCallback((item) => {
    console.log('Toast should dismiss');
  }, []);

  return (
    <View>
      <Toast onPress={dismissHandler} show>
        <Text style={styles.root}>This is local toast</Text>
      </Toast>
      <Text >This is Home screen</Text>
    </View>
  );
}

export default function App() {
  const styles = getStyles();
  const dismissHandler = useCallback((item) => {
    console.log('Toast should dismiss');
  }, []);

  return (
    <NavigationContainer>
      <Toast onPress={dismissHandler} show>
        <Text style={styles.root}>This is global toast</Text>
      </Toast>
      <Stack.Navigator>
        <Stack.Screen name="Home" component={Home} />
      </Stack.Navigator>
    </NavigationContainer>
  );
}

export function Toast(props) {
  const {
    show,
    position,
    orientation,
    children,
    duration,
    onPress,
    ...restProps
  } = props;

  const animateTranslate = useRef(new Animated.Value(-10)).current;
  const animateOpacity = useRef(new Animated.Value(0)).current;
  let [showing, setShowing] = useState(false);
  let [hiding, setHiding] = useState(false);
  const styles = getStyles();

  useEffect(
    useCallback(() => {
      if (show) {
        if (!showing) {
          setShowing(true);
          setHiding(false);
          Animated.parallel([
            Animated.timing(
              animateTranslate,
              { 
                toValue: 0,
                duration: duration || 2000,
                useNativeDriver: true
              }
            ),
            Animated.timing(
              animateOpacity,
              { 
                toValue: 1,
                duration: duration || 2000,
                useNativeDriver: true
              }
            )
          ]).start();
        }
      } else {
        if (!hiding) {
          setShowing(false);
          setHiding(true);
          Animated.parallel([
            Animated.timing(
              animateTranslate,
              { 
                toValue: 10,
                duration: duration || 2000,
                useNativeDriver: true
              }
            ),
            Animated.timing(
              animateOpacity,
              { 
                toValue: 0,
                duration: duration || 2000,
                useNativeDriver: true
              }
            )
          ]).start(() => {
            setHiding(false);
            animateTranslate.setValue(-10);
          });
        }
      }
    }, [show])
  );

  if (!showing && !hiding) {
    return null;
  }

  return (
    <Animated.View style = {[
      styles.container,
      {
        top: (position === 'bottom') ? '80%' : 200,
        transform: [orientation === "yAxis" ? {
          translateY: animateTranslate 
        } : {
          translateX: animateTranslate
        }],
        opacity: animateOpacity
      }]}
      pointerEvents={show ? 'auto' : 'none'}
    >
      <TouchableWithoutFeedback style={styles.touchable} onPress={onPress}>
        <View style={styles.root}>
          {children}
        </View>
      </TouchableWithoutFeedback>
    </Animated.View>
  );
}

const getStyles = () => {
  return {
    container: {
      width: '100%',
      zIndex: 9999,
      elevation: 1,
      position: 'absolute',
    },
    touchable: {
      height: '100%',
      width: '100%',
    },
    root: {
      borderRadius: Platform.select({ios: 12, android: 4}),
      marginLeft: Platform.select({ios: 16, android: 8}),
      marginRight: Platform.select({ios: 16, android: 8}),
      marginBottom: Platform.select({ios: 8, android: 6}),
      marginTop: Platform.select({ios: 8, android: 6}),
      color: 'white',
      backgroundColor: 'black',
    },
  };
}

package.json

  "dependencies": {
    "expo": "~38.0.0",
    "react": "~16.12.0",
    "react-dom": "~16.12.0",
    "react-native": "https://github.com/expo/react-native/archive/sdk-38.0.0.tar.gz",
    "@react-navigation/native": "5.7.4",
    "@react-navigation/stack": "5.9.1",
    "@react-native-community/masked-view": "0.1.10",
    "react-native-gesture-handler": "1.7.0",
    "react-native-screens": "2.11.0",
    "react-native-web": "0.13.13"
  }

Guía de contribución

Abrir la guía de contribución

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Línea de trabajo

Comienza con la reproducción proporcionada de App y Home usando las dependencias de Expo 38.0.0 en package.json, comparando el toast global fuera de Stack.Navigator con el toast local dentro de Home. Traza el manejo táctil de Android para el Animated.View con posición absoluta y el contenedor de navegación; se considera terminado cuando el toast global responde a onPress en Android, tal como ya lo hace el toast local.

Escrito por el modelo de indexación a partir del texto del issue.

Evaluación

Stack tecnológico
android, javascript, react-native
Área
mobile
Tipo de issue
Error
Dificultad
4/5
Tiempo estimado
3-5 días
Estado de actividad
Estancado
Claridad
Bastante claro
Aptitud para principiantes
42/100

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.