React Native導(dǎo)航器

React Native 進(jìn)階(四)--導(dǎo)航器

導(dǎo)航器對(duì)比

如果你剛開(kāi)始接觸描扯,那么直接選擇Navigator就好譬涡。如果你只針對(duì)iOS平臺(tái)開(kāi)發(fā)合呐,并且想和系統(tǒng)原生外觀一致板乙,那么可以選擇NavigatorIOS砸脊。如果你想更好地管理導(dǎo)航棧具篇,那么應(yīng)該嘗試一下NavigationExperimental。

Navigator

Navigator使用純JavaScript實(shí)現(xiàn)了一個(gè)導(dǎo)航棧凌埂,因此可以跨平臺(tái)工作驱显,同時(shí)也便于定制。

Navigator可以在renderScene方法中根據(jù)當(dāng)前路由渲染不同的組件侨舆。默認(rèn)情況下新的場(chǎng)景會(huì)從屏幕右側(cè)滑進(jìn)來(lái)秒紧,但你也可以通過(guò)configureScene方法來(lái)管理這一行為。你還可以通過(guò)navigationBar屬性來(lái)配置一個(gè)跨場(chǎng)景的導(dǎo)航欄挨下。但我們不推薦使用跨場(chǎng)景的navigationBar熔恢,它的代碼邏輯維護(hù)起來(lái)很困難!建議自己在場(chǎng)景中用View實(shí)現(xiàn)自定義的導(dǎo)航欄臭笆。

方法

如果你得到了一個(gè)navigator對(duì)象的引用叙淌,則可以調(diào)用許多方法來(lái)進(jìn)行導(dǎo)航:

  • getCurrentRoutes() - 獲取當(dāng)前棧里的路由,也就是push進(jìn)來(lái)愁铺,沒(méi)有pop掉的那些鹰霍。
  • jumpBack() - 跳回之前的路由,當(dāng)然前提是保留現(xiàn)在的茵乱,還可以再跳回來(lái)茂洒,會(huì)給你保留原樣。
  • jumpForward() - 上一個(gè)方法不是調(diào)到之前的路由了么瓶竭,用這個(gè)跳回來(lái)就好了督勺。
  • jumpTo(route) - 跳轉(zhuǎn)到已有的場(chǎng)景并且不卸載。
  • push(route) - 跳轉(zhuǎn)到新的場(chǎng)景斤贰,并且將場(chǎng)景入棧智哀,你可以稍后跳轉(zhuǎn)過(guò)去
  • pop() - 跳轉(zhuǎn)回去并且卸載掉當(dāng)前場(chǎng)景
  • replace(route) - 用一個(gè)新的路由替換掉當(dāng)前場(chǎng)景
  • replaceAtIndex(route, index) - 替換掉指定序列的路由場(chǎng)景
  • replacePrevious(route) - 替換掉之前的場(chǎng)景
  • resetTo(route) - 跳轉(zhuǎn)到新的場(chǎng)景,并且重置整個(gè)路由棧
  • immediatelyResetRouteStack(routeStack) - 用新的路由數(shù)組來(lái)重置路由棧
  • popToRoute(route) - pop到路由指定的場(chǎng)景荧恍,在整個(gè)路由棧中瓷叫,處于指定場(chǎng)景之后的場(chǎng)景將會(huì)被卸載。
  • popToTop() - pop到棧中的第一個(gè)場(chǎng)景,卸載掉所有的其他場(chǎng)景摹菠。

