专栏首页向治洪React Native 自定义控件之验证码和Toast

React Native 自定义控件之验证码和Toast

React Native通过近两年的迭代和维护,最新版本已经到了0.45.1。

话说回来,尽管迭代的挺快,但还是有很多坑,很多基础的组件和API还是不完善。

今天给大家带来的自定义小专题,其实对于React Native来说,自定义组件的过程更像是Android、iOS的组合控件。大体步骤有如下几个步骤(不完全准确,但是方向大体准确): 1,定义构造函数constructor; 2,定义组件属性propTypes; 3,绘制界面; 4,添加更新界面逻辑等

自定义Toast

在系统组件中,RN为我们提供了ToastAndroid组件,但是对于iOS好像并没有直接提供,这时候我们就想到了自定义控件了。如下图所示:

我们之前讲过Animated组件,这个组件可以实现渐变,缩放,旋转等动画效果,在这里,我们可以用它来实现Toast的功能。比如,显示两秒后消失,为了对显示的位置进行设置,我们还可以设置显示的位置,所以绘制render的代码如下:

render() {
        let top;
        switch (this.props.position){
            case 'top':
                top=160;
                break;
            case 'center':
                top=height /2;
                break;
            case 'bottom':
                top=height - 160;
                break;
        }
        let view = this.state.isShow ?
            <View
                style={[styles.container,{top:top}]}
                pointerEvents="none"
            >
                <Animated.View
                    style={[styles.content,{opacity:this.state.opacityValue}]}
                >
                    <Text style={styles.text}>{this.state.text}</Text>
                </Animated.View>
            </View> : null;
        return view;
    }

显示时长控制方法:

show(text, duration) {
        if(duration>=DURATION.LENGTH_LONG){
            this.duration=DURATION.LENGTH_LONG;
        }else {
            this.duration=DURATION.LENGTH_SHORT;
        }
        this.setState({
            isShow: true,
            text: text,
        });
        this.isShow=true;
        this.state.opacityValue.setValue(OPACITY)
        this.close();
    }

完整代码:

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 * @flow
 */

import React, {Component,PropTypes} from 'react';
import {
    StyleSheet,
    View,
    Animated,
    Dimensions,
    Text,
} from 'react-native'
export const DURATION = {LENGTH_LONG: 2000, LENGTH_SHORT: 500};
const {height, width} = Dimensions.get('window');
const OPACITY=0.6;

const dismissKeyboard = require('dismissKeyboard')

export default class ToastUtil extends Component {
    static propTypes = {
        position: PropTypes.oneOf([
            'top',
            'center',
            'bottom',
        ]),
    }
    static defaultProps = {
        position:'center',
    }
    constructor(props) {
        super(props);
        this.state = {
            isShow: false,
            text: '',
            opacityValue:new Animated.Value(OPACITY),
        }
    }
    show(text, duration) {
        if(duration>=DURATION.LENGTH_LONG){
            this.duration=DURATION.LENGTH_LONG;
        }else {
            this.duration=DURATION.LENGTH_SHORT;
        }
        this.setState({
            isShow: true,
            text: text,
        });
        this.isShow=true;
        this.state.opacityValue.setValue(OPACITY)
        this.close();
    }

    close() {
        if(!this.isShow)return;
        this.timer && clearTimeout(this.timer);
        this.timer = setTimeout(() => {
            Animated.timing(
                this.state.opacityValue,
                {
                    toValue: 0.0,
                    duration:1000,
                }
            ).start(()=>{
                this.setState({
                    isShow: false,
                });
                this.isShow=false;
            });
        }, this.duration);
    }
    componentWillUnmount() {
        this.timer && clearTimeout(this.timer);
    }

    render() {
        let top;
        switch (this.props.position){
            case 'top':
                top=160;
                break;
            case 'center':
                top=height /2;
                break;
            case 'bottom':
                top=height - 160;
                break;
        }
        let view = this.state.isShow ?
            <View
                style={[styles.container,{top:top}]}
                pointerEvents="none"
            >
                <Animated.View
                    style={[styles.content,{opacity:this.state.opacityValue}]}
                >
                    <Text style={styles.text}>{this.state.text}</Text>
                </Animated.View>
            </View> : null;
        return view;
    }
}
const styles = StyleSheet.create({
    container: {
        position: 'absolute',
        left: 0,
        right: 0,
        alignItems: 'center',
    },
    content: {
        backgroundColor: 'black',
        opacity: OPACITY,
        borderRadius: 5,
        padding: 10,
    },
    text:{
        color:'white'
    },
})

