继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

ReactNative学习笔记--下拉选择菜单的简单封装

ZKReadStone
关注TA
已关注
手记 52
粉丝 32
获赞 322

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

打开App,阅读手记
13人推荐
发表评论
随时随地看视频慕课网APP

热门评论

获得组件位置不用这么麻烦吧,用UTmanager不就可以了吗,而且用modal的话,他是全屏覆盖的,其他的组件都失去了焦点,想按标签栏其他的按钮就不行了

求源码zhoutianlun_1989@163.com

非常好,要是能封装成插件就更完美了

查看全部评论