前言
除了極少數(shù)特殊設(shè)計(jì)的App,導(dǎo)航都是一個(gè)App重要組成的部分。導(dǎo)航欄能夠維護(hù)一個(gè)導(dǎo)航堆棧,能夠讓用戶(hù)清楚的知道自己當(dāng)前所處的頁(yè)面和返回的頁(yè)面。
在React Native中,官方推薦使用Navigator,因?yàn)檫@個(gè)能夠在iOS和安卓中通用,不過(guò)在現(xiàn)在(2016年5月18日),Navigator 性能較差。所以,對(duì)iOS應(yīng)用,往往可以使用NavigatorIOS,你將獲得系統(tǒng)的iOS導(dǎo)航欄性能和動(dòng)畫(huà)。但是對(duì)Android App,只能用Navigator。
文檔
目前的文檔都可以在這里找到,這里我列出常用的部分。
常用參數(shù)
- configureScene,類(lèi)型是function,可選。通過(guò)這個(gè)參數(shù)可以修改界面在導(dǎo)航時(shí)候切換的動(dòng)畫(huà)。
(route, routeStack) => Navigator.SceneConfigs.FloatFromRight
initialRoute,類(lèi)型是對(duì)象,表明最初的Route對(duì)象。一個(gè)Route簡(jiǎn)單來(lái)說(shuō)就是一個(gè)界面,Navigator用Route來(lái)區(qū)分不同的界面。
navigationBar,類(lèi)型是node,導(dǎo)航欄
renderScene,類(lèi)型是function,必須參數(shù),在這個(gè)方法里根據(jù)Route來(lái)渲染不同的Scene。
常用函數(shù)
push(route) ,跳轉(zhuǎn)到某一個(gè)Route
pop(),推出當(dāng)前狀態(tài)
popToTop(),推出到第一個(gè)界面
popToRoute(route),推出到某一個(gè)界面
Getting start
創(chuàng)建一個(gè)工程,打開(kāi)終端,到一個(gè)想要的路徑,然后
react-native init LHWeather --verbose
*Tips:調(diào)用上述代碼會(huì)從網(wǎng)絡(luò)上下載幾十M的數(shù)據(jù),所以添加–verbose不會(huì)讓你等的心煩意亂。 *
然后
react-native run-ios
會(huì)啟動(dòng)模擬器,如果啟動(dòng)成功,說(shuō)明項(xiàng)目創(chuàng)建成功。
我們首先會(huì)創(chuàng)建一個(gè)這樣效果工程