如何使用:

 <Toast ref="toast"/>
 //省略...
 <Text style={styles.styleText} onPress={()=>{
                    this.refs.toast.show('你点击了忘记密码!',3000);}}>
  忘记密码?
 </Text>
 //省略...

获取验证码

在很多应用开发中都会涉及到获取手机验证码的场景,例如登录或者注册获取验证码。如下图:

那么按照自定义组件的流程,先添加构造函数,并定义必须的一些字段(相关属性),并完成初始化:

static propTypes = {
        style: PropTypes.object,//style属性
        textStyle: Text.propTypes.style,//文本文字
        onClick: PropTypes.func,//点击事件
        disableColor: PropTypes.string,//倒计时过程中颜色
        timerTitle: PropTypes.string,//倒计时文本
        enable: React.PropTypes.oneOfType([React.PropTypes.bool,React.PropTypes.number])
    };

2,构造函数:

constructor(props) {
        super(props)
        this.state = {
            timerCount: this.props.timerCount || 60,//默认倒计时时间
            timerTitle: this.props.timerTitle || '获取验证码',
            counting: false,
            selfEnable: true,
        };
        this.shouldStartCountting = this.shouldStartCountting.bind(this)
        this.countDownAction = this.countDownAction.bind(this)
    }

3,添加绘制界面代码:

render() {
        const {onClick, style, textStyle, disableColor} = this.props;
        const {counting, timerTitle, selfEnable} = this.state;
        return (
            <TouchableOpacity activeOpacity={counting ? 1 : 0.8} onPress={() => {
                if (!counting &&selfEnable) {
                    this.setState({selfEnable: false});
                    this.shouldStartCountting(true);
                };
            }}>
                <View
                    style={styles.styleCodeView}>
                    <Text
                        style={[{fontSize: 12}, textStyle, {color: ((!counting && selfEnable) ? textStyle.color : disableColor || 'gray')}]}>{timerTitle}</Text>
                </View>
            </TouchableOpacity>
        )
    }

4,添加逻辑代码:

shouldStartCountting(shouldStart) {
        if (this.state.counting) {
            return
        }
        if (shouldStart) {
            this.countDownAction()
            this.setState({counting: true, selfEnable: false})
        } else {
            this.setState({selfEnable: true})
        }
    }

//倒计时逻辑
countDownAction() {
        const codeTime = this.state.timerCount;
        this.interval = setInterval(() => {
            const timer = this.state.timerCount - 1
            if (timer === 0) {
                this.interval && clearInterval(this.interval);
                this.setState({
                    timerCount: codeTime,
                    timerTitle: this.props.timerTitle || '获取验证码',
                    counting: false,
                    selfEnable: true
                })
            } else {
                this.setState({
                    timerCount: timer,
                    timerTitle: `重新获取(${timer}s)`,
                })
            }
        }, 1000)
    }

说明: shouldStartCountting:回调函数,接受一个Bool类型的参数 1,shouldStartCountting(true),开始倒计时,倒计时结束时自动恢复初始状态 2,shouldStartCountting(false), 按钮的selfEnable会立即被置为true 所以,获取验证码的完整代码如下:

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 * @flow
 */

import React, {Component,PropTypes} from 'react';
import {
    Text,
    StyleSheet,
    View,
    TouchableOpacity,
} from 'react-native';

var Dimensions = require('Dimensions');
var screenWidth = Dimensions.get('window').width;

export default  class TimerButton extends Component {

    constructor(props) {
        super(props)
        this.state = {
            timerCount: this.props.timerCount || 60,
            timerTitle: this.props.timerTitle || '获取验证码',
            counting: false,
            selfEnable: true,
        };
        this.shouldStartCountting = this.shouldStartCountting.bind(this)
        this.countDownAction = this.countDownAction.bind(this)
    }

    static propTypes = {
        style: PropTypes.object,
        textStyle: Text.propTypes.style,
        onClick: PropTypes.func,
        disableColor: PropTypes.string,
        timerTitle: PropTypes.string,
        enable: React.PropTypes.oneOfType([React.PropTypes.bool,React.PropTypes.number])
    };

