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

在现阶段我们的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

[javascript]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. import React, { Component } from 'react';  
  2. import {  
  3.   StyleSheet,  
  4.   View,  
  5.   Text,  
  6.   Image,  
  7.   TouchableOpacity,  
  8.   Platform,  
  9.   NativeModules,  
  10. } from 'react-native';  
  11. const {CommonDispatcher} = NativeModules;  
  12.   
  13. //通用导航组件  
  14. export default class Nav extends Component {  
  15.   
  16.   constructor(props) {  
  17.     super(props);  
  18.     height = (Platform.OS === 'ios') ? 64 : 45;  
  19.     leftWidth = 60;  
  20.     rightWidth = 60;  
  21.   }  
  22.   
  23.   //控制返回事件,navigator返回 或 返回到原生页面  
  24.   back() {  
  25.     const { navigator } = this.props;  
  26.     if(navigator) {  
  27.       const routers = navigator.getCurrentRoutes();  
  28.       if (routers.length >1) {  
  29.         navigator.pop();  
  30.       }else{  
  31.         //此处为桥接,需要finish 掉RN壳,跳转到原生页面  
  32.         CommonDispatcher.toBack({});  
  33.       }  
  34.     }  
  35.   }  
  36.   
  37.   //左上角事件  
  38.   leftCallBack() {  
  39.     if (this.props.leftCallBack) {  
  40.       this.props.leftCallBack();  
  41.     }else {  
  42.       this.back();  
  43.     }  
  44.   }  
  45.   
  46.   //右上角事件  
  47.   rightCallBack(){  
  48.       if (this.props.rightCallBack) {  
  49.           this.props.rightCallBack();  
  50.       }  
  51.   }  
  52.   
  53.   render() {  
  54.     //左边返回图片可隐藏  
  55.     let leftView = this.props.hiddenBack ?  
  56.      <View style={styles.leftView} />  
  57.     :(  
  58.       <TouchableOpacity onPress={this.leftCallBack.bind(this)}>  
  59.         <View style={styles.leftView}>  
  60.           <Image source={{uri:"nav_back"}} style={styles.image}/>  
  61.         </View>  
  62.       </TouchableOpacity>);  
  63.   
  64.     //标题现在只支持文本,样式后续也可支持修改  
  65.     let centerView = <Text style={styles.title}>{this.props.title}</Text>;  
  66.   
  67.     //右上角区域目前只支持文本,后续可支持图片或图文  
  68.     let rightView = (  
  69.         <TouchableOpacity onPress={this.rightCallBack.bind(this)}>  
  70.           <Text style={styles.rightTitle}>{this.props.rightTitle}</Text>  
  71.         </TouchableOpacity>);  
  72.   
  73.     return (  
  74.       <View style={styles.container} height={height}  backgroundColor={this.props.backgroundColor}>  
  75.        <View style={styles.leftView}  width={leftWidth} >{leftView}</View>  
  76.        <View style={styles.centerView} >{centerView}</View>  
  77.        <View style={styles.rightView} width={rightWidth} >{rightView}</View>  
  78.       </View>  
  79.     );  
  80.   }  
  81. }  
  82.   
  83. const styles = StyleSheet.create({  
  84.   container: {  
  85.     justifyContent:'space-between',  
  86.     flexDirection:'row',  
  87.     alignItems:'center',  
  88.     paddingTop:(Platform.OS === 'ios') ? 20 : 0,  
  89.   },  
  90.   leftView:{  
  91.       flexDirection:'row',  
  92.       alignItems:'center',  
  93.   },  
  94.   rightView:{  
  95.       flexDirection:'row',  
  96.       alignItems:'center',  
  97.       justifyContent:'flex-end',  
  98.   },  
  99.   centerView:{  
  100.       flex:1,  
  101.       flexDirection:'row',  
  102.       alignItems:'center',  
  103.       justifyContent:'center',  
  104.   },  
  105.   image: {  
  106.     marginLeft:20,  
  107.     width:15,  
  108.     height:15,  
  109.   },  
  110.   title: {  
  111.     fontSize:17,  
  112.     color:'#ffffff',  
  113.   },  
  114.   rightTitle: {  
  115.     marginRight:15,  
  116.     color:'white'  
  117.   },  
  118. });  


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