這時(shí)候的index.ios.js中代碼如下
var React = require('react');
import {
View,
Text,
StyleSheet,
AppRegistry,
TouchableHighlight,
Navigator,
} from 'react-native';
var LHWeather = React.createClass({
render(){
return (
<Navigator
style = {styles.container}
initialRoute={{id:"main",}}
renderScene={this.renderNav}
/>
);
},
renderNav(route,nav){
switch (route.id) {
case 'main':
return <MainScreen navigator={nav} title="Main"/ >;
case 'detail':
return (<DetailScreen navigator={nav} title="Detail"/ >);
}
}
});
var MainScreen = React.createClass({
toDetail(){
this.props.navigator.push({id:"detail"});
},
render(){
return (
<View style={styles.containView}>
<TouchableHighlight
style={styles.button}
onPress={this.toDetail}
underlayColor="#B5B5B5">
<Text style={styles.blackText}>=>詳情頁(yè)</Text>
</TouchableHighlight>
</View>
);
}
});
var DetailScreen = React.createClass({
toMain(){
this.props.navigator.pop();
},
render(){
return (
<View style={styles.detailContainView}>
<TouchableHighlight
style={styles.button}
onPress={this.toMain}
underlayColor="green">
<Text style={styles.blackText}>{'主頁(yè)<='}</Text>
</TouchableHighlight>
</View>
);
}
});
const styles = StyleSheet.create({
container: {
flex: 1,
},
button: {
padding: 15,
},
containView:{
flex: 1,
justifyContent: 'center',
},
detailContainView:{
flex:1,
justifyContent: 'center',
backgroundColor:'green',
},
blackText:{
fontSize:20,
textAlign:'center',
},
});
AppRegistry.registerComponent('LHWeather', () => LHWeather);
簡(jiǎn)單講解下關(guān)于Navigator的部分
<Navigator
style = {styles.container}
initialRoute={{id:"main",}}
renderScene={this.renderNav}
/>
renderNav(route,nav){
switch (route.id) {
case 'main':
return <MainScreen navigator={nav} title="Main"/ >;
case 'detail':
return (<DetailScreen navigator={nav} title="Detail"/ >);
}
}
- 上述代碼中的Route很簡(jiǎn)單,只是一個(gè)含有屬性id的Object,然后,通過(guò)id來(lái)判斷需要渲染成哪個(gè)Scene。
-
navigator={nav}用來(lái)把當(dāng)前navigator的引用傳遞給實(shí)際要展示的Scene
設(shè)置轉(zhuǎn)場(chǎng)動(dòng)畫(huà)類(lèi)型和手勢(shì)
將上文的Navigator修改成如下
<Navigator
style = {styles.container}
initialRoute={{id:"main",}}
renderScene={this.renderNav}
configureScene={(route, routeStack) => Navigator.SceneConfigs.FloatFromBottom}
/>
這樣,轉(zhuǎn)場(chǎng)動(dòng)畫(huà)會(huì)變成從底部推到頂部??蛇x的參數(shù)如下
Navigator.SceneConfigs.PushFromRight (default)
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
添加導(dǎo)航欄
為Navigator繼續(xù)添加一個(gè)導(dǎo)航欄navigationBar
<Navigator
style = {styles.container}
initialRoute={{id:"main",title:"Main"}}
renderScene={this.renderNav}
configureScene={(route, routeStack) => Navigator.SceneConfigs.HorizontalSwipeJump}
navigationBar={
<Navigator.NavigationBar
routeMapper={NavigationBarRouteMapper}
style={styles.navBar}
/>
}
/>
這里提到了另外一個(gè)控件Navigator.NavigatorBar,這個(gè)內(nèi)嵌的NavigatorBar控件,一個(gè)屬性是
- routeMapper
*這個(gè)屬性的目的是告訴Navigator如何根據(jù)每個(gè)Sence渲染自己 *
這里的NavigationBarRouteMapper是
var NavigationBarRouteMapper = {
//左邊Button
LeftButton: function(route, navigator, index, navState) {
if (route.id === 'main') {
return null;
}
var previousRoute = navState.routeStack[index - 1];
return (
<TouchableOpacity
onPress={() => navigator.pop()}
style={styles.navBarLeftButton}>
<Text style={[styles.navBarText, styles.navBarButtonText]}>
{previousRoute.title}
</Text>
</TouchableOpacity>
);
},
//右邊Button
RightButton: function(route, navigator, index, navState) {
if (route.id === 'detail') {
return null;
}
return (
<TouchableOpacity
onPress={() => navigator.push({id:'detail',title:'Detail'})}
style={styles.navBarRightButton}>
<Text style={[styles.navBarText, styles.navBarButtonText]}>
Next
</Text>
</TouchableOpacity>
);
},
//標(biāo)題
Title: function(route, navigator, index, navState) {
return (
<Text style={[styles.navBarText, styles.navBarTitleText]}>
{route.title}
</Text>
);
},
};
注:LeftButton、RightButton、Title 缺一不可,如果不想顯示可以返回nulll
同時(shí),要在StylesSheet中添加用到的Style
navBar: {
backgroundColor: 'white',
},
navBarText: {
fontSize: 16,
marginVertical: 10,
},
navBarTitleText: {
color: '#373E4D',
fontWeight: '500',
marginVertical: 9,
},
navBarLeftButton: {
paddingLeft: 10,
},
navBarRightButton: {
paddingRight: 10,
},
navBarButtonText: {
color: '#5890FF',
},
并且import中添加
TouchableOpacity,
這時(shí)候的效果如下