這些都是navigator可以用的public method,就是跳轉(zhuǎn)用的盒卸,里面有些帶參數(shù)的XXX(route),這個(gè)route參數(shù)是什么呢辨嗽,這個(gè)route就是:
renderScene={(route, navigator) =>
這里的route世落,最基本的route就是:

let route = {
  component: SampleComponent
}

屬性

configureScene function

可選的函數(shù),用來(lái)配置場(chǎng)景動(dòng)畫(huà)和手勢(shì)糟需。會(huì)帶有兩個(gè)參數(shù)調(diào)用屉佳,一個(gè)是當(dāng)前的路由,一個(gè)是當(dāng)前的路由棧洲押。然后它應(yīng)當(dāng)返回一個(gè)場(chǎng)景配置對(duì)象

(route, routeStack) => Navigator.SceneConfigs.FloatFromRight

  • Navigator.SceneConfigs.PushFromRight (默認(rèn))
  • Navigator.SceneConfigs.FloatFromRight
  • Navigator.SceneConfigs.FloatFromLeft
  • Navigator.SceneConfigs.FloatFromBottom
  • Navigator.SceneConfigs.FloatFromBottomAndroid
  • Navigator.SceneConfigs.FadeAndroid
  • Navigator.SceneConfigs.HorizontalSwipeJump
  • Navigator.SceneConfigs.HorizontalSwipeJumpFromRight
  • Navigator.SceneConfigs.VerticalUpSwipeJump
  • Navigator.SceneConfigs.VerticalDownSwipeJump

initialRoute object

定義啟動(dòng)時(shí)加載的路由武花。路由是導(dǎo)航欄用來(lái)識(shí)別渲染場(chǎng)景的一個(gè)對(duì)象。initialRoute必須是initialRouteStack中的一個(gè)路由杈帐。initialRoute默認(rèn)為initialRouteStack中最后一項(xiàng)体箕。

initialRouteStack [object]

提供一個(gè)路由集合用來(lái)初始化。如果沒(méi)有設(shè)置初始路由的話則必須設(shè)置該屬性挑童。如果沒(méi)有提供該屬性累铅,它將被默認(rèn)設(shè)置成一個(gè)只含有initialRoute的數(shù)組。

navigationBar node

可選參數(shù)站叼,提供一個(gè)在場(chǎng)景切換的時(shí)候保持的導(dǎo)航欄娃兽。

navigator object

可選參數(shù),提供從父導(dǎo)航器獲得的導(dǎo)航器對(duì)象尽楔。

onDidFocus function

每當(dāng)導(dǎo)航切換完成或初始化之后投储,調(diào)用此回調(diào),參數(shù)為新場(chǎng)景的路由阔馋。

onWillFocus function

會(huì)在導(dǎo)航切換之前調(diào)用玛荞,參數(shù)為目標(biāo)路由。

renderScene function

必要參數(shù)呕寝。用來(lái)渲染指定路由的場(chǎng)景勋眯。調(diào)用的參數(shù)是路由和導(dǎo)航器。

(route, navigator) =><MySceneComponent title={route.title} navigator={navigator} />

sceneStyle View#style

將會(huì)應(yīng)用在每個(gè)場(chǎng)景的容器上的樣式下梢。

例子

Navigator.js

import React from 'react';
import {
  View,
  Navigator
} from 'react-native';
import FirstPageComponent from './FirstPageComponent';

export default class Sample extends React.Component {
  render() {
    let defaultName = 'FirstPageComponent';
    let defaultComponent = FirstPageComponent;
    return (
      <Navigator
        initialRoute={{ name: defaultName, component: defaultComponent }}
        configureScene={(route) => { // 跳轉(zhuǎn)動(dòng)畫(huà)
          return Navigator.SceneConfigs.VerticalDownSwipeJump;
        }}
        renderScene={(route, navigator) => {
          let Component = route.component;
          // 這里有個(gè) { ...route.params }客蹋,這個(gè)語(yǔ)法是把 routes.params 里的每個(gè)key 作為props的一個(gè)屬性
          return <Component {...route.params} navigator={navigator} />
        }} 
      />
    );
  }
} 
第10行: 一個(gè)初始首頁(yè)的component名字,比如我寫(xiě)了一個(gè)component叫HomeComponent怔球,
        那么這個(gè)name就是這個(gè)組件的名字[HomeComponent]了嚼酝。  
第11行: 這個(gè)組件的Class浮还,用來(lái)一會(huì)兒實(shí)例化成 <Component />標(biāo)簽
第14行: initialRoute={{ name: defaultName, component: defaultComponent }} 這個(gè)指定了默認(rèn)的頁(yè)面竟坛,
        也就是啟動(dòng)app之后會(huì)看到界面的第一屏。 需要填寫(xiě)兩個(gè)參數(shù): name 跟 component。(注意這里填什么參數(shù)(參數(shù)名)純粹是自定義的担汤,
        因?yàn)檫@個(gè)參數(shù)也是你自己發(fā)自己收涎跨,自己在renderScene方法中處理。這個(gè)示例用了兩個(gè)參數(shù)崭歧,但其實(shí)真正使用的參數(shù)只有component)  
