最近做一个需求,需求里需要实现一个类似Picker组件的效果,如下图所示,页面布局很简单,上面一个View 包含两个Text或者Touch*组件,下面放置一个Picker组件。
这个组件在app中已经存在,本来打算直接桥接native的,觉得这样基础的组件,还是使用RN的吧,这样就开始了我的Picker刨根之路。
布局代码如下:
<Animated.View style={[styles.tip , {transform: [{ translateY: this.state.offset.interpolate({ inputRange: [0, 1], outputRange: [height, (height-aHeight)] }), }] }]}> <View style={styles.tipTitleView} > <Text style={styles.cancelText} onPress={this.cancel.bind(this)}>取消</Text> <Text style={styles.okText} onPress={this.ok.bind(this)} >确定</Text> </View> <Picker style={styles.picker} mode={Picker.MODE_DIALOG} itemStyle={styles.itempicker} selectedValue={this.state.choice} onValueChange={choice => this.setState({choice: choice})}> {this.options.map((aOption) => <Picker.Item color='#b5b9be' label={aOption} value={aOption} key={aOption} /> )} </Picker> </Animated.View>
那么问题来了,当我给Picker的style属性设置height为161的时候,发现Picker的高度确实是161,背景色为green,但是滚动轮的height确不是161,如下图所示:
在上面的一个View部分,还是可以点击Picker的item选项,当我点击取消和确定的时候,Picker.item还是可以点击,这是不是Picker的bug?
我是先在iOS上实现的,后来了解,Picker在原生iOS中height系统默认是216,好像还不能修改,what ?
难道又要和UI妥协,让修改下height。有点...
好吧,还是看下RN的源码看看是怎么回事!
搜索找到PickerIOS 这个类。有问题多看看源码,绝大多数问题都可以在源码中找到答案!
发现下面2段代码:
代码片段1:
代码片段2:
看到这儿,似乎找到了我想要的答案。
Picker有如下两个属性:
那么想要实现我们的效果,设置itemStyle的属性,覆盖默认的height不就可以了吗。
经过实现,确实解决了问题,这里也说明系统默认的Picker的216的高度也是可以修改的。
该组件实现功能
1、动画效果,点击时,组件从手机下面弹出,有遮罩层
2、布局效果见本文开始处
源码如下:
'use strict'; import React, { Component } from 'react'; import { StyleSheet, View, Image, Text, TouchableHighlight, Animated, Easing, Dimensions, Picker, TouchableOpacity, } from 'react-native'; const {width, height} = Dimensions.get('window'); const navigatorH = 64; // navigator height const [aWidth, aHeight] = [width, 214]; const [left, top] = [0, 0]; const styles = StyleSheet.create({ container: { position:"absolute", width:width, height:height, left:left, top:top, }, mask: { justifyContent:"center", backgroundColor:"#383838", opacity:0.8, position:"absolute", width:width, height:height, left:left, top:top, }, tip: { width:aWidth, height:aHeight, // left:middleLeft, backgroundColor:"#fff", alignItems:"center", justifyContent:"space-between", }, tipTitleView: { height:53, width:aWidth, flexDirection:'row', alignItems:'center', justifyContent:'space-between', borderBottomWidth:0.5, borderColor:"#f0f0f0", }, cancelText:{ color:"#e6454a", fontSize:16, paddingLeft:30, }, okText:{ color:"#e6454a", fontSize:16, paddingRight:27, fontWeight:'bold', }, picker:{ justifyContent:'center', // height: 216,//Picker 默认高度 width:aWidth, }, itempicker:{ color:'#e6454a', fontSize:19, height:161 } }); export default class PickerWidget extends Component { constructor(props) { super(props); this.state = { offset: new Animated.Value(0), opacity: new Animated.Value(0), choice:this.props.defaultVal, hide: true, }; this.options = this.props.options; this.callback = function () {};//回调方法 this.parent ={}; } componentWillUnMount(){ this.timer && clearTimeout(this.timer); } render() { if(this.state.hide){ return (<View />) } else { return ( <View style={styles.container} > <Animated.View style={ styles.mask } > </Animated.View> <Animated.View style={[styles.tip , {transform: [{ translateY: this.state.offset.interpolate({ inputRange: [0, 1], outputRange: [height, (height-aHeight)] }), }] }]}> <View style={styles.tipTitleView} > <Text style={styles.cancelText} onPress={this.cancel.bind(this)}>取消</Text> <Text style={styles.okText} onPress={this.ok.bind(this)} >确定</Text> </View> <Picker style={styles.picker} mode={Picker.MODE_DIALOG} itemStyle={styles.itempicker} selectedValue={this.state.choice} onValueChange={choice => this.setState({choice: choice})}> {this.options.map((aOption) => <Picker.Item color='#b5b9be' label={aOption} value={aOption} key={aOption} /> )} </Picker> </Animated.View> </View> ); } } componentDidMount() { } //显示动画 in() { Animated.parallel([ Animated.timing( this.state.opacity, { easing: Easing.linear, duration: 500, toValue: 0.8, } ), Animated.timing( this.state.offset, { easing: Easing.linear, duration: 500, toValue: 1, } ) ]).start(); } //隐藏动画 out(){ Animated.parallel([ Animated.timing( this.state.opacity, { easing: Easing.linear, duration: 500, toValue: 0, } ), Animated.timing( this.state.offset, { easing: Easing.linear, duration: 500, toValue: 0, } ) ]).start(); this.timer = setTimeout( () => this.setState({hide: true}), 500 ); } //取消 cancel(event) { if(!this.state.hide){ this.out(); } } //选择 ok() { if(!this.state.hide){ this.out(); this.callback.apply(this.parent,[this.state.choice]); } } show(obj:Object,callback:Object) { this.parent = obj; this.callback = callback; if(this.state.hide){ this.setState({ hide: false}, this.in); } } }