美文网首页react-native开发React-Native程序员
《React-Native系列》43、通用容器和导航设计方案

《React-Native系列》43、通用容器和导航设计方案

作者: coder_小刀 | 来源:发表于2016-11-23 23:52 被阅读374次

    原文发布于CSDN,地址:查看原文

    在现阶段我们的RN实践都是基于已发布过的APP,譬如将从某个入口进入的子模块都替换成RN页面。那么我们可以将这个子模块设计成一个通用RN容器,所有的RN页面都在RN容器内部跳转。

    RN容器在iOS使用UIViewController、Android使用Activity或者Fragment,加载bundle文件,正常情况下,一个模块只有一个bundle文件。
    要实现页面的跳转,我们可以使用Navigator组件,具体使用可以参考:http://blog.csdn.net/codetomylaw/article/details/52059493
    还有几个问题需要解决:
    1、导航栏
    在原生App中导航栏通常是统一管理的,那么在通用容器中,我们可以定义一个通用的RN导航。
    2、Native跳转RN容器
    使用正常的Native跳转方式即可,譬如在Android中 startActivity 。
    3、RN容器返回Native界面
    由于导航栏已经是RN界面编写的,那么Native端就需要提供一个桥接的方法给RN调用,桥接方法需要实现的逻辑:finish掉初始化的RN容器
    4、处理安卓系统返回键
    详细见Demo代码

    好,我们通过一个简单的Demo来演示。

    我们实现的效果是
    1、从Native页面跳转RN页面A,RN页面A是由RN容器加载,点击左上角可以返回到Native界面
    2、点击RN页面A中的“跳转详情”可以跳转到RN页面B
    3、点击RN页面B中的左上角或安卓物理返回键,可以返回到RN页面A
    4、点击RN页面B中的左上角或安卓物理返回键,可以阻断页面的返回,实现我们自己的逻辑
    5、点击RN页面B中的分享,可以调用回调
    页面A如下图:


    页面B如下图:


    导航组件代码如下:Nav.js

    import React, { Component } from 'react';
    import {
      StyleSheet,
      View,
      Text,
      Image,
      TouchableOpacity,
      Platform,
      NativeModules,
    } from 'react-native';
    const {CommonDispatcher} = NativeModules;
    
    //通用导航组件
    export default class Nav extends Component {
    
      constructor(props) {
        super(props);
        height = (Platform.OS === 'ios') ? 64 : 45;
        leftWidth = 60;
        rightWidth = 60;
      }
    
      //控制返回事件,navigator返回 或 返回到原生页面
      back() {
        const { navigator } = this.props;
        if(navigator) {
          const routers = navigator.getCurrentRoutes();
          if (routers.length >1) {
            navigator.pop();
          }else{
            //此处为桥接,需要finish 掉RN壳,跳转到原生页面
            CommonDispatcher.toBack({});
          }
        }
      }
    
      //左上角事件
      leftCallBack() {
        if (this.props.leftCallBack) {
          this.props.leftCallBack();
        }else {
          this.back();
        }
      }
    
      //右上角事件
      rightCallBack(){
          if (this.props.rightCallBack) {
              this.props.rightCallBack();
          }
      }
    
      render() {
        //左边返回图片可隐藏
        let leftView = this.props.hiddenBack ?
         <View style={styles.leftView} />
        :(
          <TouchableOpacity onPress={this.leftCallBack.bind(this)}>
            <View style={styles.leftView}>
              <Image source={{uri:"nav_back"}} style={styles.image}/>
            </View>
          </TouchableOpacity>);
    
        //标题现在只支持文本,样式后续也可支持修改
        let centerView = <Text style={styles.title}>{this.props.title}</Text>;
    
        //右上角区域目前只支持文本,后续可支持图片或图文
        let rightView = (
            <TouchableOpacity onPress={this.rightCallBack.bind(this)}>
              <Text style={styles.rightTitle}>{this.props.rightTitle}</Text>
            </TouchableOpacity>);
    
        return (
          <View style={styles.container} height={height}  backgroundColor={this.props.backgroundColor}>
           <View style={styles.leftView}  width={leftWidth} >{leftView}</View>
           <View style={styles.centerView} >{centerView}</View>
           <View style={styles.rightView} width={rightWidth} >{rightView}</View>
          </View>
        );
      }
    }
    
    const styles = StyleSheet.create({
      container: {
        justifyContent:'space-between',
        flexDirection:'row',
        alignItems:'center',
        paddingTop:(Platform.OS === 'ios') ? 20 : 0,
      },
      leftView:{
          flexDirection:'row',
          alignItems:'center',
      },
      rightView:{
          flexDirection:'row',
          alignItems:'center',
          justifyContent:'flex-end',
      },
      centerView:{
          flex:1,
          flexDirection:'row',
          alignItems:'center',
          justifyContent:'center',
      },
      image: {
        marginLeft:20,
        width:15,
        height:15,
      },
      title: {
        fontSize:17,
        color:'#ffffff',
      },
      rightTitle: {
        marginRight:15,
        color:'white'
      },
    });
    

    容器组件代码如下(HomePage也就是RN页面A):page.js

    'use strict';
    
    import React, { Component } from 'react';
    import {
      Platform,
      Navigator,
      BackAndroid,
      NativeModules,
      View,
      Text,
      AppRegistry,
      TouchableOpacity,
    } from 'react-native';
    
    import Nav from './Nav.js';
    import DetailPage from './DetailPage';
    const {CommonDispatcher} = NativeModules;
    
    export default class PageIndex extends Component {
      constructor(props) {
        super(props);
      }
    
      componentWillMount() {
        if (Platform.OS === 'android') {
          //监听安卓物理按键返回
          BackAndroid.addEventListener('hardwareBackPress', this.onBackAndroid);
        }
      }
    
      componentWillUnmount() {
        if (Platform.OS === 'android') {
          BackAndroid.removeEventListener('hardwareBackPress', this.onBackAndroid);
        }
      }
    
      //处理安卓物理back键
      onBackAndroid = () => {
        let nav = this.navigator;
        let routers = nav.getCurrentRoutes();
        // 当前页面不为root页面时的处理
        if (routers.length >1) {
          let top = routers[routers.length - 1];
          let handleBack = top.handleBack;
          if (handleBack) {
            // 路由或组件上决定这个界面自行处理back键
            handleBack();
            return true;
          }
          // 默认处理
          nav.pop();
          return true;
        }
        return false;
      };
    
      render() {
        return (
          <Navigator
            ref={ nav => { this.navigator = nav; }}
            initialRoute={{ name: "HomePage", component: HomePage }}
            configureScene={(route) => {
              return Navigator.SceneConfigs.PushFromRight;
            }}
            renderScene={(route, navigator) => {
              let Component = route.component;
              return <Component {...route.params} navigator={navigator} />
            }} />
        );
      }
    }
    
    //这是一个使用了通用导航的测试页面
    class HomePage extends Component {
    
      toDetailPage(){
        const { navigator } = this.props;
        if(navigator) {
            navigator.push({
                name: 'DetailPage',
                component: DetailPage,
                params:{
                  rightTitle:"分享"
                }
            })
        }
      }
      render(){
        return (
            <View style={{flex:1}}>
              <Nav {...this.props} ref='nav'  title='通用导航Home' backgroundColor='#e6454a'/>
              <TouchableOpacity onPress={this.toDetailPage.bind(this)} style={{backgroundColor:'#f2f2f2',marginTop:20,justifyContent:'center',alignItems:'center',}}>
                <Text style={{fontSize:28,color:'#998462',textAlign:'center',}}>跳转详情</Text>
              </TouchableOpacity>
            </View>
        );
      }
    }
    
    AppRegistry.registerComponent('你自己的模块名', () => PageIndex);
    

    RN页面B代码如下:DetailPage.js

    'use strict';
    import React, { Component } from 'react';
    import {
      View,
      Text,
    } from 'react-native';
    import Nav from './Nav.js';
    
    export default class DetailPage extends Component {
      constructor(props) {
          super(props);
          let navigator = this.props.navigator;
          if (navigator) {
            let routes = navigator.getCurrentRoutes(); //nav是导航器对象
            let lastRoute = routes[routes.length - 1]; // 当前页面对应的route对象
            lastRoute.handleBack = this.leftCallBack.bind(this);//设置route对象的hanleBack属性
          }
      }
    
      /**
       * 场景:编辑页面,点击物理或左上角返回,需要提示“确定放弃修改吗?”
       */
      leftCallBack(){
        let logic = false;//你可以修改为true
        if(logic){
          alert("我不想返回");
        }else{
          this.refs.nav.back();
        }
      }
      render(){
        return (
            <View style={{flex:1}}>
              <Nav {...this.props} ref='nav' leftCallBack={this.leftCallBack.bind(this)}  rightCallBack={()=>{alert('分享')}} title='通用导航Detail' backgroundColor='#e6454a'/>
              <View style={{flex:1,backgroundColor:'#f2f2f2',justifyContent:'center',alignItems:'center',}}>
                <Text style={{fontSize:28,color:'#998462',textAlign:'center',}}>我只是容器里的一个RN页面</Text>
              </View>
            </View>
        );
      }
    }
    

    好,这样就基本实现了通用的RN容器和导航,当然还有一些地方可以优化。

    《React-Native系列》前42篇博文见http://www.jianshu.com/p/34ef5d19ea12

    相关文章

      网友评论

        本文标题:《React-Native系列》43、通用容器和导航设计方案

        本文链接:https://www.haomeiwen.com/subject/cjfbpttx.html