第15隅很,16,17行: configureScene={() => {return Navigator.SceneConfigs.VerticalDownSwipeJump;}} 這個(gè)是頁(yè)面之間跳轉(zhuǎn)時(shí)候的動(dòng)畫(huà)率碾,
        具體有哪些動(dòng)畫(huà)可以看node_modules/react-native/Libraries/CustomComponents/Navigator/NavigatorSceneConfigs.js下的源代碼叔营。
最后的幾行: renderScene={(route, navigator) => {let Component = route.component;return <Component {...route.params} navigator={navigator} />}},
        這里是每個(gè)人最疑惑的所宰,我們先看到回調(diào)里的兩個(gè)參數(shù):route, navigator绒尊。通過(guò)打印我們發(fā)現(xiàn)route里其實(shí)就是我們傳遞的name,component這兩個(gè)貨,
        navigator是一個(gè)Navigator的對(duì)象仔粥,為什么呢婴谱,因?yàn)樗衟ush pop jump...等方法,這是我們等下用來(lái)跳轉(zhuǎn)頁(yè)面用的那個(gè)navigator對(duì)象躯泰。
        return <Component {...route.params} navigator={navigator} />  
        這里有一個(gè)判斷谭羔,也就是如果傳遞進(jìn)來(lái)的component存在,那我們就是返回一個(gè)這個(gè)component麦向,結(jié)合前面 initialRoute 的參數(shù)瘟裸,我們就知道,
        這是一個(gè)會(huì)被render出來(lái)給用戶看到的component磕蛇,然后navigator作為props傳遞給了這個(gè)component景描。

所以下一步,在這個(gè)FirstPageComponent里面秀撇,我們可以直接拿到這個(gè) props.navigator:

FirstPageComponent.js

import React from 'react';
import {
  View,
  Navigator,
  TouchableOpacity,
  Text
} from 'react-native';

import SecondPageComponent from './SecondPageComponent';

export default class FirstPageComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      id: 2,
      user: null,
    };
  }
  _pressButton() {
    let _this = this;
    const { navigator } = this.props;
    //為什么這里可以取得 props.navigator?請(qǐng)注意Navigator.js中:
    //<Component {...route.params} navigator={navigator} />
    //這里傳遞了navigator作為props
    if (navigator) {
      // 入棧~ 把SecondPageComponent頁(yè)面push進(jìn)棧超棺,接著跳轉(zhuǎn)到SecondPageComponent
      navigator.push({
        name: 'SecondPageComponent',
        component: SecondPageComponent,
        //這個(gè) params 其實(shí)來(lái)自于Navigator 里的一個(gè)方法的參數(shù)
        params: {  //routes.params
          id: this.state.id,
          //從SecondPageComponent獲取user
          getUser: function (user) {
            _this.setState({
              user: user
            })
          }
        }
      })
    }
  }
  render() {
    if (this.state.user) {
      return (
        <View>
          <Text>用戶信息: {JSON.stringify(this.state.user)}</Text>
        </View>
      );
    } else {
      return (
        <View>
          <TouchableOpacity onPress={this._pressButton.bind(this)}>
            <Text>查詢ID為{this.state.id}的用戶信息</Text>
          </TouchableOpacity>
        </View>
      );
    }
  }
}

這個(gè)里面創(chuàng)建了一個(gè)可以點(diǎn)擊的區(qū)域,點(diǎn)擊可以跳到SecondPageComponent這個(gè)頁(yè)面呵燕,實(shí)現(xiàn)頁(yè)面的跳轉(zhuǎn)棠绘。
現(xiàn)在來(lái)創(chuàng)建SecondPageComponent,并且讓它可以再跳回FirstPageComponent:

SecondPageComponent.js

import React from 'react';
import {
  View,
  Navigator,
  Text,
  TouchableOpacity
} from 'react-native';