    countDownAction() {
        const codeTime = this.state.timerCount;
        this.interval = setInterval(() => {
            const timer = this.state.timerCount - 1
            if (timer === 0) {
                this.interval && clearInterval(this.interval);
                this.setState({
                    timerCount: codeTime,
                    timerTitle: this.props.timerTitle || '获取验证码',
                    counting: false,
                    selfEnable: true
                })
            } else {
                this.setState({
                    timerCount: timer,
                    timerTitle: `重新获取(${timer}s)`,
                })
            }
        }, 1000)
    }

    shouldStartCountting(shouldStart) {
        if (this.state.counting) {
            return
        }
        if (shouldStart) {
            this.countDownAction()
            this.setState({counting: true, selfEnable: false})
        } else {
            this.setState({selfEnable: true})
        }
    }

    componentWillUnmount() {
        clearInterval(this.interval)
    }

    render() {
        const {onClick, style, textStyle, disableColor} = this.props;
        const {counting, timerTitle, selfEnable} = this.state;
        return (
            <TouchableOpacity activeOpacity={counting ? 1 : 0.8} onPress={() => {
                if (!counting &&selfEnable) {
                    this.setState({selfEnable: false});
                    this.shouldStartCountting(true);
                };
            }}>
                <View
                    style={styles.styleCodeView}>
                    <Text
                        style={[{fontSize: 12}, textStyle, {color: ((!counting && selfEnable) ? textStyle.color : disableColor || 'gray')}]}>{timerTitle}</Text>
                </View>
            </TouchableOpacity>
        )
    }
}

const styles = StyleSheet.create({
    container: {
        flex: 1,
        marginTop: 20
    },
    styleCodeView: {
        height: 28,
        width: screenWidth*0.22,
        borderColor: '#dc1466',
        borderWidth: 1,
        borderRadius: 5,
        justifyContent: 'center',
        alignItems: 'center',
    },
    styleTextCode: {
        fontSize: 12,
        color: '#dc1466',
        textAlign: 'center',
    },

});

如何使用?

import TimerButton from './TimerButton'

var Dimensions = require('Dimensions');
var screenWidth = Dimensions.get('window').width;

//省略...
<TimerButton
 style={{width: screenWidth*0.2,marginRight: 10}}
 timerCount={60}
 textStyle={{color: '#dc1466'}}
 onclick={(start)=>{
 }}/>  

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • React Native 自定义控件专题

    React Native通过近两年的迭代和维护,最新版本已经到了0.45.1,关于最新版本的介绍请查看我之前的博客:0.45新特性。话说回来,尽管迭代的挺快,但...

    xiangzhihong
  • react native实现上拉加载下拉刷新

    前言 我们在做原生app开发的时候,很多场景都会用到下拉刷新、上拉加载的操作,Android中如PullToRefreshListView,ios中如MJRef...

    xiangzhihong
  • android断点下载

    断点下载往往用在大文件的下载过程中,如传统的迅雷下载用的就是断点下载技术,说起来原理比较简单:对文件进行分片,并对分片的文件进行标记,然后分片下载,下载完成后对...

    xiangzhihong
  • 实现一个同步的RenderApplication

    Nebula3 Sep2008 SDK之后渲染改成异步的了, 对于想直接操作内部API接口的我来说, 没法直接下手了

    逍遥剑客
  • Openlayers2卷帘功能的实现

    在WebGIS开发中,经常会有用户提需求,要实现卷帘功能,卷帘功能主要是实现两张图之间的对比。在前文中,讲到了openlayers3以及Arcgis for j...

    lzugis
  • vue 中使用threejs

    tianyawhl
  • Flutter开发:TextField常用属性的使用

    在flutter开发过程中,掌握常用组件的使用是必备技能,flutter常用的组件和App开发时候常用的控件基本一模一样,只是使用的方式不一样罢了。

    三掌柜
  • TRTC学习之旅(三)-- 使用vue+ts集成互动直播

    上次我们已经用vue+ts实现了多人会议室的搭建,这次我们继续在上次项目的基础上,实现互动直播功能。

    黑眼圈云豆
  • 冬天到了,分享两款雪花特效代码

    小小鱼儿小小林
  • ActiveMQ源码分析——生产消息

    创建Session时,第一个传入是否开启事务,第二个传入session提交消费消息的方式 接下来看源码处理,生产者id对象由当前sessionID加上使用内部s...

    歪歪梯

扫码关注云+社区

领取腾讯云代金券