onPress 在 Android 上无法触发,但在 iOS 上正常

JmJ*_*JmJ 5 javascript android react-native

我已经编写了一个 React Native 应用程序,iOS 是首要任务,所以我首先构建了它。它在 App Store 中并且工作完美,但是我刚刚开始在 Android 上工作,尽管除了触摸事件之外一切似乎都工作正常,而触摸事件根本没有触发。

没有任何可触摸元素正在调用回调onPress,该元素也没有Button。我什至尝试完全剥离应用程序,删除导航器,并向初始屏幕添加大量可触摸元素,但仍然没有onPress触发任何回调。

下面是我的应用程序初始屏幕的代码,尽管我怀疑这些代码是否导致了问题:

// @flow
import React, { type Element } from 'react';
import { View, Text, Image, TouchableOpacity } from 'react-native';
import type { NavigatorScreenProps } from 'react-navigation';
import i18n from '../../i18n';
import style from './style';

type Props = {
  navigation: NavigatorScreenProps
}

export default function SignIn ({ navigation }: Props): Element<typeof View> {
  return (
    <View style={style.container}>
      <View style={style.top}>
        <Image source={require('../../assets/images/purpleWithTransparentBackground.png')} style={style.logo} />
      </View>
      <View style={style.bottom}>
        <TouchableOpacity activeOpacity={0.97} onPressIn={() => console.log('in')} onPressOut={() => console.log('out')} onPress={() => { console.log('do something!'); navigation.navigate('EnterEmail'); }} style={[style.submit, { zIndex: 99999, elevation: 99999 }]}>
          <Text style={style.submitText}>
            {i18n.t('SIGN_IN')}
          </Text>
        </TouchableOpacity>
      </View>
      <Image source={require('../../assets/images/cityscapeGrey.png')} style={style.cityscape} />
    </View>
  );
}
Run Code Online (Sandbox Code Playgroud)

组件样式:

import { StyleSheet, Dimensions } from 'react-native';
import defaultStyles from '../../style';

const { width: screenWidth } = Dimensions.get('window');

export default StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: defaultStyles.white
  },

  top: {
    flex: 1,
    justifyContent: 'flex-end',
    alignItems: 'center',
    width: '100%'
  },

  bottom: {
    flex: 1,
    width: '100%'
  },

  animatedContainer: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
    width: '100%'
  },

  postcode: {
    padding: 12,
    height: 50,
    backgroundColor: 'white',
    width: '100%',
    borderRadius: 5,
    fontSize: 17
  },

  text: {
    width: 296,
    height: 44,
    fontFamily: 'SFProText-Light',
    fontSize: 16,
    fontWeight: '500',
    fontStyle: 'normal',
    lineHeight: 22,
    letterSpacing: 0,
    textAlign: 'center',
    color: defaultStyles.balticSea
  },

  logo: {
    marginBottom: 14
  },

  error: {
    color: defaultStyles.brickRed,
    marginVertical: 12,
    width: '100%',
    textAlign: 'center'
  },

  submit: {
    width: 311,
    height: 56,
    borderRadius: 4,
    backgroundColor: defaultStyles.mountainMeadow,
    justifyContent: 'center',
    alignItems: 'center',
    alignSelf: 'center',
    marginTop: 30
  },

  submitText: {
    width: 311,
    height: 21,
    fontFamily: 'SFProDisplay-Heavy',
    fontSize: 18,
    fontWeight: 'bold',
    fontStyle: 'normal',
    letterSpacing: 0,
    textAlign: 'center',
    color: defaultStyles.white
  },

  highlight: {
    color: defaultStyles.mountainMeadow
  },

  cityscape: {
    position: 'absolute',
    left: 0,
    bottom: 0,
    width: screenWidth,
    resizeMode: 'repeat'
  }
});
Run Code Online (Sandbox Code Playgroud)

预先感谢您的任何帮助。

JmJ*_*JmJ 1

我找到了问题的原因。

简短回答:

我的应用程序的根目录中有一个组件正在创建一个不可见的覆盖层。发生这种情况是因为如果应用于 Android 上的同一元素,display: 'none'则 和position: 'absolute'不起作用。

长答案:

在我的根组件中,我有一个从屏幕底部出现的菜单,名为OptionsMenu

export default function App (): Element<typeof Provider> {
  return (
    <Provider store={store}>
      <ActionSheetProvider>
        <OptionsMenuContext.Provider>
          <>
            <Navigation uriPrefix={DEEP_LINK_URI_PREFIX} ref={setNavigator} />
            <Notification />
            <OptionsMenu />
          </>
        </OptionsMenuContext.Provider>
      </ActionSheetProvider>
    </Provider>
  );
}
Run Code Online (Sandbox Code Playgroud)

里面OptionsMenu有一个覆盖屏幕的覆盖层,这样我们就可以在菜单出现时调暗所有内容。覆盖层(最外层Animated.View)具有position: 'absolute'以及display: 'none'。设置display来自 prop,并且position来自style.container

function OptionsMenu ({ hideOptionsMenu, overlayDisplay, overlayOpacity, containerPositionBottom, options = [] }: Props): Element<typeof Animated.View> {
  return (
    <Animated.View style={[style.container, { display: overlayDisplay }]}>
      <TouchableWithoutFeedback onPress={hideOptionsMenu}>
        <Animated.View style={[style.overlay, { opacity: overlayOpacity }]} />
      </TouchableWithoutFeedback>
      <Animated.View style={[style.optionsContainer, { bottom: containerPositionBottom }]}>
        {options.map(({ icon, text, onPress, type, component: Component }: Option) => !Component
          ? (
            <TouchableOpacity activeOpacity={0.97} key={text} disabled={!onPress} onPress={onPress} style={style.optionContainer}>
              {!!icon && (
                <Image source={icon} style={[style.optionIcon, optionTypeTintMap[type]]} />
              )}
              <Text style={[style.optionText, optionTypeColorMap[type || 'neutralColor']]}>
                {text}
              </Text>
            </TouchableOpacity>
          ) : (
            <Component key={text} />
          ))}
      </Animated.View>
    </Animated.View>
  );
}

export default withOptionsContext(OptionsMenu);
Run Code Online (Sandbox Code Playgroud)

问题是在 Android 上绝对定位将覆盖显示设置。因此,解决方案是将绝对定位的组件包装在控制显示设置的组件内:

选项菜单/style.js:

export default StyleSheet.create({
  container: {
    flex: 1,
    // Removed these:
    // position: 'absolute',
    // left: 0,
    // bottom: 0,
    // width: screenWidth,
    // height: screenHeight,
    // zIndex: 19,
    // elevation: 19
  },

  // Moved styles to new property:
  overlayContainer: {
    flex: 1,
    position: 'absolute',
    left: 0,
    bottom: 0,
    width: screenWidth,
    height: screenHeight,
    zIndex: 19,
    elevation: 19
  },
Run Code Online (Sandbox Code Playgroud)

选项菜单/OptionsMenu.js:

function OptionsMenu ({ hideOptionsMenu, overlayDisplay, overlayOpacity, containerPositionBottom, options = [] }: Props): Element<typeof Animated.View> {
  return (
    // Added new <View /> to control display setting separately:
    <View style={[style.container, { display: overlayDisplay }]}>
      <Animated.View style={style.overlayContainer}>
Run Code Online (Sandbox Code Playgroud)