import FirstPageComponent from './FirstPageComponent';

const USER_MODELS = {
  1: { name: '小李', age: 18 },
  2: { name: '小明', age: 20 }
};

export default class SecondPageComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      id: null
    };
  }
  componentDidMount() {
    //這里獲取從FirstPageComponent傳遞過(guò)來(lái)的參數(shù): id
    this.setState({
      id: this.props.id
    });
  }
  _pressButton() {
    const { navigator } = this.props;
    if (this.props.getUser) {
      let user = USER_MODELS[this.props.id];
      this.props.getUser(user);
    }
    //出棧~ 把當(dāng)前的頁(yè)面pop掉再扭,這里就返回到了上一個(gè)頁(yè)面:FirstPageComponent了
    if (navigator) {
      navigator.pop();
    }
  }
  
  render() {
    return (
      <View>
        <Text>獲得的參數(shù)(id): id={this.state.id}</Text>
        <TouchableOpacity onPress={this._pressButton.bind(this)}>
          <Text>點(diǎn)我跳回去</Text>
        </TouchableOpacity>
      </View>
    );
  }
}

傳遞參數(shù)和獲取參數(shù)

傳遞參數(shù)到新頁(yè)面

傳遞參數(shù)到新頁(yè)面氧苍,可以通過(guò)push。比如在一個(gè) press的事件里:

FirstPageComponent.js

...
constructor(props) {
  super(props);
  this.state = {
    id: 2
  };
}
_pressButton() {
  ...
    navigator.push({
      name: 'SecondPageComponent',
      component: SecondPageComponent,
      //這個(gè) params 其實(shí)來(lái)自于Navigator 里的一個(gè)方法的參數(shù)
      params: {
        id: this.state.id
      }
    })
  ...
}

params的來(lái)歷:

//Navigator.js
...
<Navigator
  initialRoute={{ name: defaultName, component: defaultComponent }}
  configureScene={() => {
    return Navigator.SceneConfigs.VerticalDownSwipeJump;
  }}
  renderScene={(route, navigator) => {
    let Component = route.component;
    if(route.component) {
        //這里有個(gè) { ...route.params }
        return <Component {...route.params} navigator={navigator} />
    }
  }} 
/>
...

{ ...route.params }語(yǔ)法是把 routes.params 里的每個(gè)key 作為props的一個(gè)屬性:

//FirstPageComponent.js
...
navigator.push({
    name: 'SecondPageComponent',
    component: SecondPageComponent,
    params: {  //routes.params
        id: this.state.id
    }
});
...

這里的 params.id 就變成了 <Component id={routes.params.id} navigator={navigator}> 里的id屬性(props)傳遞給了下一個(gè)頁(yè)面泛范。

//SecondPageComponent.js
...
componentDidMount() {
  //這里獲取從FirstPageComponent傳遞過(guò)來(lái)的參數(shù): id
  this.setState({
    id: this.props.id
  });
}
...
render() {
  return (
    <View>
      <Text>獲得的參數(shù): id={ this.state.id }</Text>
      <TouchableOpacity onPress={this._pressButton.bind(this)}>
        <Text>點(diǎn)我跳回去</Text>
      </TouchableOpacity>
    </View>
  );
}
返回參數(shù)到之前頁(yè)面

返回的時(shí)候让虐,也需要傳遞參數(shù)回上一個(gè)頁(yè)面。但是navigator.pop()并沒(méi)有提供參數(shù)罢荡,因?yàn)閜op()只是從 [路由1,路由2赡突,路由3对扶。。惭缰。]里把最后一個(gè)路由踢出去的操作浪南,并不支持傳遞參數(shù)給倒數(shù)第二個(gè)路由蹂喻,這里要用到一個(gè)概念银萍,把上一個(gè)頁(yè)面的實(shí)例或者回調(diào)方法,作為參數(shù)傳遞到當(dāng)前頁(yè)面來(lái)冈敛,在當(dāng)前頁(yè)面操作上一個(gè)頁(yè)面的state:

比如FirstPageComponent傳遞id到SecondPageComponent昂羡,然后SecondPageComponent返回user信息給FirstPageComponent:

