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

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

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

RN容器在iOS使用UIViewController和屎、Android使用Activity或者Fragment,加載bundle文件春瞬,正常情況下柴信,一個模塊只有一個bundle文件。
要實現(xiàn)頁面的跳轉宽气,我們可以使用Navigator組件随常,具體使用可以參考:http://blog.csdn.net/codetomylaw/article/details/52059493
還有幾個問題需要解決:
1、導航欄
在原生App中導航欄通常是統(tǒng)一管理的萄涯,那么在通用容器中绪氛,我們可以定義一個通用的RN導航。
2涝影、Native跳轉RN容器
使用正常的Native跳轉方式即可枣察,譬如在Android中 startActivity 。
3燃逻、RN容器返回Native界面
由于導航欄已經是RN界面編寫的询件,那么Native端就需要提供一個橋接的方法給RN調用,橋接方法需要實現(xiàn)的邏輯:finish掉初始化的RN容器
4唆樊、處理安卓系統(tǒng)返回鍵
詳細見Demo代碼

好宛琅,我們通過一個簡單的Demo來演示。

我們實現(xiàn)的效果是
1逗旁、從Native頁面跳轉RN頁面A嘿辟,RN頁面A是由RN容器加載,點擊左上角可以返回到Native界面
2片效、點擊RN頁面A中的“跳轉詳情”可以跳轉到RN頁面B
3红伦、點擊RN頁面B中的左上角或安卓物理返回鍵,可以返回到RN頁面A
4淀衣、點擊RN頁面B中的左上角或安卓物理返回鍵昙读,可以阻斷頁面的返回,實現(xiàn)我們自己的邏輯
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>);

    //標題現(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();
    // 當前頁面不為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>
    );
  }
}

好盛杰,這樣就基本實現(xiàn)了通用的RN容器和導航挽荡,當然還有一些地方可以優(yōu)化。

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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末即供,一起剝皮案震驚了整個濱河市定拟,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌募狂,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,946評論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件角雷,死亡現(xiàn)場離奇詭異祸穷,居然都是意外死亡,警方通過查閱死者的電腦和手機勺三,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,336評論 3 399
  • 文/潘曉璐 我一進店門雷滚,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人吗坚,你說我怎么就攤上這事祈远。” “怎么了商源?”我有些...
    開封第一講書人閱讀 169,716評論 0 364
  • 文/不壞的土叔 我叫張陵车份,是天一觀的道長。 經常有香客問我牡彻,道長扫沼,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,222評論 1 300
  • 正文 為了忘掉前任庄吼,我火速辦了婚禮缎除,結果婚禮上,老公的妹妹穿的比我還像新娘总寻。我一直安慰自己器罐,他們只是感情好,可當我...
    茶點故事閱讀 69,223評論 6 398
  • 文/花漫 我一把揭開白布渐行。 她就那樣靜靜地躺著轰坊,像睡著了一般铸董。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上衰倦,一...
    開封第一講書人閱讀 52,807評論 1 314
  • 那天袒炉,我揣著相機與錄音,去河邊找鬼樊零。 笑死我磁,一個胖子當著我的面吹牛,可吹牛的內容都是我干的驻襟。 我是一名探鬼主播夺艰,決...
    沈念sama閱讀 41,235評論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼沉衣!你這毒婦竟也來了郁副?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 40,189評論 0 277
  • 序言:老撾萬榮一對情侶失蹤豌习,失蹤者是張志新(化名)和其女友劉穎存谎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體肥隆,經...
    沈念sama閱讀 46,712評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡既荚,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,775評論 3 343
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了栋艳。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片恰聘。...
    茶點故事閱讀 40,926評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖吸占,靈堂內的尸體忽然破棺而出晴叨,到底是詐尸還是另有隱情,我是刑警寧澤矾屯,帶...
    沈念sama閱讀 36,580評論 5 351
  • 正文 年R本政府宣布兼蕊,位于F島的核電站,受9級特大地震影響件蚕,放射性物質發(fā)生泄漏遍略。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,259評論 3 336
  • 文/蒙蒙 一骤坐、第九天 我趴在偏房一處隱蔽的房頂上張望绪杏。 院中可真熱鬧,春花似錦纽绍、人聲如沸蕾久。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,750評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽僧著。三九已至履因,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間盹愚,已是汗流浹背栅迄。 一陣腳步聲響...
    開封第一講書人閱讀 33,867評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留皆怕,地道東北人毅舆。 一個月前我還...
    沈念sama閱讀 49,368評論 3 379
  • 正文 我出身青樓,卻偏偏與公主長得像愈腾,于是被迫代替她去往敵國和親憋活。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,930評論 2 361

推薦閱讀更多精彩內容

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