[javascript]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. 'use strict';  
  2.   
  3. import React, { Component } from 'react';  
  4. import {  
  5.   Platform,  
  6.   Navigator,  
  7.   BackAndroid,  
  8.   NativeModules,  
  9.   View,  
  10.   Text,  
  11.   AppRegistry,  
  12.   TouchableOpacity,  
  13. } from 'react-native';  
  14.   
  15. import Nav from './Nav.js';  
  16. import DetailPage from './DetailPage';  
  17. const {CommonDispatcher} = NativeModules;  
  18.   
  19. export default class PageIndex extends Component {  
  20.   constructor(props) {  
  21.     super(props);  
  22.   }  
  23.   
  24.   componentWillMount() {  
  25.     if (Platform.OS === 'android') {  
  26.       //监听安卓物理按键返回  
  27.       BackAndroid.addEventListener('hardwareBackPress'this.onBackAndroid);  
  28.     }  
  29.   }  
  30.   
  31.   componentWillUnmount() {  
  32.     if (Platform.OS === 'android') {  
  33.       BackAndroid.removeEventListener('hardwareBackPress'this.onBackAndroid);  
  34.     }  
  35.   }  
  36.   
  37.   //处理安卓物理back键  
  38.   onBackAndroid = () => {  
  39.     let nav = this.navigator;  
  40.     let routers = nav.getCurrentRoutes();  
  41.     // 当前页面不为root页面时的处理  
  42.     if (routers.length >1) {  
  43.       let top = routers[routers.length - 1];  
  44.       let handleBack = top.handleBack;  
  45.       if (handleBack) {  
  46.         // 路由或组件上决定这个界面自行处理back键  
  47.         handleBack();  
  48.         return true;  
  49.       }  
  50.       // 默认处理  
  51.       nav.pop();  
  52.       return true;  
  53.     }  
  54.     return false;  
  55.   };  
  56.   
  57.   render() {  
  58.     return (  
  59.       <Navigator  
  60.         ref={ nav => { this.navigator = nav; }}  
  61.         initialRoute={{ name: "HomePage", component: HomePage }}  
  62.         configureScene={(route) => {  
  63.           return Navigator.SceneConfigs.PushFromRight;  
  64.         }}  
  65.         renderScene={(route, navigator) => {  
  66.           let Component = route.component;  
  67.           return <Component {...route.params} navigator={navigator} />  
  68.         }} />  
  69.     );  
  70.   }  
  71. }  
  72.   
  73. //这是一个使用了通用导航的测试页面  
  74. class HomePage extends Component {  
  75.   
  76.   toDetailPage(){  
  77.     const { navigator } = this.props;  
  78.     if(navigator) {  
  79.         navigator.push({  
  80.             name: 'DetailPage',  
  81.             component: DetailPage,  
  82.             params:{  
  83.               rightTitle:"分享"  
  84.             }  
  85.         })  
  86.     }  
  87.   }  
  88.   render(){  
  89.     return (  
  90.         <View style={{flex:1}}>  
  91.           <Nav {...this.props} ref='nav'  title='通用导航Home' backgroundColor='#e6454a'/>  
  92.           <TouchableOpacity onPress={this.toDetailPage.bind(this)} style={{backgroundColor:'#f2f2f2',marginTop:20,justifyContent:'center',alignItems:'center',}}>  
  93.             <Text style={{fontSize:28,color:'#998462',textAlign:'center',}}>跳转详情</Text>  
  94.           </TouchableOpacity>  
  95.         </View>  
  96.     );  
  97.   }  
  98. }  
  99.   
  100. AppRegistry.registerComponent('你自己的模块名', () => PageIndex);  



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

[javascript]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. 'use strict';  
  2. import React, { Component } from 'react';  
  3. import {  
  4.   View,  
  5.   Text,  
  6. } from 'react-native';  
  7. import Nav from './Nav.js';  
  8.   
  9. export default class DetailPage extends Component {  
  10.   constructor(props) {  
  11.       super(props);  
  12.       let navigator = this.props.navigator;  
  13.       if (navigator) {  
  14.         let routes = navigator.getCurrentRoutes(); //nav是导航器对象  
  15.         let lastRoute = routes[routes.length - 1]; // 当前页面对应的route对象  
  16.         lastRoute.handleBack = this.leftCallBack.bind(this);//设置route对象的hanleBack属性  
  17.       }  
  18.   }  
  19.   
  20.   /** 
  21.    * 场景:编辑页面,点击物理或左上角返回,需要提示“确定放弃修改吗?” 
  22.    */  
  23.   leftCallBack(){  
  24.     let logic = false;//你可以修改为true  
  25.     if(logic){  
  26.       alert("我不想返回");  
  27.     }else{  
  28.       this.refs.nav.back();  
  29.     }  
  30.   }  
  31.   render(){  
  32.     return (  
  33.         <View style={{flex:1}}>  
  34.           <Nav {...this.props} ref='nav' leftCallBack={this.leftCallBack.bind(this)}  rightCallBack={()=>{alert('分享')}} title='通用导航Detail' backgroundColor='#e6454a'/>  
  35.           <View style={{flex:1,backgroundColor:'#f2f2f2',justifyContent:'center',alignItems:'center',}}>  
  36.             <Text style={{fontSize:28,color:'#998462',textAlign:'center',}}>我只是容器里的一个RN页面</Text>  
  37.           </View>  
  38.         </View>  
  39.     );  
  40.   }  
  41. }  


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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值