//FirstPageComponent.js
...
constructor(props) {
  super(props);
  this.state = {
    id: 2,
    user: null,
  }
}
...
_pressButton() {
    let _this = this;
    ...
      params: {  //routes.params
        id: this.state.id,
        //從SecondPageComponent獲取user
        getUser: function (user) {
          _this.setState({
            user: user
          })
        }
      }
...
render() {
  if (this.state.user) {
    return (
      <View>
        <Text>用戶信息: {JSON.stringify(this.state.user)}</Text>
      </View>
    );
  } else {
    return (
      <View>
        <TouchableOpacity onPress={this._pressButton.bind(this)}>
          <Text>查詢ID為{this.state.id}的用戶信息</Text>
        </TouchableOpacity>
      </View>
    );
  }
}
//SecondPageComponent.js
...
const USER_MODELS = {
  1: { name: '小李', age: 18 },
  2: { name: '小明', age: 20 }
};

export default class SecondPageComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      id: null
    };
  }
  componentDidMount() {
    //這里獲取從FirstPageComponent傳遞過(guò)來(lái)的參數(shù): id
    this.setState({
      id: this.props.id
    });
  }
  _pressButton() {
    const { navigator } = this.props;
    if (this.props.getUser) {
      let user = USER_MODELS[this.props.id];
      this.props.getUser(user);
    }
    //出棧~ 把當(dāng)前的頁(yè)面pop掉絮记,這里就返回到了上一個(gè)頁(yè)面:FirstPageComponent了
    if (navigator) {
      navigator.pop();
    }
  }
}
...

NavigatorIOS

如果你只針對(duì)iOS平臺(tái)開(kāi)發(fā),那么可以考慮使用NavigatorIOS虐先。它是基于 UINavigationController封裝的到千。

<NavigatorIOS
  initialRoute={{
    component: MyScene,
    title: 'My Initial Scene',
    passProps: { myProp: 'foo' },
  }}
/>

用法類似Navigator,NavigatorIOS也使用路由對(duì)象來(lái)描述場(chǎng)景赴穗,但有一些重要區(qū)別憔四。其中要渲染的組件在路由對(duì)象的component字段中指定,要給目標(biāo)組件傳遞的參數(shù)則寫(xiě)在passProps中般眉。被渲染的component都會(huì)自動(dòng)接受到一個(gè)名為navigator的屬性了赵,你可以直接調(diào)用此對(duì)象(this.props.navigator)的push和pop方法。
由于NavigatorIOS使用的是原生的UIKit導(dǎo)航甸赃,所以它會(huì)自動(dòng)渲染一個(gè)帶有返回按鈕和標(biāo)題的導(dǎo)航欄柿汛。

你還可以看看react-native-navigation,這是一個(gè)第三方的組件埠对,旨在于提供原生的跨平臺(tái)的導(dǎo)航組件络断。

NavigationExperimental

Navigator和NavigatorIOS都是有狀態(tài)的組件。如果你在app中多處使用這些組件项玛,那么維護(hù)工作就會(huì)變得非常麻煩貌笨。NavigationExperimental以不同的方式實(shí)現(xiàn)了導(dǎo)航,它可以使用任何視圖來(lái)作為導(dǎo)航視圖襟沮,同時(shí)還用到了規(guī)約函數(shù)(reducer)自頂向下地管理狀態(tài)锥惋。正如名字中的Experimental(實(shí)驗(yàn))所示,這一組件的整體實(shí)現(xiàn)具有一定的實(shí)驗(yàn)性开伏,但仍然建議你嘗試一下用它去更好地管理應(yīng)用的導(dǎo)航膀跌。

<NavigationCardStack
  onNavigateBack={onPopRouteFunc}
  navigationState={myNavigationState}
  renderScene={renderSceneFun}
/>

引入NavigationExperimental的步驟和React Native中的其他組件一樣。在引入此組件之后固灵,還可以進(jìn)一步解構(gòu)其中一些有用的子組件捅伤,比如這里我們會(huì)從中解構(gòu)NavigationCardStack和 NavigationStateUtils這兩個(gè)子組件。

import React, { Component } from 'react';
import { NavigationExperimental } from 'react-native';

