最近做一个需求,需求里需要实现一个类似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);
}
}
}