reactnative下拉选择
React Native下拉选择组件摘要: 该组件实现了一个通用的下拉筛选功能,主要特点包括: 支持对象数组和字符串数组两种数据格式 可自定义显示文本和值的字段名 通过Modal实现下拉菜单展示 包含选中状态样式和箭头图标切换效果 支持外部样式自定义和禁用状态 实现按钮与菜单宽度对齐的功能 关键参数: options:选项数组 value:当前选中值 onChange:选择回调函数 labelKe
·
reactnative下拉选择
引用
<FilterDropdown
label={""}
options={this.state.recordListType}
value={this.state.recordType}
onChange={(item) => this.handleRecordTypeChange(item)}
leftIcon={recordSimg}
width={'100%'}
style={{ width: "100%", color: 'red' }}
labelKey="label"
valueKey="value"
/>
代码组件
import React, { useState, useRef } from 'react';
import { View, Text, TouchableOpacity, StyleSheet, Image, Modal } from 'react-native';
/**
* 通用下拉筛选组件(横向筛选栏专用,菜单用Modal渲染)
* @param {string} label - 按钮显示文字
* @param {array} options - 选项数组(字符串数组或对象数组)
* @param {string|object} value - 当前选中值
* @param {function} onChange - 选中项变化回调
* @param {number} width - 按钮宽度(可选)
* @param {string} labelKey - 对象数组中用于显示的字段名(可选,默认为'label')
* @param {string} valueKey - 对象数组中用于值的字段名(可选,默认为'value')
* @param {string} displayKey - 按钮显示文字的字段名(可选,默认为'label')
*/
const FilterDropdown = ({
label = '',
options = [],
value = '',
onChange = () => {}, // 默认空函数
width = zero.yWidth(100),
leftIcon,
disabled = false,
style = {}, // 新增,接收外部style
labelKey = 'label', // 对象数组中用于显示的字段名
valueKey = 'value', // 对象数组中用于值的字段名
displayKey = 'label', // 按钮显示文字的字段名
}) => {
const [visible, setVisible] = useState(false);
const btnRef = useRef();
const [btnLayout, setBtnLayout] = useState({ x: 0, y: 0, width: width, height: zero.yHeight(32) });
// 判断是否为对象数组
const isObjectArray = options.length > 0 && typeof options[0] === 'object';
// 获取显示文本
const getDisplayText = (item) => {
if (isObjectArray) {
return item[displayKey] || item[labelKey] || '';
}
return item;
};
// 获取选项值
const getOptionValue = (item) => {
if (isObjectArray) {
return item[valueKey] || item[labelKey] || '';
}
return item;
};
// 获取当前选中项的显示文本
const getCurrentDisplayText = () => {
if (!value) return label;
if (isObjectArray) {
const selectedOption = options.find(option => getOptionValue(option) === value);
return selectedOption ? getDisplayText(selectedOption) : label;
}
return value;
};
// 记录按钮宽度和位置,菜单宽度与按钮对齐
const handleBtnLayout = (e) => {
if (btnRef.current) {
btnRef.current.measureInWindow((x, y, width, height) => {
setBtnLayout({ x, y, width, height });
});
}
};
return (
<View style={[styles.wrap, style]}>
<TouchableOpacity
ref={btnRef}
style={[styles.btn, value ? styles.btnActive : null, { width }]}
activeOpacity={0.8}
onPress={() => {
if (disabled) return
handleBtnLayout();
setVisible(!visible);
}}
>
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
{leftIcon && (
<Image
source={leftIcon}
style={[styles.leftIcon]}
/>
)}
<Text style={[styles.btnText, value ? styles.btnTextActive : null]}>{getCurrentDisplayText()}</Text>
</View>
<Image
source={visible ? require('../res/arrowup.png') : require('../res/arrowdown.png')}
style={[styles.arrowIcon]}
/>
</TouchableOpacity>
<Modal
visible={visible}
transparent
animationType="fade"
onRequestClose={() => setVisible(false)}
>
<TouchableOpacity
style={styles.modalMask}
activeOpacity={1}
onPress={() => setVisible(false)}
>
<View style={[
styles.dropdown,
{
position: 'absolute',
left: btnLayout.x,
top: btnLayout.y + btnLayout.height,
width: btnLayout.width,
}
]}>
{options.map((opt, idx) => {
const optionValue = getOptionValue(opt);
const optionText = getDisplayText(opt);
const isSelected = isObjectArray ? optionValue === value : opt === value;
return (
<React.Fragment key={optionValue || idx}>
<TouchableOpacity
style={styles.option}
onPress={() => {
setVisible(false);
onChange && onChange(optionValue, opt);
}}
>
<Text style={[styles.optionText, isSelected && styles.optionTextActive]}>{optionText}</Text>
</TouchableOpacity>
{idx !== options.length - 1 && <View style={styles.divider} />}
</React.Fragment>
);
})}
</View>
</TouchableOpacity>
</Modal>
</View>
);
};
const styles = StyleSheet.create({
wrap: {
position: 'relative',
marginRight: zero.yWidth(8),
justifyContent: 'space-between',
zIndex: 10,
// width: zero.yWidth(87), // 移除固定宽度
// height: zero.yHeight(29.43), // 移除固定高度
},
btn: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: '#F5F7FA',
borderRadius: zero.yHeight(10),
paddingHorizontal: zero.yWidth(10),
height: zero.yHeight(32),
borderWidth: 1,
borderColor: 'transparent',
minWidth: zero.yWidth(70),
justifyContent: 'space-between',
},
btnActive: {
// borderColor: '#1890FF',
// backgroundColor: '#E6F0FF',
},
btnText: {
color: '#222',
fontSize: zero.yFont(15),
},
btnTextActive: {
color: '#000000',
fontWeight: 'bold',
},
leftIcon:{
width: zero.yWidth(18),
height: zero.yHeight(18),
marginRight:zero.yWidth(15),
resizeMode: 'contain',
},
arrowIcon: {
width: zero.yWidth(9.19),
height: zero.yHeight(5.89),
marginLeft: zero.yWidth(4),
tintColor: '#1890FF',
resizeMode: 'contain',
transform: [{ rotate: '0deg' }],
},
arrowIconUp: {
transform: [{ rotate: '180deg' }],
},
arrowActive: {
tintColor: '#1890FF',
},
modalMask: {
flex: 1,
backgroundColor: 'rgba(0,0,0,0.1)',
},
dropdown: {
backgroundColor: '#fff',
borderRadius: zero.yHeight(10),
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.08,
shadowRadius: zero.yHeight(8),
elevation: zero.yHeight(8),
minWidth: zero.yWidth(80),
overflow: 'hidden',
},
option: {
paddingVertical: zero.yHeight(12),
paddingHorizontal: zero.yWidth(20),
backgroundColor: '#fff',
},
optionText: {
color: '#222',
fontSize: zero.yFont(15),
textAlign: 'left',
},
optionTextActive: {
color: '#1890FF',
fontWeight: 'bold',
},
divider: {
height: zero.yHeight(1),
backgroundColor: '#F0F0F0',
marginHorizontal: zero.yWidth(10),
},
});
export default FilterDropdown;
更多推荐
所有评论(0)