Learn once, navigate anywhere.
React Navigation的诞生,源于React Native社区对基于Javascript的可扩展且使用简单的导航解决方案的需求
只需安装react-navigation
的npm package, 就可以开始使用React Navigation了
安装react-navigation
- 使用 NPM 安装
npm install --save react-navigation
或者使用yarn安装
yarn add react-navigation
要开始使用React Navigation,您必须先创建一个navigator,React Navigation带有三种默认的navigator。
- StackNavigator - 为应用程序提供了一种页面切换的方法,每次切换时,新的页面会放置在堆栈的顶部
- TabNavigator - 用于设置具有多个Tab页的页面
- DrawerNavigator - 用于设置抽屉导航的页面
StackNavigator
'use strict';
import React,{Component} from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
Button,
PixelRatio,
} from 'react-native';
//1. 导入文件
import { StackNavigator } from 'react-navigation';
//2. 编写页面
const HomeScreen = ({navigation}) =>(
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Home Screen</Text>
//5. 从Home页面跳转到Details页面
<Button
onPress={()=>navigation.navigate('Details')}
title='Go to Details'
></Button>
</View>
);
const Details = ()=>(
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Details Screen</Text>
</View>
);
//3.创建StackNavigator
const RootNavigator = StackNavigator({
Home:{
screen:HomeScreen,
//4. 添加标题
navigationOptions:{
headerTitle:'home',
},
},
Details:{
screen:Details,
navigationOptions:{
headerTitle:'details',
},
}
});
export default RootNavigator;
效果图
TabNavigator
我们将在例子中使用
react-native-vector-icons
, 如果你的项目中没有安装,请自行安装。
react-native-vector-icons ICONS是矢量图,可以直接使用图片名, 就能加载图片的第三方,使用很方便, 你不需要在工程文件夹里塞各种图片, 节省很多空间,
图片库
图片库
icons 使用
代码
'use strict';
import React,{Component} from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
Button,
PixelRatio,
} from 'react-native';
//1. 导入文件
import { createBottomTabNavigator } from 'react-navigation';
import Ionicons from 'react-native-vector-icons/Ionicons';
//2.创建一些页面
const HomeScreen=()=>(
<View style={{flex:1,alignItems:'center',justifyContent:'center'}}>
<Text>HomeScreen</Text>
</View>
);
const ProfileScreen=()=>(
<View style={{flex:1,alignItems:'center',justifyContent:'center'}}>
<Text>ProfileScreen</Text>
</View>
);
//3. 添加TabNavigator中
const RootTabs = createBottomTabNavigator({
Home:{
screen:HomeScreen,
navigationOptions:{
//4. 创建标签
tabBarLabel:'MyHome',
//5 .添加图标
tabBarIcon:({tintColor,focused})=>(
<Ionicons
name={focused?'ios-home' : 'md-home'}
size={26}
style={focused?{ color: tintColor }:{color:'#f00'}}
>
</Ionicons>
),
},
},
Profile:{
screen:ProfileScreen,
navigationOptions:{
tabBarLabel:'MyProfile',
tabBarIcon:({tintColor,focused})=>(
<Ionicons
name={'ios-aperture'}
size={26}
style={{color:tintColor}}
>
</Ionicons>
),
}
},
});
//最后导出
export default RootTabs;
效果图
出现的几个问题
- 消除警告
"Method jumpToIndex is deprecated. Please upgrade your code to use jumpTo instead. Change your code from jumpToIndex(1) to `jumpTo('Parties')."
这个警告的意思是把TabNavigator
替换成createBottomTabNavigator
问题解决
import { createBottomTabNavigator } from 'react-navigation';
const RootTabs = createBottomTabNavigator({...});
- react-native-vector-icons 使用问题
- 安装时注意
npm install react-native-vector-icons --save
或者
yarn add react-native-vector-icons
注意:目前npm5存在安装新库时会删除其他库的问题,可能后面会修复导致项目无法正常运行。请尽量使用yarn代替npm操作。
执行以下命令链接原生库
react-native link react-native-vector-icons
ios配置(ios需要单独导入字体文件,安卓link完就可以用了)
ios必须要配置 如果不配置会报错Unrecognized font family ‘Ionicons’
如果还是报错
我的解决方法
在node_modules
目录找到react-native-vector-icons
目录,删除掉,使用yarn
命令重新下载,link
命令,ios配置,关闭所有服务,使用xcode
安装。问题解决。
DrawerNavigator
代码
'use strict';
import React,{Component} from 'react';
352152
import {
AppRegistry,
StyleSheet,
Text,
View,
Button,
PixelRatio,
} from 'react-native';
import { DrawerNavigator } from 'react-navigation';
import Ionicons from 'react-native-vector-icons/Ionicons';
const HomeScreen=({navigation})=>(
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>HomeScreen</Text>
<Button
onPress={()=>(navigation.toggleDrawer())}
title="Open Drawer"
></Button>
</View>
);
const ProfileScreen=()=>(
<View style={{flex: 1, alignItems: 'center', justifyContent: 'center'}}><Text>ProfileScreen</Text></View>
);
const RootDrawer =DrawerNavigator({
Home:{
screen:HomeScreen,
navigationOptions:{
drawerLabel:'MyHome',
drawerIcon:({tintColor, focused })=>(
<Ionicons
name={'ios-home'}
size={20}
style={{ color: tintColor }}
></Ionicons>
),
},
},
Profile:{
screen:ProfileScreen,
navigationOptions:{
drawerLabel:'MyProfile',
drawerIcon:({tintColor})=>(
<Ionicons
name={'ios-person'}
size={20}
style={{color:tintColor}}
></Ionicons>
),
},
}
});
export default RootDrawer;
效果图
点击Button会打开侧拉菜单
嵌套导航
标题仅适用于StackNavigator。 本来想切换tab页面,标题也跟着切换,折腾了半天也没做出来。官方文档说的意思是标题仅适用于StackNavigator
这个例子意思是:StackNavigator导航里面嵌套TabNavigator导航。
把StackNavigator的主页面替换成了TabNavigator两个tab页面,在tab页面点击按钮跳转到StackNavigator导航页面并携带数据。
'use strict';
import React,{Component} from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
Button,
PixelRatio,
} from 'react-native';
import { StackNavigator } from 'react-navigation';
import { createBottomTabNavigator } from "react-navigation";
/**
* Home页面
*/
class HomeScreen extends Component{
static navigationOptions={
title:'Welcome',
};
render(){
const { navigate } = this.props.navigation;
return(
<View>
<Text>Hello, Navigation!</Text>
<Button
onPress={ ()=>{navigate('Chat',{user:'Lucy'})}}
title='Chat with Lucy'
></Button>
</View>
);
}
}
/**
* 聊天界面
*/
class ChatScreen extends Component{
static navigationOptions=({navigation})=>({
title:navigation.state.params.user,
});
render(){
const userTitle=this.props.navigation.state.params.user;
const {params} = this.props.navigation.state;
return(
<Text>{params.user}</Text>
);
}
}
class RecentChatsScreen extends Component{
render(){
return(
<View>
<Text>List of recent chats</Text>
<Button
onPress={ () => {this.props.navigation.navigate('Chat',{user:'RecentChatsScreen'})}}
title='Chat with Lucy'
></Button>
</View>
);
}
}
class AllContactsScreen extends Component{
render(){
return(
<View>
<Text>List of all contacts</Text>
<Button
onPress={ () => {this.props.navigation.navigate('Chat',{user:'AllContactsScreen'})}}
title='Chat with Lucy'
></Button>
</View>
);
}
}
/**
* 页面嵌套
*/
export const MainScreenNavigator=createBottomTabNavigator({
Recent:{
screen:RecentChatsScreen,
navigationOptions:{
headerTitle:'My Chat',
}
},
All:{
screen:AllContactsScreen,
navigationOptions:{
headerTitle:'My All',
}
},
});
export const SimpleApp=StackNavigator({
HomeScreen:{
screen:MainScreenNavigator,
navigationOptions:{
title:'My Chats',
}
},
Chat:{
screen:ChatScreen,
Navigation:{
}
}
});
AppRegistry.registerComponent('MyApp', () => SimpleApp);
需要注意一点
点击按钮打开侧拉侧单下面这种做法是错误的。因为过时了。
<Button
onPress={() => navigation.navigate('DrawerToggle')}
title="Open Drawer"
/>
解决办法: 使用最新的API
onPress={()=>(navigation.toggleDrawer())}
标题仅适用于StackNavigator。
添加右侧的按钮
'use strict';
import React,{Component} from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
Button,
PixelRatio,
TextInput,
//屏幕组件与标题的交互 记得导入
ActivityIndicator,
} from 'react-native';
import { StackNavigator } from 'react-navigation';
import { createBottomTabNavigator } from "react-navigation";
/**
* Home页面
*/
class HomeScreen extends Component{
static navigationOptions={
title:'Welcome',
};
render(){
const { navigate } = this.props.navigation;
return(
<View>
<Text>Hello, Navigation!</Text>
<Button
onPress={ ()=>{navigate('Chat',{user:'Lucy'})}}
title='Chat with Lucy'
></Button>
</View>
);
}
}
/**
* 聊天界面
*/
class ChatScreen extends Component{
static navigationOptions=({navigation})=>{
console.log(navigation);
const {state,setParams} = navigation;
const isInfo = state.params.mode === 'info';
const {user} = state.params;
//添加右侧的按钮
//动态修改标题 和 button的title
return{
title: isInfo ? `${user}'s Contact Info` : `Chat with ${state.params.user}`,
headerRight:(
<Button
title={isInfo ? 'Done' : `${user}'s info`}
onPress={()=>{setParams({mode:isInfo?'none' : 'info'})}}
></Button>
),
};
};
render(){
console.log(this);
const userTitle=this.props.navigation.state.params.user;
const {params} = this.props.navigation.state;
return(
<Text>{params.user}</Text>
);
}
}
class RecentChatsScreen extends Component{
render(){
return(
<View>
<Text>List of recent chats</Text>
<Button
onPress={ () => {this.props.navigation.navigate('Chat',{user:'RecentChatsScreen'})}}
title='Chat with Lucy'
></Button>
</View>
);
}
}
class AllContactsScreen extends Component{
render(){
return(
<View>
<Text>List of all contacts</Text>
<Button
onPress={ () => {this.props.navigation.navigate('Chat',{user:'AllContactsScreen'})}}
title='Chat with Lucy'
></Button>
</View>
);
}
}
/**
* 页面嵌套
*/
export const MainScreenNavigator=createBottomTabNavigator({
Recent:{
screen:RecentChatsScreen,
navigationOptions:{
headerTitle:'My Chat',
}
},
All:{
screen:AllContactsScreen,
navigationOptions:{
headerTitle:'My All',
}
},
});
/**
* 屏幕组件与标题的交互
*/
class EditInfoScreen extends Component{
static navigationOptions= ({navigation}) =>{
// 3 获取navigation state 中 params 属性 使用解构赋值 并设置默认值
const {params={}} = navigation.state;
console.log(params);
console.log(params.handleSave);
//设置标题右侧按钮
let headerRight=(
<Button
title='Save'
// handleSave 是函数_handlerSave params.handleSave才会调用函数
onPress={params.handleSave? params.handleSave : ()=>null}
></Button>
);
console.log(params.isSaveing);
if(params.isSaveing){
headerRight = <ActivityIndicator/>;
}
return {headerRight};
}
//2. 编写state属性 存放数据
state={
nickName:'fengxing',
}
componentDidMount(){
this.props.navigation.setParams({handleSave:this._handlerSave});
console.log(this._handlerSave);
}
_handlerSave= ()=>{
console.log('_handlerSave');
this.props.navigation.setParams({isSaveing:true});
//saveInfo().then(()=>{
// this.props.navigation.setParams({isSaveing:false});
//});
}
render(){
return(
//1. 编写组件基本内容
<View>
<TextInput
style={{height: 400, borderColor: 'gray', borderWidth: 1,marginTop:30}}
placeholder={'Nickname'}
onChangeText={ (nickName) => this.setState({nickName}) }
multiline={true}
value={this.state.nickName}
>
</TextInput>
<Button
title='点击'
onPress = { ()=> this.props.navigation.setParams({isSaveing:false}) }
>
</Button>
</View>
);
}
}
/**
* 输出的视图
*/
export const SimpleApp=StackNavigator({
HomeScreen:{
screen:MainScreenNavigator,
navigationOptions:{
title:'My Chats',
}
},
Chat:{
screen:EditInfoScreen,
Navigation:{
}
}
});
AppRegistry.registerComponent('MyApp', () => SimpleApp);
内置导航器
react-navigation
包含以下功能来帮助你创建导航器:
- StackNavigator - 一次只渲染一个页面,并提供页面之间跳转的方法。 当打开一个新的页面时,它被放置在堆栈的顶部
- TabNavigator - 渲染一个选项卡,让用户可以在几个页面之间切换
- DrawerNavigator - 提供一个从屏幕左侧滑入的抽屉
StackNavigator
API 定义
StackNavigator(RouteConfigs, StackNavigatorConfig)
RouteConfigs
路由配置对象是从路由名称到路由配置的映射,告诉导航器该路由应该呈现什么。
StackNavigator({
// For each screen that you can navigate to, create a new entry like this:
Profile: {
// `ProfileScreen` is a React component that will be the main content of the screen.
screen: ProfileScreen,
// When `ProfileScreen` is loaded by the StackNavigator, it will be given a `navigation` prop.
// Optional: When deep linking or using react-navigation in a web app, this path is used:
path: 'people/:name',
// The action and route params are extracted from the path.
// Optional: Override the `navigationOptions` for the screen
navigationOptions: ({navigation}) => ({
title: `${navigation.state.params.name}'s Profile'`,
}),
},
...MyOtherRoutes,
});
StackNavigatorConfig
router
的选项:
-
initialRouteName
- 设置堆栈的默认页面。 必须匹配RouteConfigs
中的一个key
。 -
initialRouteParams
- 初始化路由的参数 -
navigationOptions
- 用于页面的默认导航选项 -
paths
- 用于覆盖RouteConfigs
中设置的path
的一个映射
视觉选项:
-
mode
- 定义页面渲染和转换的风格:-
card
- 使用标准的iOS和Android页面转换风格,此项为缺省。 -
modal
- 使页面从屏幕底部滑入,这是一种常见的iOS模式。 只适用于iOS,在Android上不起作用。
-
-
headerMode
- 定义标题该如何渲染:-
float
- 渲染一个放在顶部的标题栏,并在页面改变时显示动画。 这是iOS上的常见模式。 -
screen
- 每个页面上都有一个标题栏,标题栏与页面一起淡入淡出。 这是Android上的常见模式。 -
none
- 没有标题栏
-
-
cardStyle
- 使用这个属性覆盖或者扩展堆栈中单个Card
的默认样式。 -
transitionConfig
- 返回一个与默认页面的transitionConfig
(参见类型定义)合并的对象的函数。 提供的函数将传递以下参数:-
transitionProps
- 新页面跳转的属性。 -
prevTransitionProps
- 上一个页面跳转的属性 -
isModal
- 指定页面是否为modal
。
-
-
onTransitionStart
-card
跳转动画开始时要调用的函数。 -
onTransitionEnd
-card
跳转动画结束时要调用的函数。
Screen Navigation Options
title
可当作headerTitle
的备用的字符串。 此外,将用作tabBarLabel
(如果嵌套在TabNavigator中)或drawerLabel
(如果嵌套在DrawerNavigator中)的后备。
header
可以是React元素或给定了HeaderProps
然后返回一个React元素的函数,显示为标题。 设置为null
隐藏标题。
headerTitle
字符串、React元素或被当作标题的React组件。默认显示title
属性的值。当使用一个组件时,它会收到allowFontScaling
,style
和children
属性。 标题字符串在children
中进行传递。
headerTitleAllowFontScaling
标题栏中标题字体是否应该缩放取决于文本大小是否可以设置。 默认为true。
headerBackTitle
iOS上的返回按钮的文字使用的字符串,或者使用null
来禁用。 默认为上一个页面的headerTitle
。
headerTruncatedBackTitle
当headerBackTitle
不适合在屏幕显示时(一般是因为文字太多),返回按钮使用的标题字符串。 默认是Back
。
headerRight
显示在标题栏右侧的React元素。
headerLeft
用于在标题栏左侧展示的React元素或组件。当一个组件被渲染时,它会接收到很多的属性(onPress
, title
, titleStyle
等等, - 请检查 Header.js
的完整列表)
headerStyle
标题栏的样式
headerTitleStyle
标题栏中标题的样式
headerBackTitleStyle
标题栏中返回按钮标题的样式
headerTintColor
标题栏的色调
headerPressColorAndroid
material design中的波纹颜色 (仅支持Android >= 5.0)
gesturesEnabled
是否可以使用手势来关闭此页面。 在iOS上默认为true,在Android上默认为false。
gestureResponseDistance
一个对象,用以覆盖从屏幕边缘开始触摸到手势被识别的距离。 它具有以下属性:
-
horizontal
- 数值型 - 水平方向的距离,默认值25 -
vertical
- 数值型 - 垂直方向的距离,默认值135.
字符串,用来设置关闭页面的手势方向,默认(default
)是从做往右,inverted
是从右往左
/**
* 输出的视图
*/
export const SimpleApp=StackNavigator({
HomeScreen:{
screen:MainScreenNavigator,
navigationOptions:{
title:'My Chats',
}
},
Chat:{
screen:EditInfoScreen,
Navigation:{
}
}
},{
//定义标题该如何渲染 none 没有标题 float 淡入淡出 screen
headerMode: 'screen',
//弹出方式
mode:'modal',
navigationOptions: {
//是否可以使用手势来关闭此页面。 在iOS上默认为true,在Android上默认为false。
gesturesEnabled: false,
},
//
transitionConfig:() => ({
transitionSpec:{
duration:300,
easing:Easing.out(Easing.poly(4)),
timing: Animated.timing,
}
})
});
TabNavigator
API 定义
TabNavigator(RouteConfigs, TabNavigatorConfig)
TabNavigatorConfig
-
tabBarComponent
- 用作渲染tab bar
的组件,例如TabBarBottom
(这是iOS上的默认设置),TabBarTop
(这是Android上的默认设置)。 -
tabBarPosition
-tab bar
的位置, 可选值:'top'
or'bottom'
-
swipeEnabled
- 是否允许滑动切换tab页 -
animationEnabled
- 是否在切换tab页时使用动画 -
configureTransition
- 给定currentTransitionProps
和nextTransitionProps
的函数,其返回一个配置对象,该对象用于描述tab页之间的动画 -
initialLayout
- 可以传递包含初始height
和width
的可选对象,用以防止react-native-tab-view渲染中一个帧的延迟 -
tabBarOptions
- 配置tab bar
,详情见下文
传递到底层路由,用于修改导航逻辑的几个选项:
-
initialRouteName
- 第一次加载tab bar
时路由的routeName
-
order
- 定义了tab bar
顺序的一个routeNames
数组 -
paths
- 提供routeName
到path config
的映射,它覆盖了routeConfigs
中设置的path
。 -
backBehavior
- 返回按钮是否会导致tab
切换到初始tab页? 如果是,则设置为initialRoute
,否则为none
。 缺省为initialRoute
。
TabBarBottom
的tabBarOptions
(TabBarBottom
为iOS的默认tab bar
)
-
activeTintColor
- 当前选中的tab bar
的文本颜色和图标颜色 -
activeBackgroundColor
- 当前选中的tab bar
的背景色 -
inactiveTintColor
- 当前未选中的tab bar
的文本颜色和图标颜色 -
inactiveBackgroundColor
- 当前未选中的tab bar
的背景色 -
showLabel
- 是否显示tab bar
的文本,默认是true
-
style
-tab bar
的样式 -
labelStyle
-tab bar
的文本样式 -
tabStyle
-tab
页的样式 -
allowFontScaling
- 文本字体大小是否可以缩放取决于该设置,默认为true。
tabBarOptions: {
activeTintColor: '#e91e63',
labelStyle: {
fontSize: 12,
},
style: {
backgroundColor: 'blue',
},
}
TabBarTop
的tabBarOptions
(TabBarTop
为Android的默认tab bar
)
-
activeTintColor
- 当前选中的tab bar
的文本颜色和图标颜色 -
inactiveTintColor
- 当前未选中的tab bar
的文本颜色和图标颜色 -
showIcon
- 是否显示tab bar
的图标,默认是false
-
showLabel
- 是否显示tab bar
的文本,默认是true
-
upperCaseLabel
- 是否将文本转换为大小,默认是true
-
pressColor
- material design中的波纹颜色(仅支持Android >= 5.0) -
pressOpacity
- 按下tab bar
时的不透明度(仅支持iOS和Android < 5.0). -
scrollEnabled
- 是否允许滑动切换 -
tabStyle
- tab页的样式 -
indicatorStyle
- tab 页指示符的样式 (tab页下面的一条线). -
labelStyle
-tab bar
的文本样式 -
iconStyle
-tab bar
的图标样式 -
style
-tab bar
的样式 -
allowFontScaling
- 文本字体大小是否可以缩放取决于该设置,默认为true。
栗子:
tabBarOptions: {
labelStyle: {
fontSize: 12,
},
tabStyle: {
width: 100,
},
style: {
backgroundColor: 'blue',
},
}
Screen Navigation Options
title
可以用作headerTitle
和 tabBarLabel
后备的通用标题。(headerTitle
和 tabBarLabel
未设置时,就会使用title
的值替代)
tabBarVisible
tab bar
是否可见,缺省是true
swipeEnabled
是否允许tab页之间滑动切换,如果未设置,则使用TabNavigatorConfig
的swipeEnabled
选项
tabBarIcon
用于在tab bar
中展示的React元素或一个传入{ focused: boolean, tintColor: string }
返回React.Node
的函数
tabBarLabel
用于在tab bar
中展示的一个字符串或者一个传入{ focused: boolean, tintColor: string }
返回React.Node
的函数,如果未定义,则使用页面的title
属性。如果像隐藏文本,请参阅上一小节中讲到的tabBarOptions.showLabel
tabBarOnPress
tab被点击时的回调函数;参数是一个对象,包含一下属性:
-
previousScene: { route, index }
:正在离开的页面 -
scene: { route, index }
被点击的页面 -
jumpToIndex
执行跳转操作必须的参
DrawerNavigator
使用...navigate('DrawerOpen')和...navigate('DrawerClose')打开和关闭抽屉
this.props.navigation.navigate('DrawerOpen'); // open drawer
this.props.navigation.navigate('DrawerClose'); // close drawer