《React-Native系列》43、通用容器和導航設計方案

原文發布于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.lxweimin.com/p/34ef5d19ea12

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,698評論 6 539
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,202評論 3 426
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,742評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,580評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,297評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,688評論 1 327
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,693評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,875評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,438評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,183評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,384評論 1 372
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,931評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,612評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,022評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,297評論 1 292
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,093評論 3 397
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,330評論 2 377

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,701評論 25 708
  • ANavigator稍微復雜點的移動應用都會有頁面跳轉的場景,即用戶在頁面A上點擊某個功能,比如查看內容詳情或者幫...
    conf1234閱讀 1,541評論 0 3
  • 昨天去參加了一天的會議,可以說是受益匪淺,因為時間的緣故,昨天沒有來的及寫總結,所以利用早上的時間,想來寫下我一天...
    曦冉持續行動成長記閱讀 549評論 0 0
  • 標題:搬運工 我從黎明的深處走來, 霧水早已打濕了雙眼。 點燃一根前行的煙, 微弱的火光, 緩解了緊繃的神經。 用...
    CZP_閱讀 265評論 0 3
  • 以arch的基本系統為基礎,我們可以對其進行各種配置操作,讓其更符合個人喜好。下面介紹了一些常用的配置。 1.用戶...
    Air_WaWei閱讀 14,073評論 1 6