• 从零学React Native之03页面导航


    之前我们介绍了RN相关的知识:
    是时候了解React Native了
    从零学React Native之01创建第一个程序
    从零学React Native之02状态机

    本篇主要介绍页面导航

    上一篇文章给大家介绍了简单界面的搭建, 这一篇我们需要两个界面, 一个是注册界面,一个是注册信息界面. 当然我们还需要一个组件去控制两个界面的切换.

    每个界面其实就一个组件 , 可以通过下面的代码抽取相关的模块

    module.exports=RegisterLeaf;

    注册界面的代码:

    主要代码

    
    import React, { Component } from 'react';
    import {
        AppRegistry, //框架提供的API
        StyleSheet,
        Text,  // 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;
    
    class RegisterLeaf extends Component {
    
        //构造函数, 组件创建的时候会执行
        constructor(props) {
            super(props); //必须有这句代码 父组件向子组件传递属性, 比如styles属性等
            // 声明状态机的初始值
            this.state = {
                inputedNum: '',
                inputedPw: ''
            };
            // ES6中使用的方法需要绑定this, 否则方法中不能使用this
            this.userPressConfirm=this.userPressConfirm.bind(this);
        }
        // 定义函数
        updateNum(newText) {
            this.setState((state)=> {
                return {
                    inputedNum: newText
                }
            });
        }
        // 定义函数
        updatePW(newText) {
            this.setState(()=> { // 用不到的参数也可以不用写
                return {
                    inputedPw: newText
                }
            });
        }
        userPressConfirm(){
            console.log("userPressConfirm");
            this.props.navigator.push({
                phoneNumber:this.state.inputedNum,
                userPW:this.state.inputedPw,
                name:'waiting'
            })
        }
        // 绘制渲染的控件
        render() {
            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开发需要加上该高度
             componentWidth,
            backgroundColor: 'gray',
            fontSize: 20
        },
        textPromptStyle: {
            top: 30,
            left: leftStartPoint,
            //  // height:30,  // IOS开发需要加上该高度 因为IOS中TextInput不会自动设置高度
             componentWidth,
            fontSize: 20
        },
        passwordInputStyle: {
            top: 50,
            left: leftStartPoint,
             componentWidth,
            backgroundColor: 'gray',
            fontSize: 20
        },
        bigTextPrompt: {
            top: 70,
            left: leftStartPoint,
             componentWidth,
            backgroundColor: 'gray',
            color: 'white',
            textAlign: 'center',//位置居中显示
            fontSize: 60
        }
    });
    module.exports=RegisterLeaf;

    这里写图片描述

    注册展示信息的代码

    import React, { Component } from 'react';
    import {
        StyleSheet,
        Text,
        View
    } from 'react-native';
    
    class WaitingLeaf extends Component {
        constructor(props) {
            super(props);
            this.goBack=this.goBack.bind(this);
        }
        render() {
            return (
                <View style={styles.container}>
                    <Text style={styles.textPromptStyle}>
                        注册使用的手机号:{this.props.phoneNumber}
                    </Text>
                    <Text style={styles.textPromptStyle}>
                        注册时使用的密码:{this.props.userPW}
                    </Text>
                    <Text style={styles.bigTextPrompt}
                          onPress={this.goBack}>
                        返回
                    </Text>
                </View>
            );
        }
        // 会调用 index.js 中模块renderScene方法 渲染RegisterLeaf
        //push函数像是Navigator组件在原来的屏幕上新放了一张纸,replace像是把原来的纸拿下来放了一张新的纸
        goBack(){
            this.props.navigator.replace({
                name:"register"
            })
        }
    }
    
    const styles = StyleSheet.create({
        container: {
            flex: 1,
            justifyContent: 'center',
            alignItems: 'center',
            backgroundColor: '#F5FCFF'
        },
        textPromptStyle: {
            fontSize: 20
        },
        bigTextPrompt: {
             300,
            backgroundColor: 'gray',
            color: 'white',
            textAlign: 'center',
            fontSize: 60
        }
    });
    
    module.exports = WaitingLeaf;

    界面效果:
    这里写图片描述

    主View 代码

    import React, { Component } from 'react';
    import {
        AppRegistry, //框架提供的API
        Navigator,
        BackAndroid
    } from 'react-native';
    let RegisterLeaf = require('./RegisterLeaf');
    let WaitingLeaf = require('./WaitingLeaf');
    
    class NaviModule extends Component {
        constructor(props){
            super(props);
            //返回一个绑定好this的方法并存储于当前实例中
            this.handleBack=this.handleBack.bind(this);
        }
        //告知Navigator 模块切换时的效果, 名字要和Navigator组件的configureScene保持一致
        configureScene(route) {
            return Navigator.SceneConfigs.FadeAndroid;
        }
    
        // 告知Navigator模块我们希望如何挂接当前的视图 函数名保持一致
        renderScene(router, navigator) {
            console.log("renderScene");
           Navigator.navigator = navigator;
            switch (router.name) {
                case "register":  // 当声明路径为register 加载 RegisterLeaf模块
                    return < RegisterLeaf navigator={navigator}/>;
                case "waiting": //当声明路径为Waiting 加载 Waiting模块
                    console.log("waiting");
                    return < WaitingLeaf phoneNumber={router.phoneNumber}
                                         userPW={router.userPW}
                                         navigator={navigator}/>
            }
        }
    
        // 挂载的时候调用
        componentDidMount() {
            console.log("componentDidMount");
            //监听Android 返回键按下时,判断导航的路径长度是否大于1 大于1 表示当前视图下最少还有一个视图
            BackAndroid.addEventListener('hardwareBackPress',this.handleBack);
        }
        handleBack(){
            const navigator = Navigator.navigator;
            console.log(navigator);
            if (navigator && navigator.getCurrentRoutes().length > 1) {
                navigator.pop(); // 返回上一个视图
                return true;  // 返回true 表示已经处理了事件
            }
            return false;
        }
        // 移除的时候调用
        componentWillUnMount() {
            console.log("componentWillUnMount");
            //清除组件被挂载接时设置的后退事件监听器
            BackAndroid.removeEventListener('hardwareBackPress',this.handleBack);
    
        }
    
        render() {
            return (
                <Navigator
                    initialRoute={{name:'register'}}
                    configureScene={this.configureScene}
                    renderScene={this.renderScene}/>
            );
        }
    }
    
    // 注册了 NaviModule 组件
    AppRegistry.registerComponent('AwesomeProject', () => NaviModule);

    Navigator组件中configureScene函数的用途是告知Navigator模块我们希望在视图转换时使用何种效果。
    renderScene函数用来告知Navigator模块我们希望如何挂接当前的视图.
    componentDidMount和 componentWillUnmount函数是React框架的两个生命周期的函数,NaviModule组件实现了这两个函数后,当这个组件被挂接到当前页面或者被移除时,这两个函数会被调用.

    绘制原理

    当navigator的push函数(或者replace函数,区别见代码注释) 被调用后,通过React Native的Navigator组件的工作机制,NaviModule的renderScene函数将被调用,并且push函数传入的变量成为renderScene函数的第一个参数.
    当name等于 waiting时, 将WaitingLeaf模块挂接上去. 当name等于register时, RegisterLeaf模块被挂载上去.

    属性总结

    本节对属性做一个总结, 属性用于React Native组件外界向组件内传递变量或者数据.通过this.props属性来访问. 当属性的取值在组件外界被改变时,如果属性被组件中的UI以某种方式显示出来,则显示也会相应的发生改变. 在React Native组件内部,我们不可用对属性进行赋值操作,这种做法会导致运行时出错.

    参考文章:

    新手理解Navigator的教程
    ReactNative的Navigator组件使用方式
    React Native–导航

    更多精彩请关注微信公众账号likeDev,公众账号名称:爱上Android。
    这里写图片描述

  • 相关阅读:
    算法笔记--二分图判定
    算法笔记--最小表示法
    Codeforces 525A
    Codeforces 140D
    Codeforces C
    Codeforces 665C
    Codeforces 604B
    Codeforces 285C
    The Fewest Coins POJ
    Triangular Pastures POJ
  • 原文地址:https://www.cnblogs.com/hehe520/p/6329899.html
Copyright © 2020-2023  润新知