可触摸印刷机未在带有zIndex的绝对定位容器中触发(用于下拉菜单)

时间:2019-04-08 18:18:21

标签: reactjs react-native

我一直在尝试调整样式和元素的渲染顺序,以使Android和iOS的行为相同。做了大量研究,当前的答案似乎无法解决问题。

  • 产品:我正在编写一个跨平台下拉组件
  • 问题/行为:在iOS上,印刷机可以正确触发,但是在android上,印刷机会掉线。
  • 示例Here is a codepen showing what it should do 如果您想在码本上进行更改,请分叉

问题是此确切的代码可在iOS上使用,而不能在Android设备上

class Dropdown extends React.Component {
  constructor() {
    super()
    this.state = {
      open: false,
      selected: undefined,
    }
  }

  handleSelectPress = () => {
    this.setState({open: !this.state.open});
  }
  handleOptionPress = (item) => {
    this.setState({selected: item, open: !this.state.open});
    this.props.onSelectOption(item);
  }
  render () {
    const { selected = {}, open } = this.state
    const { placeholder = '', options = [] } = this.props

    return (
      <Select
        onPress={this.handleSelectPress}
        value={selected.name || placeholder}
        open={open}
        options={options}
        onOptionPress={this.handleOptionPress} />
    )
  }
}

const shadowStyles = {
  ios: {
    shadowOpacity: 0.3,
    shadowRadius: 3,
    shadowOffset: {
        height: 1,
        width: 1,
    },
  },
  android: {
    elevation: 5,
  },
}

class Select extends React.PureComponent {
  render () {
    const { value, open, options, onOptionPress } = this.props
    const shadowStyle = shadowStyles[Platform.OS]
    return (
      <View style={[shadowStyle, styles.wrap]}>
        <TouchableWithoutFeedback onPress={this.props.onPress}>
          <View style={styles.selectContainer}>
            <Text>{value}</Text>
          </View>
        </TouchableWithoutFeedback>
        {open && <View style={styles.menu}>
          { options.map( (item, idx) => <Option value={item} key={idx} onPress={onOptionPress} />)}
        </View>}
      </View>

    )
  }
}

class Option extends React.PureComponent {
  handlePress = () => {
    this.props.onPress(this.props.value)
  }
  render () {
    const { value } = this.props
    return (
      <TouchableOpacity onPress={this.handlePress}>
        <View style={styles.optionContainer}>
          <Text>{value.name}</Text>
        </View>
      </TouchableOpacity>
    )
  }
}

class App extends React.Component {
  render() {
    return (
      <View style={styles.root}>
        <Dropdown
          placeholder="--- SELECT ---"
          options={
            [
              { name: "Press me!", id: "1" },
              { name: "No Me!", id: "2" },
              { name: "Cmon guys, here!", id: "3" },
            ]
          }
          onSelectOption={ (item) => console.log(`item pressed! ${item}`)}
        />
      </View>
    )
  }
}

const styles = StyleSheet.create({
    root: {
      width: 360,
      height: 640,
      backgroundColor: '#292c2e'
    },
    wrap: {
      position: 'relative',
      zIndex: 10,
      backgroundColor: 'rgb(73,75,77)'
    },
    selectContainer: {
      padding: 16,
    },
    menu: {
      backgroundColor: 'rgb(73,75,77)',
      position: 'absolute',
      top: '100%',
      left: 0,
      right: 0,
      maxHeight: 300
    },
    optionContainer: {
      padding: 16
    }

  });

反应本机版本-0.59.3

几件事要注意:

  • 我不是在寻找已经做到这一点的软件包。
  • 我正在使用zIndex,以便将菜单元素呈现在其他元素之上(在下拉组件之后呈现)。
  • 我尝试使用TouchableHighlightTouchableOpacityTouchableWithoutFeedback,并且仅适用于Android TouchableNativeFeedback
  • 我尝试在ViewScrollView之间更改“菜单”元素(最初是滚动)
  • 我在选项/ touchables上玩过海拔和z-index
  • Select上切换open标志的触摸在Android和iOS上均正常工作,因此,我很确定它的范围缩小到了绝对定位的父级。
  • 我正计划添加动画,修饰样式...等等(在项目中自动使用样式化组件)。但是首先需要它起作用:D

2 个答案:

答案 0 :(得分:2)

String errorMsg; StreamBuilder( stream: myStream, builder: (BuildContext context, snapshot) { if (snapshot.hasError) { errorMsg = snapshot.error.toString(); } if (errorMsg != null) { return Text(errorMsg); } return new Text( snapshot.data.toString(), ); }, ) 中的样式存在问题,而不是zIndex。

您已给styles.menuposition:'absolute'

这意味着您的子视图相对于父视图处于绝对位置,并且由于top:'100%',您的子视图将在父视图完成后立即开始。

结论:您的选项在父项之外显示。是的,它们可见,因为下面没有其他组件,并且zIndex更高。并且您不能触摸任何不在父级范围内的东西。

