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)代碼