《React-Native系列》43、通用容器和導(dǎo)航設(shè)計(jì)方案

原文發(fā)布于CSDN,地址:查看原文

在現(xiàn)階段我們的RN實(shí)踐都是基于已發(fā)布過的APP,譬如將從某個(gè)入口進(jìn)入的子模塊都替換成RN頁面。那么我們可以將這個(gè)子模塊設(shè)計(jì)成一個(gè)通用RN容器,所有的RN頁面都在RN容器內(nèi)部跳轉(zhuǎn)。

RN容器在iOS使用UIViewController、Android使用Activity或者Fragment,加載bundle文件,正常情況下,一個(gè)模塊只有一個(gè)bundle文件。
要實(shí)現(xiàn)頁面的跳轉(zhuǎn),我們可以使用Navigator組件,具體使用可以參考:http://blog.csdn.net/codetomylaw/article/details/52059493
還有幾個(gè)問題需要解決:
1、導(dǎo)航欄
在原生App中導(dǎo)航欄通常是統(tǒng)一管理的,那么在通用容器中,我們可以定義一個(gè)通用的RN導(dǎo)航。
2、Native跳轉(zhuǎn)RN容器
使用正常的Native跳轉(zhuǎn)方式即可,譬如在Android中 startActivity 。
3、RN容器返回Native界面
由于導(dǎo)航欄已經(jīng)是RN界面編寫的,那么Native端就需要提供一個(gè)橋接的方法給RN調(diào)用,橋接方法需要實(shí)現(xiàn)的邏輯:finish掉初始化的RN容器
4、處理安卓系統(tǒng)返回鍵
詳細(xì)見Demo代碼

好,我們通過一個(gè)簡單的Demo來演示。

我們實(shí)現(xiàn)的效果是
1、從Native頁面跳轉(zhuǎn)RN頁面A,RN頁面A是由RN容器加載,點(diǎn)擊左上角可以返回到Native界面
2、點(diǎn)擊RN頁面A中的“跳轉(zhuǎn)詳情”可以跳轉(zhuǎn)到RN頁面B
3、點(diǎn)擊RN頁面B中的左上角或安卓物理返回鍵,可以返回到RN頁面A
4、點(diǎn)擊RN頁面B中的左上角或安卓物理返回鍵,可以阻斷頁面的返回,實(shí)現(xiàn)我們自己的邏輯
5、點(diǎn)擊RN頁面B中的分享,可以調(diào)用回調(diào)
頁面A如下圖:


頁面B如下圖:


導(dǎo)航組件代碼如下:Nav.js

import React, { Component } from 'react';
import {
  StyleSheet,
  View,
  Text,
  Image,
  TouchableOpacity,
  Platform,
  NativeModules,
} from 'react-native';
const {CommonDispatcher} = NativeModules;

//通用導(dǎo)航組件
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殼,跳轉(zhuǎn)到原生頁面
        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>);

    //標(biāo)題現(xiàn)在只支持文本,樣式后續(xù)也可支持修改
    let centerView = <Text style={styles.title}>{this.props.title}</Text>;

    //右上角區(qū)域目前只支持文本,后續(xù)可支持圖片或圖文
    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') {
      //監(jiān)聽安卓物理按鍵返回
      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();
    // 當(dāng)前頁面不為root頁面時(shí)的處理
    if (routers.length >1) {
      let top = routers[routers.length - 1];
      let handleBack = top.handleBack;
      if (handleBack) {
        // 路由或組件上決定這個(gè)界面自行處理back鍵
        handleBack();
        return true;
      }
      // 默認(rèn)處理
      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} />
        }} />
    );
  }
}

//這是一個(gè)使用了通用導(dǎo)航的測試頁面
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='通用導(dǎo)航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',}}>跳轉(zhuǎn)詳情</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是導(dǎo)航器對(duì)象
        let lastRoute = routes[routes.length - 1]; // 當(dāng)前頁面對(duì)應(yīng)的route對(duì)象
        lastRoute.handleBack = this.leftCallBack.bind(this);//設(shè)置route對(duì)象的hanleBack屬性
      }
  }

  /**
   * 場景:編輯頁面,點(diǎn)擊物理或左上角返回,需要提示“確定放棄修改嗎?”
   */
  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='通用導(dǎo)航Detail' backgroundColor='#e6454a'/>
          <View style={{flex:1,backgroundColor:'#f2f2f2',justifyContent:'center',alignItems:'center',}}>
            <Text style={{fontSize:28,color:'#998462',textAlign:'center',}}>我只是容器里的一個(gè)RN頁面</Text>
          </View>
        </View>
    );
  }
}

好,這樣就基本實(shí)現(xiàn)了通用的RN容器和導(dǎo)航,當(dāng)然還有一些地方可以優(yōu)化。

《React-Native系列》前42篇博文見http://www.itdecent.cn/p/34ef5d19ea12

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容