const {
  CardStack: NavigationCardStack,
  StateUtils: NavigationStateUtils,
} = NavigationExperimental;

NavigationExperimental的實(shí)現(xiàn)機(jī)制與Navigator和NavigatorIOS有所不同巫玻,用它來(lái)構(gòu)建導(dǎo)航棧還需要一些額外的步驟丛忆。

第一步:定義初始狀態(tài)和根容器

首先創(chuàng)建一個(gè)新組件困介,我們會(huì)把它作為根容器,并在這里定義初始狀態(tài)蘸际。導(dǎo)航棧會(huì)定義在navigationState字段中,其中也包含了初始的路由定義:

import React, { Component } from 'react';
import { NavigationExperimental } from 'react-native';

const {
  CardStack: NavigationCardStack,
  StateUtils: NavigationStateUtils,
} = NavigationExperimental;

class Sample extends Component {
  constructor(props, context) {
    super(props, context);
    this.state = {
      // 定義初始的導(dǎo)航狀態(tài)
      navigationState: {
        index: 0, // 現(xiàn)在是第一頁(yè)(索引從0開(kāi)始)
        routes: [{key: '最初的場(chǎng)景'}], // 初始僅設(shè)定一個(gè)路由
      },
    };
    // 稍后再補(bǔ)充此函數(shù)的實(shí)現(xiàn)細(xì)節(jié)
    this._onNavigationChange = this._onNavigationChange.bind(this);
  }
  _onNavigationChange(type) {
    // 稍后再補(bǔ)充此函數(shù)的實(shí)現(xiàn)細(xì)節(jié)
  }
  _exit() {
    //exit()實(shí)現(xiàn)
  }
  render() {
    return (
      <Text>這是一段占位的文字徒扶。稍后會(huì)在這里渲染導(dǎo)航粮彤。</Text>
    );
  }
}

現(xiàn)在我們定義了一個(gè)有狀態(tài)的組件,暫時(shí)是無(wú)用的姜骡。我們的初始狀態(tài)包含了一個(gè)路由對(duì)象导坟,以及當(dāng)前頁(yè)面的索引值。但是這看起來(lái)跟Navigator的初始路由定義好像沒(méi)什么區(qū)別圈澈!回憶一下navigator對(duì)象提供了push和pop操作惫周,看起來(lái)也非常直觀。但是前面我們說(shuō)過(guò)了康栈,現(xiàn)在我們會(huì)在根容器上使用規(guī)約函數(shù)來(lái)管理狀態(tài)递递,下面繼續(xù)。

第二步:規(guī)約導(dǎo)航狀態(tài)

NavigationExperimental內(nèi)置了一些有用的規(guī)約函數(shù)(reducer)啥么,都放在NavigationStateUtils中登舞。我們現(xiàn)在要用的兩個(gè)就是push和pop了。它們接受一個(gè)navigationState對(duì)象參數(shù)悬荣,然后返回新的navigationState對(duì)象菠秒。

據(jù)此我們可以這樣來(lái)編寫(xiě)_onNavigationChange函數(shù),在其中判斷"push"和"pop"的行為氯迂,并分別規(guī)約對(duì)應(yīng)的狀態(tài)践叠。

_onNavigationChange(type) {
  // 從state中解構(gòu)出navigationState
  let {navigationState} = this.state;
  switch (type) {
    case 'push':
      // push一個(gè)新路由,在這里就是一個(gè)帶有key屬性的對(duì)象嚼蚀。
      // key必須要確保唯一性
      const route = {key: 'Route-' + Date.now()};
      // 調(diào)用NavigationStateUtils提供的push規(guī)約函數(shù)
      navigationState = NavigationStateUtils.push(navigationState, route);
      break;
    case 'pop':
      // 使用pop函數(shù)來(lái)彈出當(dāng)前路由
      navigationState = NavigationStateUtils.pop(navigationState);
      break;
  }
  // 如果沒(méi)有實(shí)際變化禁灼,則NavigationStateUtils會(huì)返回同樣的`navigationState`
  // 我們只會(huì)更新確實(shí)發(fā)生變化的狀態(tài)
  if (this.state.navigationState !== navigationState) {
    // 請(qǐng)記住更新?tīng)顟B(tài)必須通過(guò)setState()方法!
    this.setState({navigationState});
    // 簡(jiǎn)單講解一下上面那一句ES6語(yǔ)法
    // 如果key和value的字面一樣轿曙,那么可以簡(jiǎn)寫(xiě)成一個(gè)匾二,等同于下面的寫(xiě)法:
    // this.setState({navigationState: navigationState});
  }
}

