ReactNative 学习笔记--封装下拉菜单
单个下拉子项
先看整体要做的效果
1.实现原理:
先做一行按钮,使我们要点击弹出菜单列表的按钮,然后计算点击的按钮所在的位置,再通过点击按钮的高度计算出要弹出列表的位置和宽度高度等,利用绝对布局在Modal组件上显示,并设置对应的效果,例如'fade',弹出的Modal覆盖整个界面,选择列表子项或者点击其他位置(Modal上)都要让Modal消失,再次点击按钮的时候,显示下拉菜单前重新计算对应按钮的位置和点击按钮对应的下拉菜单的位置,然后重新更改下拉菜单的位置和内容并显示,后面就按这个逻辑。
2.实现代码
分两步:
单个子项 SiftListItem
先看render了解整体布局
render() { return ( <View {...this.props}> {this._renderButton()} {this._renderSiftList()} </View> ); }
_renderButton函数 负责按钮 showSiftList控制着是否显示SiftList
里面的item:
item: { title:'交易方向', tag:0, list:[], } _renderButton = ()=> { const {item,textStyle,style}=this.props; const {showSiftList}=this.state; let icon = showSiftList?require('../images/icon_up.svg'):require('../images/btn_down.svg'); return ( <TouchableOpacity onPress={this._onButtonPress}> <View style={[styles.button,style]}> <Text style={[styles.buttonText, textStyle]} numberOfLines={1}> {item.title} </Text> <SvgImage style={{marginLeft:4}} height={6} source={icon} /> </View> </TouchableOpacity> ); }; _renderSiftList负责下拉菜单绘制,可以写成ListView,如果下拉菜单的高度不大,且确定就可以用ScrollView,这里就是用的ScrollView _renderModal = ()=> { const {showSiftList,selectedIndex}=this.state; const {style,item}=this.props; if (showSiftList && this._buttonFrame) { let frameStyle = this._calculatePosition(); return ( <Modal animationType='fade' transparent={true} > <TouchableWithoutFeedback onPress={this._onModalPress}> <View style={[styles.modal]}> <View style={[frameStyle,styles.dropdown,{height:item.list?30*item.list.length:0},{width:style.width}]}> { item.list?item.list.map((sublist,i)=>{ return( <TouchableOpacity onPress={()=>this.select(i)} key={i} > <View style={[styles.subItemStyle,{width:style.width-1},selectedIndex===i&&{backgroundColor:System_styles.hei_240}]} > <Text style={[styles.rowText,selectedIndex===i&&{color:System_styles.blue}]}> {sublist} </Text> </View> </TouchableOpacity> ) }):null} </View> </View> </TouchableWithoutFeedback> </Modal> ); } };
计算SiftList菜单的位置
_calculatePosition = ()=> { const {style}=this.props; let dimensions = Dimensions.get('window'); let windowWidth = dimensions.width; let windowHeight = dimensions.height; let dropdownHeight = (style && StyleSheet.flatten(style).height) || StyleSheet.flatten(styles.dropdown).height; let bottomSpace = windowHeight - this._buttonFrame.y - this._buttonFrame.h; let rightSpace = windowWidth - this._buttonFrame.x; let showInBottom = bottomSpace >= dropdownHeight || bottomSpace >= this._buttonFrame.y; let showInLeft = rightSpace >= this._buttonFrame.x; var style = { height: dropdownHeight, top: (showInBottom ? this._buttonFrame.y + this._buttonFrame.h : Math.max(0, this._buttonFrame.y - dropdownHeight))-0.5, } if (showInLeft) { style.left = this._buttonFrame.x; } else { let dropdownWidth = (style && StyleSheet.flatten(style).width) || -1; if (dropdownWidth !== -1) { style.width = dropdownWidth; } style.right = rightSpace - this._buttonFrame.w; } if (this.props.adjustFrame) { style = this.props.adjustFrame(style) || style; } return style; };
子项的选中和下拉菜单的显示、隐藏方法
show = ()=> { this._updatePosition(() => { this.setState({ showSiftList: true, }); }); }; hide = ()=> { this.setState({ showSiftList: false, }); }; select = (index)=> { const {item,selectedCallBack}=this.props; const {selectedIndex}=this.state; if (index == null || item.list == null || index >= item.list.length) { index = selectedIndex; } this.setState({ selectedIndex: index, }); selectedCallBack&&selectedCallBack(index,item.tag); this.hide(); };
获取按钮对应位置的方法
_updatePosition = (callback)=> { if (this._button && this._button.measure) { this._button.measure((fx, fy, width, height, px, py) => { this._buttonFrame = {x: px, y: py, w: width, h: height}; callback && callback(); }); } };
封装成一个组件SiftListControl
export default class SiftListControl extends Component { static defaultProps = { items:[ { title:'交易方向', tag:0, icon:require('../images/btn_down.svg'), list:[], } ] }; constructor(){ super(); this.state = { }; } _selectedIndex = (index,tag)=>{ const {callBack}=this.props; callBack&&callBack(index,tag); }; render() { const {items,subItemStyle}=this.props; return ( <View style={[styles.listBar,this.props.style]}> { items.map((item,i)=>{ return( <SiftListViewNew style={{backgroundColor:'white',width:subItemStyle.width}} item={item} key={i} selectedCallBack={this._selectedIndex} > </SiftListViewNew> ) }) } </View> ); } } const styles = StyleSheet.create({ listBar:{ height:32, flexDirection:'row', } });
下载链接:http://7xrqmg.com1.z0.glb.clouddn.com/Sift.zip
链接:http://www.imooc.com/article/15100