添加返回圖片
到這里就很簡(jiǎn)單了,只需要在LeftButton中換成顯示一張圖片
修改navBarLeftButton的Style
navBarLeftButton: {
paddingLeft: 10,
paddingTop:6,
},
然后,添加一個(gè)Style
backImage:{
width:13,
height:26,
},
修改LeftButton
<TouchableOpacity
onPress={() => navigator.pop()}
style={styles.navBarLeftButton}>
<Image source={require('./back.png')} style={styles.backImage}>
</Image>
</TouchableOpacity>
傳遞數(shù)據(jù)
Navigator傳遞數(shù)據(jù)使用Route
例如,在Main Screen中,傳遞data 給Route
toDetail(){
this.props.navigator.push({id:"detail",title:"Detail",data:"Passed from Main screen"});
},
然后,渲染的時(shí)候,傳遞data給DetailScreen的props
case 'detail':
return (<DetailScreen navigator={nav} title="Detail" data={route.data}/>);
然后,獲取數(shù)據(jù),進(jìn)行渲染
Text style={styles.blackText}>{this.props.data}</Text>
最后
附上完整代碼
/**
* Sample React Native App
* https://github.com/facebook/react-native
* @flow
*/
var React = require('react');
import {
View,
Text,
StyleSheet,
AppRegistry,
TouchableHighlight,
Navigator,
TouchableOpacity,
Image,
} from 'react-native';
var NavigationBarRouteMapper = {
LeftButton: function(route, navigator, index, navState) {
if (route.id === 'main') {
return null;
}
var previousRoute = navState.routeStack[index - 1];
return (
<TouchableOpacity
onPress={() => navigator.pop()}
style={styles.navBarLeftButton}>
<Image source={require('./back.png')} style={styles.backImage}>
</Image>
</TouchableOpacity>
);
},
RightButton: function(route, navigator, index, navState) {
if (route.id === 'detail') {
return null;
}
return (
<TouchableOpacity
onPress={() => navigator.push({id:'detail',title:'Detail'})}
style={styles.navBarRightButton}>
<Text style={[styles.navBarText, styles.navBarButtonText]}>
Next
</Text>
</TouchableOpacity>
);
},
Title: function(route, navigator, index, navState) {
return (
<Text style={[styles.navBarText, styles.navBarTitleText]}>
{route.title}
</Text>
);
},
};
var LHWeather = React.createClass({
render(){
return (
<Navigator
style = {styles.container}
initialRoute={{id:"main",title:"Main"}}
renderScene={this.renderNav}
configureScene={(route, routeStack) => Navigator.SceneConfigs.HorizontalSwipeJump}
navigationBar={
<Navigator.NavigationBar
routeMapper={NavigationBarRouteMapper}
style={styles.navBar}
/>
}
/>
);
},
renderNav(route,nav){
switch (route.id) {
case 'main':
return <MainScreen navigator={nav} title="Main"/ >;
case 'detail':
return (<DetailScreen navigator={nav} title="Detail" data={route.data}/>);
}
}
});
var MainScreen = React.createClass({
toDetail(){
this.props.navigator.push({id:"detail",title:"Detail",data:"Passed from Main screen"});
},
render(){
return (
<View style={styles.containView}>
<TouchableHighlight
style={styles.button}
onPress={this.toDetail}
underlayColor="#B5B5B5">
<Text style={styles.blackText}>=>詳情頁(yè)</Text>
</TouchableHighlight>
</View>
);
}
});
var DetailScreen = React.createClass({
toMain(){
this.props.navigator.pop();
},
render(){
return (
<View style={styles.detailContainView}>
<TouchableHighlight
style={styles.button}
onPress={this.toMain}
underlayColor="green">
<Text style={styles.blackText}>{this.props.data}</Text>
</TouchableHighlight>
</View>
);
}
});
const styles = StyleSheet.create({
backImage:{
width:13,
height:26,
},
container: {
flex: 1,
},
navBar: {
backgroundColor: 'white',
},
button: {
padding: 15,
},
containView:{
flex: 1,
backgroundColor:'gray',
justifyContent: 'center',
},
detailContainView:{
flex:1,
justifyContent: 'center',
backgroundColor:'green',
},
blackText:{
fontSize:20,
textAlign:'center',
},
navBar: {
backgroundColor: 'white',
},
navBarText: {
fontSize: 16,
marginVertical: 10,
},
navBarTitleText: {
color: '#373E4D',
fontWeight: '500',
marginVertical: 9,
},
navBarLeftButton: {
paddingLeft: 10,
paddingTop:6,
},
navBarRightButton: {
paddingRight: 10,
},
navBarButtonText: {
color: '#5890FF',
},
});
AppRegistry.registerComponent('LHWeather', () => LHWeather);
附navigationBar的另一種寫(xiě)法:
import React, { Component, PropTypes } from 'react';
import {
Image,
View,
Text,
TouchableHighlight,
StyleSheet,
Navigator,
Dimensions,
TouchableOpacity,
} from 'react-native';
export default class MyScene extends Component {
renderScene(route, navigator) {
return (
<View style={styles.container}>
<Image source={{uri: GLOBAL.WebRoot + 'web/img/home.png'}} style={{width: 96, height: 96, borderRadius: 8}}/>
</View>
);
}
renderNavTitle(route, navigator) {
return (
<Text style={styles.navBarTitleText}>
{route.title}
</Text>
);
}
renderNavLeftButton(route, navigator) {
var title = "";
return (
<TouchableOpacity
onPress={() => {}}
style={styles.navBarLeftButton}>
<Image source={{uri: GLOBAL.WebRoot + 'web/img/scan.png'}} style={styles.scan} />
</TouchableOpacity>
);
}
renderNavRightButton(route, navigator) {
var title = "";
return (
<TouchableOpacity
style={styles.navBarRightButton}>
<Text style={styles.navBarButtonText}>
{title}
</Text>
</TouchableOpacity>
);
}
render() {
var bar = (
<Navigator.NavigationBar
routeMapper={{
LeftButton:this.renderNavLeftButton.bind(this),
RightButton:this.renderNavRightButton.bind(this),
Title:this.renderNavTitle.bind(this),
}}
style={styles.navBar}
/>
);
return (
<Navigator style={styles.navigator}
navigationBar={bar}
renderScene={this.renderScene.bind(this)}
initialRoute={{title:'首頁(yè)'}}
/>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#f3f3f3',
},
navigator: {
flex:1,
justifyContent: 'center',
alignItems: 'stretch',
},
navBar: {
backgroundColor: '#262929',
justifyContent: 'center',
},
navBarLeftButton: {
paddingLeft: 10,
marginVertical: 2,
},
navBarRightButton: {
paddingRight: 10,
},
navBarTitleText: {
fontSize:20,
fontWeight: '400',
marginVertical: 9,
width: 3 * (Dimensions.get('window').width) / 5,
textAlign: 'center',
color: '#ffffff',
},
navBarButtonText: {
fontSize: 15,
color: 'white',
marginVertical: 16,
},
scan: {
width: 32,
height: 32,
marginVertical: 5,
}
});