到這里,我們已經(jīng)觸碰到了NavigationExperimental的精髓所在拳芙。這里我們只處理了兩種行為察藐,實(shí)際開(kāi)發(fā)中行為可能更復(fù)雜,比如可能會(huì)考慮后退(back)行為舟扎,又或者是tab間的切換過(guò)渡行為等等分飞。

我們現(xiàn)在還沒(méi)寫(xiě)初始場(chǎng)景和實(shí)際的導(dǎo)航器,不過(guò)別急睹限,我們一步一步來(lái)譬猫。

第三步:定義場(chǎng)景

為方便起見(jiàn)我們先定義一個(gè)Row(行)組件讯檐。其中顯示了一些文字,并帶有點(diǎn)擊事件染服。

class TappableRow extends Component {
  render() {
    return (
      <TouchableHighlight
        style={styles.row}
        underlayColor="#D0D0D0"
        onPress={this.props.onPress}>
        <Text style={styles.buttonText}>
          {this.props.text}
        </Text>
      </TouchableHighlight>
    );
  }
}

現(xiàn)在來(lái)定義實(shí)際的場(chǎng)景别洪。其中用到了一個(gè)ScrollView來(lái)顯示一個(gè)垂直列表,第一行顯示當(dāng)前路由對(duì)象的key字段值柳刮,后兩行用來(lái)點(diǎn)擊后調(diào)用導(dǎo)航器的push和pop方法挖垛。

class MyVeryComplexScene extends Component {
  render() {
    return (
      <ScrollView style={styles.scrollView}>
        <Text style={styles.row}>
          路由: {this.props.route.key}
        </Text>
        <TappableRow
          text="加載下一個(gè)場(chǎng)景"
          onPress={this.props.onPushRoute}/>
        <TappableRow
          text="返回上一個(gè)場(chǎng)景"
          onPress={this.props.onPopRoute}/>
      </ScrollView>
    );
  }
}

第四步:創(chuàng)建導(dǎo)航棧

我們之前已經(jīng)定義了狀態(tài)和管理狀態(tài)的規(guī)約函數(shù),現(xiàn)在可以創(chuàng)建導(dǎo)航器組件了秉颗。在寫(xiě)導(dǎo)航器的同時(shí)痢毒,我們可以使用當(dāng)前路由的屬性來(lái)配置場(chǎng)景并渲染它了。

class MyVerySampleNavigator extends Component {
  // 在這里綁定一些導(dǎo)航用的方法
  constructor(props, context) {
    super(props, context);
    this._onPushRoute = this.props.onNavigationChange.bind(null, 'push');
    this._onPopRoute = this.props.onNavigationChange.bind(null, 'pop');
    this._renderScene = this._renderScene.bind(this);
  }
  // 現(xiàn)在我們終于可以使用“NavigationCardStack”來(lái)渲染場(chǎng)景蚕甥。
  render() {
    return (
      <NavigationCardStack
        onNavigateBack={this._onPopRoute}
        navigationState={this.props.navigationState}
        renderScene={this._renderScene}
        style={styles.navigator}
      />
    );
  }
  // 根據(jù)路由來(lái)渲染場(chǎng)景
  // `sceneProps`的具體結(jié)構(gòu)定義在`NavigationTypeDefinition`的`NavigationSceneRendererProps`中
  // 這里你可以根據(jù)路由的不同來(lái)返回不同的場(chǎng)景組件哪替,我們這里為了簡(jiǎn)要說(shuō)明,始終只返回這一個(gè)場(chǎng)景組件
  _renderScene(sceneProps) {
    return (
      <MyVeryComplexScene
        route={sceneProps.scene.route}
        onPushRoute={this._onPushRoute}
        onPopRoute={this._onPopRoute}
        onExit={this.props.onExit}
      />
    );
  }
}

