You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Native安卓端绝对定位View内TouchableOpacity点击失效问题及解决

React Native安卓端绝对定位下拉项点击失效问题

开发React Native的SelectBox组件时,为实现下拉菜单悬浮效果,给包裹下拉项的View设置了absolute绝对定位,下拉项内部使用TouchableOpacity作为可点击组件。这套实现方案在iOS和Web端运行正常,但在安卓端无法响应点击事件。

已尝试的常见解决方案均不适用于当前场景,包括调整组件渲染顺序、给外层包裹View设置zIndex、改用Pressable组件等。

完整实现代码如下:

import {
  Animated,
  Easing,
  Platform,
  StyleProp,
  TextStyle,
  TouchableOpacity,
  View,
  ViewStyle,
} from 'react-native';
import {DoobooTheme, light, useTheme} from '../theme';
import React, {FC, ReactElement, useEffect, useRef, useState} from 'react';

import {Icon} from '../Icon';
import {Typography} from '../Typography';
import styled from '@emotion/native';
import {withTheme} from '@emotion/react';

const Title = styled.View`
  width: 200px;
  height: 30px;
  border-width: 1px;

  flex-direction: row;
  justify-content: center;
  align-items: center;
`;

const Item = styled.View`
  height: 30px;
  width: 200px;
  border-bottom-width: 1px;
  border-left-width: 1px;
  border-right-width: 1px;

  justify-content: center;
  align-items: center;
`;

type Styles = {
  titleContainer?: StyleProp<ViewStyle>;
  titleText?: StyleProp<TextStyle>;
  rightElementContainer?: StyleProp<ViewStyle>;
  itemContainer?: StyleProp<ViewStyle>;
  itemText?: StyleProp<TextStyle>;
};
interface ItemCompProps {
  value: string;
  order: number;
  styles?: Styles;
  setIsOpened: (value: boolean) => void;
  itemActiveOpacity: number;
  onPress?: (i: number) => void;
}

const ItemComp: FC<ItemCompProps> = ({
  value,
  order,
  styles,
  setIsOpened,
  itemActiveOpacity,
  onPress,
}) => {
  const {theme} = useTheme();

  const handlePress = (): void => {
    onPress?.(order);
    setIsOpened(false);
  };

  return (
    <TouchableOpacity onPress={handlePress} activeOpacity={itemActiveOpacity}>
      <Item
        style={[
          {
            borderColor: theme.primary,
            backgroundColor: theme.textContrast,
          },
          styles?.itemContainer,
        ]}>
        <Typography.Body2 style={styles?.itemText}>{value}</Typography.Body2>
      </Item>
    </TouchableOpacity>
  );
};

interface Props {
  data: string[];
  onPress?: (i: number) => void;
  selectedIndex?: number;
  theme?: DoobooTheme;
  style?: StyleProp<ViewStyle>;
  styles?: Styles;
  rotateDuration?: number;
  titleActiveOpacity?: number;
  itemActiveOpacity?: number;
  isRotate?: boolean;
  rightElement?: ReactElement | null;
}

const Component: FC<Props> = ({
  data,
  onPress,
  selectedIndex = 0,
  style,
  styles,
  rotateDuration = 200,
  titleActiveOpacity = 1,
  itemActiveOpacity = 1,
  isRotate: shouldRotate = true,
  rightElement = <Icon name="chevron-down-light" />,
}) => {
  const {theme} = useTheme();
  const [isOpened, setIsOpened] = useState(false);

  const rotateAnimValue = useRef(new Animated.Value(0)).current;

  useEffect(() => {
    const toValue = isOpened ? 1 : 0;

    if (!shouldRotate) rotateAnimValue.setValue(toValue);

    Animated.timing(rotateAnimValue, {
      toValue,
      duration: rotateDuration,
      easing: Easing.linear,
      useNativeDriver: Platform.OS !== 'web' ? true : false,
    }).start();
  }, [isOpened, rotateAnimValue, rotateDuration, shouldRotate]);

  return (
    <View style={[style]}>
      <TouchableOpacity
        onPress={() => setIsOpened((prev) => !prev)}
        activeOpacity={titleActiveOpacity}>
        <Title
          style={[
            {
              borderColor: theme.primary,
              backgroundColor: theme.textContrast,
            },
            styles?.titleContainer,
          ]}>
          <Typography.Body2 style={styles?.titleText} testID="selected-value">
            {data[selectedIndex]}
          </Typography.Body2>
          {rightElement ? (
            <Animated.View
              style={[
                {
                  position: 'absolute',
                  right: 10,
                  transform: [
                    {
                      rotate: rotateAnimValue.interpolate({
                        inputRange: [0, 1],
                        outputRange: ['0deg', '180deg'],
                      }),
                    },
                  ],
                },
                styles?.rightElementContainer,
              ]}>
              {rightElement}
            </Animated.View>
          ) : null}
        </Title>
      </TouchableOpacity>
      <View>
        <View style={{position: 'absolute'}}>
          {isOpened &&
            data.map((datum, key) => (
              <ItemComp
                key={key}
                order={key}
                value={datum}
                styles={styles}
                setIsOpened={setIsOpened}
                onPress={onPress}
                itemActiveOpacity={itemActiveOpacity}
              />
            ))}
        </View>
      </View>
    </View>
  );
};

Component.defaultProps = {theme: light};

export const SelectBox = withTheme(Component);

解决方案

通过react-native-gesture-handler即可解决该问题:

  • 第一步:安装react-native-gesture-handler依赖
  • 第二步:将原有代码中从react-native导入的TouchableOpacity,替换为该库导出的TouchableOpacity组件即可。

内容的提问来源于stack exchange,提问作者dean

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.07 04:36:03