从零学React Native之04自定义对话框
之前我们介绍了RN相关的知识: 本篇主要介绍: 自定义对话框之前的我都是利用React Native提供的基础组件对它们进行排列组合,其实自定义也很简单,我们还是拿上一篇文章的例子进行扩展。 当我们点击注册的时候,可以弹出一个对话框,让用户确认一下,如下图: import React,{ Component } from 'react';
import {
StyleSheet,Text,// RN提供的组件
View,BackAndroid
} from 'react-native';
let Dimensions = require('Dimensions');
let totalWidth = Dimensions.get('window').width;//宽
let totalHeight = Dimensions.get('window').height;//高
// 直接导出组件,不用写 module.exports=ConfirmDialog;了
export default class ConfirmDialog extends Component {
render() {
// onPress事件直接与父组件传递进来的属性挂接
//numberOfLines 可显示3行
// {'rn'}确 定 回车换行后跟着确定,为了克服Text组件不能垂直居中显示
return (
<View style={styles.confirmCont}>
<View style={styles.dialogStyle}>
<Text style={styles.textPrompt}>
{this.props.promptToUser}
</Text>
<Text style={styles.yesButton}
onPress={this.props.userConfirmed}
numberOfLines={3}>
{'rn'}确 定
</Text>
<Text style={styles.cancelButton}
onPress={this.props.userCanceled}
numberOfLines={3}>
{'rn'}取 消
</Text>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
confirmCont: { //全屏显示 半透明 可以看到之前的控件但是不能操作了
position:'absolute',//声明绝对定位
top:0,width:totalWidth,height:totalHeight,backgroundColor:'rgba(52,52,0.5)' //rgba a0-1 其余都是十进制数
},dialogStyle: {
position:'absolute',left:totalWidth/10,// 定义Dilaog起点位置
top:totalHeight*0.4,width:totalWidth*0.8,height:totalHeight*0.3,backgroundColor:'white'
},textPrompt: {
position:'absolute',top:10,left:10,fontSize:20,color:'black'
},yesButton: {
position:'absolute',bottom:10,width:totalWidth*0.35,height:totalHeight*0.12,backgroundColor:'grey',color:'white',textAlign:'center'
},cancelButton: {
position:'absolute',right:10,textAlign:'center'
}
});
可以看到,上面的布局使用绝对布局,通过把Text和View拼装到一起组成了对话框的组件,最外层的View是一个全屏的遮罩. 我们的对话框组件用到了几个属性,如: 挂载组件接下来我们需要修改上一篇文章中用到的 import React,{ Component } from 'react';
import {
AppRegistry,//框架提供的API
StyleSheet,// RN提供的组件
View,TextInput // 记得引入组件
} from 'react-native';
//let是更完美的var
let Dimensions = require('Dimensions');// 宽高
let totalWidth = Dimensions.get('window').width; //声明了三个变量,根据屏幕动态变化
let leftStartPoint = totalWidth * 0.1;
let componentWidth = totalWidth * 0.8;
//导入对话框
let ConfirmDialog=require('./ConfirmDialog');
class RegisterLeaf extends Component {
//构造函数,组件创建的时候会执行
constructor(props) {
super(props); //必须有这句代码 父组件向子组件传递属性,比如styles属性等
// 声明状态机的初始值
this.state = {
inputedNum: '',inputedPw: '',needToConfirm: false //导入新的状态机
};
// ES6
this.userPressConfirm=this.userPressConfirm.bind(this);
//方法不绑定 其它组件没法调用
this.userConfirmed=this.userConfirmed.bind(this);
this.userCanceled=this.userCanceled.bind(this);
}
// 定义函数
updateNum(newText) {
this.setState((state)=> { return { inputedNum: newText } }); } // 定义函数 updatePW(newText) { this.setState(()=> { // 用不到的参数也可以不用写 return { inputedPw: newText } }); } userPressConfirm(){ this.setState(()=>{ return { needToConfirm:true } }); } // 对话框取消时如何处理 userCanceled(){ this.setState({needToConfirm:false}); console.log("userPressConfirm"); } // 对话框确定 userConfirmed(){ this.setState({needToConfirm:false}); this.props.navigator.push({ phoneNumber:this.state.inputedNum,userPW:this.state.inputedPw,name:'waiting' }) } renderWithDialog(){ return( <View style={styles.container}> <TextInput style={styles.numberInputStyle} keyboardType={'phone-pad'} placeholder={'请输入手机号'} onChangeText={(newText)=>this.updateNum(newText)}/> <Text style={styles.textPromptStyle}> 您输入的手机号:{this.state.inputedNum} </Text> <TextInput secureTextEntry={true} style={styles.passwordInputStyle} placeholder='请输入密码' onChangeText={(newText)=>this.updatePW(newText)}/> <Text style={styles.bigTextPrompt} onPress={this.userPressConfirm}> 注 册 </Text> <ConfirmDialog userConfirmed={this.userConfirmed} userCanceled={this.userCanceled} promptToUser={'使用'+this.state.inputedNum+'号码登录?'}/> </View> ) } // 绘制渲染的控件 render() { // 根据不同的状态 做相应的绘制,当需要绘制对话框时,调用renderWithDialog if(this.state.needToConfirm) return this.renderWithDialog(); return ( /*(newText)=>this.updateNum(newText) 它将收到的字符串为参数调用当前组件的updateNum函数,并且将updateNum函数的返回值返回 当前函数在输入框文本变化的时候会调用 语句可以改成 this.updateNum 但一定不要写成 this.updateNum(newText) 因为有右箭头函数的时候newText是形式参数 没有箭头函数的时,newText就没有定义 */ <View style={styles.container}> <TextInput style={styles.numberInputStyle} keyboardType={'phone-pad'} placeholder={'请输入手机号'} onChangeText={(newText)=>this.updateNum(newText)}/> <Text style={styles.textPromptStyle}> 您输入的手机号:{this.state.inputedNum} </Text> <TextInput secureTextEntry={true} style={styles.passwordInputStyle} placeholder='请输入密码' onChangeText={(newText)=>this.updatePW(newText)}/> <Text style={styles.bigTextPrompt} onPress={this.userPressConfirm}> 注 册 </Text> </View> ); } } // 样式 const变量只能在声明的时候赋值一次 const styles = StyleSheet.create({ //各个组件都没有定义高度,父View设置了flex1,他会沾满整个高度,子组件没有设置会包裹内容 container: { flex: 1,//表示宽高会自动扩展 backgroundColor: 'white' },numberInputStyle: { top: 20,// top left表示从父组件的顶端(左侧) 向下(向右) 多少位置显示 left: leftStartPoint,// height:30,// IOS开发需要加上该高度 width: componentWidth,backgroundColor: 'gray',fontSize: 20 },textPromptStyle: { top: 30,left: leftStartPoint,// // height:30,// IOS开发需要加上该高度 因为IOS中TextInput不会自动设置高度 width: componentWidth,passwordInputStyle: { top: 50,width: componentWidth,bigTextPrompt: { top: 70,color: 'white',textAlign: 'center',//位置居中显示 fontSize: 60 } }); module.exports=RegisterLeaf;
可以看到,我们渲染自定义对话框时, 给对话框传递了三个属性其中 Android中返回键处理首先在RegisterLeaf组件中再增加一个函数: //告诉对话框什么时候时候需要拦截返回事件
tellConfirmDialogItsStatus() {
return this.state.needToConfirm;
}
记得在构造方法中绑定this //构造函数,组件创建的时候会执行
constructor(props) { super(props); //... this.tellConfirmDialogItsStatus=this.tellConfirmDialogItsStatus.bind(this); }
然后在挂接ConfirmDialog时再增加一个属性 amIStillAlive,如下: <ConfirmDialog userConfirmed={this.userConfirmed}
userCanceled={this.userCanceled}
amIStillAlive={this.tellConfirmDialogItsStatus}
promptToUser={'使用'+this.state.inputedNum+'号码登录?'}/>
当然我们还需要在ConfirmDialog时再增加两个生命周期的函数,再组件挂载时监听返回键按下的事件,组件移除时,取消监听(貌似RN还有Bug,取消监听还不能用) //挂载时
componentDidMount() {
//这个位置并不是多余的,见后面说明
var amIStillAlive=this.props.amIStillAlive;
BackAndroid.addEventListener('ConfirmDialogListener',()=>{ if(amIStillAlive()){ this.props.userCanceled(); return true; } return false; }) } componentWillUnMount() { //RN bug 无法取消监听 BackAndroid.removeEventListener('ConfirmDialogListener'); }
BackAndroid API的工作机制是,当挂接多个Listener后,用户按下返回键时,多个Listener都会监听到返回键被按下事件,执行的顺序就是添加的顺序,不会因为前面的处理函数处理了返回事件,后面的处理函数就不会执行了。这些处理函数都执行完后,只要有一个处理函数返回了true,返回键被按下事件就不会交给Android框架处理,也就是说没办法退出。 注意 属性声明因为自定义组件时可以复用了,我们开发过程中可能一个项目组有多个人同时开发,其他同事可能会用到我们自定义的组件,但是他们使用的时候很容易忘记使用某些属性,这时候我们应该在自定义组件中声明一些属性。 export default class ConfirmDialog extends Component {
//....
}
ConfirmDialog.propTypes = {
userConfirmed: React.PropTypes.func.isRequired,userCanceled: React.PropTypes.func.isRequired,amIStillAlive: React.PropTypes.func.isRequired,promptToUser:React.PropTypes.string.isRequired
};
上面声明的属性都是 isRequired, 如果不传递这些属性程序会在开发阶段出现警告。 我们还可以给属性指定一个默认值,当没有传递该属性时使用默认值,如: ConfirmDialog.defaultProps = { promptToUser: '确定吗?'
};
同时记得要将指定 promptToUser为必须的’isRequired’ 去掉. Alert Api对话框弹出对话框是程序开发中经常需要使用的UI手段,React Native也为开发者提供了Alert API,没有特殊要求可以使用Alert Api弹出对话框,这种对话框和原生代码的对话筐样式一样,Android和IOS样式有些区别。 import {
...
Alert
} from 'react-native';
然后修改 //使用Alert Api
userPressConfirm() {
Alert.alert(
'标题','正文',[
{text:'确定',onPress:this.userConfirmed},{text:'取消',onPress:this.userCanceled,style:'cancel'},{text:'额外选项一',onPress:this.userCanceled},{text:'额外选项二',onPress:this.userCanceled}
]
);
}
Android选项最多支持3个,多余的直接忽略不会报错,IOS可以支持多个。style:’cancel’ 的选项再最后显示, 该样式对Android无效,Android第一个选项空间比较大。 IOS弹出对话框,点击对话框周围是无法取消对话框的,但是Android可以取消,如果Android想做成和ios一样的效果,需要在RN 0.33版本以上。在对话框上添加如下代码: userPressConfirm() {
Alert.alert(
'标题',onPress:this.userCanceled}
],{
cancelable: false
}
);
}
更多精彩请关注微信公众账号likeDev,公众账号名称:爱上Android。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |