Я пытался настроить стили и порядок рендеринга элементов, чтобы Android и iOS вели себя одинаково. Провел много исследований, и текущие ответы, похоже, не решают проблему.
Проблема этот точный код работает на 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.info(`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
}
});
Нативная версия React — 0.59.3
Несколько замечаний:
zIndex
, чтобы элемент меню отображался поверх других элементов (отрисовывался после компонента раскрывающегося списка).TouchableHighlight
, TouchableOpacity
, TouchableWithoutFeedback
и только для Android TouchableNativeFeedback
View
и ScrollView
(изначально была прокрутка)Select
, которое переключает флаг open
, работает правильно как на Android, так и на iOS, поэтому я почти уверен, что оно сузилось до родительского элемента с абсолютным позиционированием.Я пробовал все эти @MikeM на самом деле редактировал вопрос, чтобы включить эту деталь, когда вы опубликовали: D
Итак, обработчик событий не работает, но как насчет визуальной обратной связи при нажатии, вы ее видели?
Нет, пресса не стреляет/не распознается.
Проблема с вашими стилями в styles.menu
, а не с zIndex.
Вы дали position:'absolute'
и top:'100%'
.
Это означает, что ваше дочернее представление позиционируется как абсолютное по отношению к родительскому представлению, и ваше дочернее представление запустится сразу после завершения родительского представления из-за top:"100%"
.
Заключение: ваши параметры рендерятся из родителя. да, они видны, потому что ниже нет других компонентов и тем более, что zIndex выше. и вы не можете трогать ничего, что не входит в диапазон родителя.
Решение:
Смотрите рабочую закуску здесь
в styles.menu измените их,
menu: {
backgroundColor: 'rgb(73,75,77)',
zIndex: 1005,
top: 0,
left: 0,
right: 0,
maxHeight: 300,
},
и вы также можете удалить zIndex: 1000 и 1005 :)
Спасибо за Ваш ответ! (zIndex был предназначен для тестирования, план состоит в том, чтобы указать только один для родителя). Совсем забыл о нажатиях вне контейнера для андроида! Тем не менее, все еще есть проблема. Пожалуйста, сошлитесь на мой запрос о награде «Хотел бы иметь нормальный элемент раскрывающегося списка (меню складывается поверх других элементов... и т.д.)». С вашим ответом, как он ведет себя как раскрывающийся список, если он не абсолютно позиционирован? Он будет поднимать содержимое, если вы отображаете элементы после раскрывающегося списка.
Вы должны установить позицию для Select
вместо Option
вот закуска: https://snack.expo.io/HkDHHo6YV
измените стиль wrap
и menu
на приведенный ниже:
wrap: {
position: 'absolute',
zIndex: 10,
width: "100%",
backgroundColor: 'rgb(73,75,77)',
},
menu: {
backgroundColor: 'rgb(73,75,77)',
maxHeight: 300,
},
Недостающая часть должна была учитывать пространство под раскрывающимся списком, поскольку он был полностью позиционирован. Мне пришлось изменить компонент Select
, чтобы отобразить элемент «заполнение» для учета пространства.
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.info(`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
},
});
Спасибо за Ваш ответ! Все выглядит так, как будто это работает, я перенесу это в свое приложение и проверю очень быстро. Выглядит многообещающе!
Таким образом, глядя на стили (еще не запуская их в приложении), элементы попадут под раскрывающийся список, потому что они полностью позиционированы. Это не проблема, потому что я могу поместить элемент под компонентом Dropdown
той же высоты. Я отредактирую ваш ответ с этими деталями, когда напишу его <3
Спасибо за вашу помощь, чтобы получить меня в нужное место! После ответа Джейдипа я, вероятно, понял это, но у меня не было времени поработать над этим. Обновил свой ответ рабочим примером!
Отлично, я только что обновил ссылку на закуску с примером, который вы привели.
Был еще один вопрос, который я пытался выяснить. Думаю, это ошибка в том, как styled-components
применяет стили. Похоже, что zIndex
не устанавливался на компоненте в стиле Wrap
. Очень странно, но я просто собираюсь использовать метод StyleSheet
для этого элемента :) Еще раз спасибо, наградил вас $$ :D
@ShashinBhayani, а что, если мне нужно это место? Есть ли решение для этого случая? <View style = {{height: 60}}></View>
может быть, попробовать использовать один из других сенсорных вариантов? например
<TouchableOpacity>
вместо<TouchableWithoutFeedback>