最后把我們新做的導(dǎo)航器放到根容器中:

class Sample extends Component {
  // 這里省略了constructor和其他的方法
  render() {
    return (
      <MyVerySampleNavigator
        navigationState={this.state.navigationState}
        onNavigationChange={this._onNavigationChange}
        onExit={this._exit}
      />
    );
  }
}

別忘了引入組件和樣式

import { NavigationExperimental, PixelRatio, ScrollView, StyleSheet, Text, TouchableHighlight } from 'react-native';

const styles = StyleSheet.create({
  navigator: {
    flex: 1,
  },
  scrollView: {
    marginTop: 64
  },
  row: {
    padding: 15,
    backgroundColor: 'white',
    borderBottomWidth: 1 / PixelRatio.get(),
    borderBottomColor: '#CDCDCD',
  },
  rowText: {
    fontSize: 17,
  },
  buttonText: {
    fontSize: 17,
    fontWeight: '500',
  },
});

React Native學(xué)習(xí)筆記--進(jìn)階(一)--嵌入到Android原生應(yīng)用中菇怀、組件的生命周期凭舶、顏色、圖片爱沟、觸摸事件
React Native學(xué)習(xí)筆記--進(jìn)階(二)--動(dòng)畫(huà)
React Native學(xué)習(xí)筆記--進(jìn)階(三)--定時(shí)器库快、直接操作(setNativeProps)、調(diào)試
React Native學(xué)習(xí)筆記--進(jìn)階(四)--導(dǎo)航器
React Native學(xué)習(xí)筆記--進(jìn)階(五)--性能钥顽、升級(jí)义屏、特定平臺(tái)代碼

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市蜂大,隨后出現(xiàn)的幾起案子闽铐,更是在濱河造成了極大的恐慌,老刑警劉巖奶浦,帶你破解...
    沈念sama閱讀 210,914評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件兄墅,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡澳叉,警方通過(guò)查閱死者的電腦和手機(jī)隙咸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評(píng)論 2 383
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)成洗,“玉大人五督,你說(shuō)我怎么就攤上這事∑垦辏” “怎么了充包?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,531評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)遥椿。 經(jīng)常有香客問(wèn)我基矮,道長(zhǎng)淆储,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,309評(píng)論 1 282
  • 正文 為了忘掉前任家浇,我火速辦了婚禮本砰,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘钢悲。我一直安慰自己点额,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,381評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布譬巫。 她就那樣靜靜地躺著,像睡著了一般督笆。 火紅的嫁衣襯著肌膚如雪芦昔。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,730評(píng)論 1 289
  • 那天娃肿,我揣著相機(jī)與錄音咕缎,去河邊找鬼。 笑死料扰,一個(gè)胖子當(dāng)著我的面吹牛凭豪,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播晒杈,決...
    沈念sama閱讀 38,882評(píng)論 3 404
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼嫂伞,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了拯钻?” 一聲冷哼從身側(cè)響起帖努,我...
    開(kāi)封第一講書(shū)人閱讀 37,643評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎粪般,沒(méi)想到半個(gè)月后拼余,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,095評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡亩歹,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,448評(píng)論 2 325
  • 正文 我和宋清朗相戀三年匙监,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片小作。...
    茶點(diǎn)故事閱讀 38,566評(píng)論 1 339
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡亭姥,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出顾稀,到底是詐尸還是另有隱情致份,我是刑警寧澤,帶...
    沈念sama閱讀 34,253評(píng)論 4 328
  • 正文 年R本政府宣布础拨,位于F島的核電站氮块,受9級(jí)特大地震影響绍载,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜滔蝉,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,829評(píng)論 3 312
  • 文/蒙蒙 一击儡、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蝠引,春花似錦阳谍、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,715評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至吊洼,卻和暖如春训貌,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背冒窍。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,945評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工递沪, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人综液。 一個(gè)月前我還...
    沈念sama閱讀 46,248評(píng)論 2 360
  • 正文 我出身青樓款慨,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親谬莹。 傳聞我的和親對(duì)象是個(gè)殘疾皇子檩奠,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,440評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容