解决方案See working snack here

top: '100%'中,更改以下内容:

styles.menu

您还可以删除zIndex:也可以删除1000和1005:)

答案 1 :(得分:1)

您必须为Select而不是Option设置职位

这是小吃:https://snack.expo.io/HkDHHo6YV

更改wrapmenu的样式,如下所示:

wrap: {
  position: 'absolute',
  zIndex: 10,
  width: "100%",
  backgroundColor: 'rgb(73,75,77)',
},
menu: {
  backgroundColor: 'rgb(73,75,77)',
  maxHeight: 300,
},

编辑,可行的解决方案

缺少的部分是要考虑下拉菜单下的空间,因为它是完全绝对定位的。我不得不更改Select组件以呈现“ padding”元素以说明空间。

class Select extends React.PureComponent {
  render() {
    const { value, open, options, onOptionPress } = this.props;
    const shadowStyle = shadowStyles[Platform.OS];
    return (
      <>
        <View style={{height: 60}}></View>
        <View style={[shadowStyle, styles.wrap]}>
          <TouchableWithoutFeedback onPress={this.props.onPress}>
            <View style={styles.selectContainer}>
              <Text>{value}</Text>
            </View>
          </TouchableWithoutFeedback>
          {open && (
            <View style={styles.menu}>
              {options.map((item, idx) => (
                <Option value={item} key={idx} onPress={onOptionPress} />
              ))}
            </View>
          )}
        </View>
      </>
    );
  }
}

然后将styles.selectContainer的高度调整为相同的高度60

selectContainer: {
  padding: 16,
  height: 60
},

完整的示例

import * as React from 'react';
import {
  Text,
  Button,
  View,
  StyleSheet,
  Platform,
  TouchableWithoutFeedback,
  TouchableOpacity,
} from 'react-native';

class Dropdown extends React.Component {
  constructor() {
    super();
    this.state = {
      open: false,
      selected: undefined,
    };
  }

  handleSelectPress = () => {
    this.setState({ open: !this.state.open });
  };
  handleOptionPress = item => {
    this.setState({ selected: item, open: !this.state.open });
    this.props.onSelectOption(item);
  };
  render() {
    const { selected = {}, open } = this.state;
    const { placeholder = '', options = [] } = this.props;

    return (
      <Select
        onPress={this.handleSelectPress}
        value={selected.name || placeholder}
        open={open}
        options={options}
        onOptionPress={this.handleOptionPress}
      />
    );
  }
}

const shadowStyles = {
  ios: {
    shadowOpacity: 0.3,
    shadowRadius: 3,
    shadowOffset: {
      height: 1,
      width: 1,
    },
  },
  android: {
    elevation: 5,
  },
};

class Select extends React.PureComponent {
  render() {
    const { value, open, options, onOptionPress } = this.props;
    const shadowStyle = shadowStyles[Platform.OS];
    return (
      <>
        <View style={{height: 60}}></View>
        <View style={[shadowStyle, styles.wrap]}>
          <TouchableWithoutFeedback onPress={this.props.onPress}>
            <View style={styles.selectContainer}>
              <Text>{value}</Text>
            </View>
          </TouchableWithoutFeedback>
          {open && (
            <View style={styles.menu}>
              {options.map((item, idx) => (
                <Option value={item} key={idx} onPress={onOptionPress} />
              ))}
            </View>
          )}
        </View>
      </>
    );
  }
}

class Option extends React.PureComponent {
  handlePress = () => {
    this.props.onPress(this.props.value);
  };
  render() {
    const { value } = this.props;
    return (
      <TouchableOpacity onPress={this.handlePress}>
        <View style={styles.optionContainer}>
          <Text>{value.name}</Text>
        </View>
      </TouchableOpacity>
    );
  }
}

export default class App extends React.Component {
  render() {
    return (
      <View style={styles.root}>
        <Dropdown
          placeholder="--- SELECT ---"
          options={[
            { name: 'Press me!', id: '1' },
            { name: 'No Me!', id: '2' },
            { name: 'Cmon guys, here!', id: '3' },
          ]}
          onSelectOption={item => console.log(`item pressed! ${item}`)}
        />
        <Text>hellof</Text>
        <View style={{backgroundColor: "red", height: 250}} />
      </View>
    );
  }
}

const styles = StyleSheet.create({
  root: {
    width: 360,
    height: 640,
    backgroundColor: '#292c2e',
    marginTop: 100
  },
  wrap: {
    position: 'absolute',
    zIndex: 10,
    width: "100%",
    backgroundColor: 'rgb(73,75,77)',
  },
  selectContainer: {
    padding: 16,
    height: 60
  },
  menu: {
    backgroundColor: 'rgb(73,75,77)',
    maxHeight: 300,
  },
  optionContainer: {
    padding: 16,
    height: 60
  },
});