项目作者: guangqiang-liu

项目描述 :
对导航组件react-navigation的使用讲解
高级语言: JavaScript
项目地址: git://github.com/guangqiang-liu/react-navigation-demo.git
创建时间: 2017-11-30T10:11:38Z
项目社区:https://github.com/guangqiang-liu/react-navigation-demo

开源协议:MIT License

下载


react-navigation导航组件使用详解

注意了,如果有小伙伴们发现运行作者提供的react-navigation示例项目报如下的错误,可能是大家使用了 yarn install 命令,解决这个错误的办法就是将nodemodules删除,然后使用npm install命令来安装,最后使用 npm start 来起服务,应该就不报错了,如果还有报错,请加作者交流群,将问题反馈到群里,谢谢。

RN技术总结

  • 作者React Native开源项目OneM地址(按照企业开发标准搭建框架完成开发的):https://github.com/guangqiang-liu/OneM (欢迎小伙伴们 star)
  • 作者React Native QQ技术交流群:620792950 欢迎小伙伴进群交流学习
  • 友情提示:在开发中有遇到RN相关的技术问题,欢迎小伙伴加入交流群(620792950),在群里提问、互相交流学习。交流群也定期更新最新的RN学习资料给大家,谢谢支持!

前言

react-navigation 组件是官方推荐使用的导航组件,功能和性能都远远的优于之前的Navigator组件,公司的RN项目最早是使用的react-native-router-flux导航组件,因为那个时候react-navigation 组件还没有出来,在使用了react-navigation后,感觉比react-native-router-flux组件有更加强大的功能,体验也略好些,这两个导航组件是目前star最多的导航组件,并且他们都完美的支持与Redux框架的结合使用,推荐小伙伴们两个组件都尝试使用下。

react-navigation官方地址

react-navigation

react-navigation Demo地址

https://github.com/guangqiang-liu/react-navigation-demo

react-navigation简书讲解地址

http://www.jianshu.com/p/5c070a302192

Demo示例讲解包含三部分

  • react-navigation中最常用的基础用法讲解
  • react-navigation中StackNavigator与TabNavigator和DrawerNavigator的混合嵌套使用
  • react-navigation与Redux框架结合使用示例

Demo效果图

gif

注意: 有小伙伴说Demo运行报错,这里大家需要注意,Demo clone下来之后,我们先要执行 npm install 操作, 然后在执行 react-native link,最后在 执行 npm start 来运行项目,如果还有其他的报错信息,欢迎进群提出报错信息

对Redux用法不熟悉的同学们,请看作者的Redux入门讲解

http://www.jianshu.com/p/faa98d8bd3fa

react-navigation 主要组成

react-navigation 组件主要由三大部分组成

  • StackNavigator:类似于iOS中的UINavigationController,顶部的导航栏,主要用于页面间的跳转
  • TabNavigator:类似于iOS中的UITabBarController,底部的tabBar选项卡,主要用于在同一tab下的不同页面的切换
  • DrawerNavigator:类似于iOS中常用的抽屉功能,抽屉导航栏

下面我们对react-navigation详解也主要围绕这三个API来展开

StackNavigator

StackNavigator导航栏的工作原理就和iOS中原生的UINavigationController一样的,是以栈的方式来管理每一个页面控制器的,当使用push就是入栈,当使用pop操作时就是出栈,这个很好理解,如果我们想让一个页面控制器有导航栏,那么我们首先要做的就是给这个页面注册导航

API

StackNavigator(RouteConfigs, StackNavigatorConfig)

StackNavigator函数中有两个参数:

  • RouteConfigs
  • StackNavigatorConfig

配置RouteConfigs

  1. const RouteConfigs = {
  2. Home: {
  3. screen: TabBar // screen属性为必须配置属性
  4. },
  5. Home2: {
  6. screen: Home2,
  7. path:'app/Home2',
  8. navigationOptions: {
  9. title: '这是在RouteConfigs中设置的title',
  10. headerTitleStyle: {
  11. fontSize: 10
  12. }
  13. }
  14. },
  15. Home3: { screen: Home3 },
  16. Home4: { screen: Home4 },
  17. Home5: {screen: Home5},
  18. Home6: {screen: Home6},
  19. Home7: {screen: Home7},
  20. Setting2: {screen: Setting2},
  21. Setting3: {screen: Setting3},
  22. }

配置StackNavigatorConfig

  1. const StackNavigatorConfig = {
  2. initialRouteName: 'Home',
  3. initialRouteParams: {initPara: '初始页面参数'},
  4. navigationOptions: {
  5. title: '标题',
  6. headerTitleStyle: {fontSize: 18, color: 'red'},
  7. headerStyle: {height: 49},
  8. },
  9. paths: 'page/main',
  10. mode: 'card',
  11. headerMode: 'screen',
  12. cardStyle: {backgroundColor: "#ffffff"},
  13. transitionConfig: (() => ({
  14. })),
  15. onTransitionStart: (() => {
  16. console.log('页面跳转动画开始')
  17. }),
  18. onTransitionEnd: (() => {
  19. console.log('页面跳转动画结束')
  20. }),
  21. }

注册导航

  1. import {StackNavigator, TabNavigator} from "react-navigation"
  2. const Navigator = StackNavigator(RouteConfigs, StackNavigatorConfig)
  3. export default class Main extends Component {
  4. render() {
  5. return (
  6. <Navigator></Navigator>
  7. )
  8. }
  9. }

从上面注册导航的代码块中,我们可以看出StackNavigator函数接受两个配置对象RouteConfigsStackNavigatorConfig,但是这里需要注意,第二个参数StackNavigatorConfig可以省略,表示不做任何导航默认配置

StackNavigatorConfig配置参数

  • initialRouteName:导航器组件中初始显示页面的路由名称,如果不设置,则默认第一个路由页面为初始显示页面
  • initialRouteParams:给初始路由的参数,在初始显示的页面中可以通过this.props.navigation.state.params来获取
  • navigationOptions:路由页面的全局配置项
  • paths:RouteConfigs里面路径设置的映射
  • mode:页面跳转方式,有card和modal两种,默认为 card
    • card:普通app常用的左右切换
    • modal:只针对iOS平台,类似于iOS中的模态跳转,上下切换
  • headerMode:页面跳转时,头部的动画模式,有 float 、 screen 、 none 三种
    • float:渐变,类似iOS的原生效果,无透明,默认方式
    • screen:标题与屏幕一起淡入淡出,如微信QQ的一样
    • none:没有动画
  • cardStyle:为各个页面设置统一的样式,比如背景色,字体大小等
  • transitionConfig:配置页面跳转的动画,覆盖默认的动画效果
  • onTransitionStart:页面跳转动画即将开始时调用
  • onTransitionEnd:页面跳转动画一旦完成会马上调用

在StackNavigatorConfig配置参数中有一个navigationOptions属性的配置,这个配置项可以理解为导航栏的全局配置表,下面就讲解这个属性的可配置参数

navigationOptions配置参数

  • title:导航栏的标题,或者Tab标题 tabBarLabel
  • header:自定义的头部组件,使用该属性后系统的头部组件会消失,如果想在页面中自定义,可以设置为null,这样就不会出现页面中留有一个高度为64navigationBar的高度
  • headerTitle:头部的标题,即页面的标题
  • headerBackTitle:返回标题,默认为 title的标题
  • headerTruncatedBackTitle:返回标题不能显示时(比如返回标题太长了)显示此标题,默认为’Back’
  • headerRight:头部右边组件
  • headerLeft:头部左边组件
  • headerStyle:头部组件的样式
  • headerTitleStyle:头部标题的样式
  • headerBackTitleStyle:头部返回标题的样式
  • headerTintColor:头部颜色
  • headerPressColorAndroid:Android 5.0 以上MD风格的波纹颜色
  • gesturesEnabled:否能侧滑返回,iOS 默认 true , Android 默认 false

navigationOptions

  1. // StackNavigatorConfig中的navigationOptions属性也可以在组件内用static navigationOptions 设置(会覆盖此处的设置)
  2. navigationOptions: {
  3. header: { // 导航栏相关设置项
  4. backTitle: '返回', // 左上角返回键文字
  5. style: {
  6. backgroundColor: '#fff'
  7. },
  8. titleStyle: {
  9. color: 'green'
  10. }
  11. },
  12. cardStack: {
  13. gesturesEnabled: true
  14. }
  15. }

注意:

  • 我们也可以在RouteConfigs中配置 navigationOptions属性,我们也可以在单独页面配置navigationOptions
  • 在页面里面采用静态的方式配置 navigationOptions属性,会覆盖StackNavigator函数中RouteConfigsStackNavigatorConfig对象中的navigationOptions属性里面的对应属性
  • navigationOptions中属性的优先级是:页面中静态配置 > RouteConfigs > StackNavigatorConfig

RouteConfigs中配置 navigationOptions

  1. const RouteConfigs = {
  2. Home: {
  3. screen: TabBar
  4. },
  5. Home2: {
  6. screen: Home2,
  7. path:'app/Home2',
  8. // 此处设置了, 会覆盖组件内的`static navigationOptions`设置. 具体参数详见下文
  9. navigationOptions: {
  10. title: '这是在RouteConfigs中设置的title',
  11. headerTitleStyle: {
  12. fontSize: 10
  13. }
  14. }
  15. },
  16. Home3: { screen: Home3 },
  17. Home4: { screen: Home4 },
  18. Home5: {screen: Home5},
  19. Home6: {screen: Home6},
  20. Home7: {screen: Home7},
  21. Setting2: {screen: Setting2},
  22. Setting3: {screen: Setting3},
  23. }

在具体页面中配置 navigationOptions

  1. import {StackNavigator, TabNavigator} from "react-navigation"
  2. const Navigator = StackNavigator(RouteConfigs, StackNavigatorConfig)
  3. export default class Main extends Component {
  4. // 配置页面导航选项
  5. static navigationOptions = {
  6. title: 'homeThree',
  7. header: (navigation, defaultHeader) => ({
  8. ...defaultHeader, // 默认预设
  9. visible: true // 覆盖预设中的此项
  10. }),
  11. cardStack: {
  12. gesturesEnabled: false // 是否可以右滑返回
  13. }
  14. }
  15. // 或这样
  16. static navigationOptions = {
  17. // title: 'Two', // 固定标题
  18. title: (navigation, childRouter) => { // 动态标题
  19. if (navigation.state.params.isSelected) {
  20. return `${navigation.state.params.name}选中`;
  21. } else {
  22. return `${navigation.state.params.name}没选中`;
  23. }
  24. },
  25. header: ({ state, setParams, goBack }) => {
  26. let right;
  27. if (state.params.isSelected) {
  28. right = (<Button title="取消" onPress={() => setParams({ isSelected: false })}/>);
  29. } else {
  30. right = (<Button title="选择" onPress={() => setParams({ isSelected: true })}/>);
  31. }
  32. let left = (<Button title="返回" onPress={() => goBack()}/>);
  33. let visible = false; // 是否显示导航栏
  34. return { right, left, visible };
  35. },
  36. // header: {left: <Button title="返回"></Button>},
  37. }
  38. render() {
  39. return (
  40. <Navigator></Navigator>
  41. )
  42. }
  43. }

TabNavigator

API

TabNavigator(RouteConfigs, TabNavigatorConfig)

从API上看,TabNavigator 和 StackNavigator 函数用法一样,都是接受RouteConfigs和TabNavigatorConfig这两个参数

RouteConfigs配置参数

路由配置和StackNavigator中一样,配置路由以及对应的 screen 页面,navigationOptions 为对应路由页面的配置选项

  • title:Tab标题,可用作headerTitle 和 tabBarLabel 回退标题
  • tabBarVisible:Tab的是否可见,默认为 true
  • tabBarIcon:Tab的icon组件,可以根据 {focused: boolean, tintColor: string} 方法来返回一个icon组件
  • tabBarLabel:Tab中显示的标题字符串或者组件,也可以根据{ focused: boolean, tintColor: string } 方法返回一个组件

配置RouteConfigs

  1. const RouteConfigs = {
  2. Home: {
  3. screen: Home,
  4. navigationOptions: ({ navigation }) => ({
  5. tabBarLabel: 'Home',
  6. tabBarIcon: ({ focused, tintColor }) => (
  7. <Ionicons
  8. name={focused ? 'ios-home' : 'ios-home-outline'}
  9. size={26}
  10. style={{ color: tintColor }}></Ionicons>
  11. )
  12. }),
  13. },
  14. People: {
  15. screen: People,
  16. navigationOptions: ({ navigation }) => ({
  17. tabBarLabel: 'People',
  18. tabBarIcon: ({ focused, tintColor }) => (
  19. <Ionicons
  20. name={focused ? 'ios-people' : 'ios-people-outline'}
  21. size={26}
  22. style={{ color: tintColor }}></Ionicons>
  23. )
  24. }),
  25. },
  26. Chat: {
  27. screen: Chat,
  28. navigationOptions: ({ navigation }) => ({
  29. tabBarLabel: 'Chat',
  30. tabBarIcon: ({ focused, tintColor }) => (
  31. <Ionicons
  32. name={focused ? 'ios-chatboxes' : 'ios-chatboxes-outline'}
  33. size={26}
  34. style={{ color: tintColor }}></Ionicons>
  35. )
  36. }),
  37. },
  38. Setting: {
  39. screen: Setting,
  40. navigationOptions: ({ navigation }) => ({
  41. tabBarLabel: 'Settings',
  42. tabBarIcon: ({ focused, tintColor }) => (
  43. <Ionicons
  44. name={focused ? 'ios-settings' : 'ios-settings-outline'}
  45. size={26}
  46. style={{ color: tintColor }}></Ionicons>
  47. )
  48. }),
  49. }
  50. }

TabNavigatorConfig配置参数

  • tabBarComponent: Tab选项卡组件,有TabBarBottom和TabBarTop两个值,在iOS中默认为 TabBarBottom ,在Android中默认为 TabBarTop
    • TabBarTop:在页面的顶部
    • TabBarBottom:在页面的底部
  • tabBarPosition:Tab选项卡的位置,有top或bottom两个值
    • top:上
    • bottom:下
  • swipeEnabled:是否可以滑动切换Tab选项卡
  • animationEnabled:切换界面是否需要动画
  • lazy:是否懒加载页面
  • initialRouteName:初始显示的Tab对应的页面路由名称
  • order:用路由名称数组来表示Tab选项卡的顺序,默认为路由配置顺序
  • paths: 路径配置
  • backBehavior:androd点击返回键时的处理,有initialRoute 和 none 两个值
    • initailRoute:返回初始界面
    • none:退出
  • tabBarOptions:Tab配置属性,用在TabBarTop和TabBarBottom时有些属性不一致
    • 用在TabBarTop时对应的属性:
      • activeTintColor:选中的文字颜色
      • inactiveTintColor:未选中的文字颜色
      • showIcon:是否显示图标,默认显示
      • showLabel:是否显示标签,默认显示
      • upperCaseLabel:是否使用大写字母,默认使用
      • pressColor:android 5.0以上的MD风格波纹颜色
      • pressOpacity:android 5.0以下或者iOS按下的透明度
      • scrollEnabled:是否可以滚动
      • tabStyle:单个Tab的样式
      • indicatorStyle:指示器的样式
      • labelStyle:标签的样式
      • iconStyle:icon的样式
      • style:整个TabBar的样式
    • 用在TabBarBottom时对应的属性:
      • activeTintColor:选中Tab的文字颜色
      • inactiveTintColor:未选中Tab的的文字颜色
      • activeBackgroundColor:选中Tab的背景颜色
      • inactiveBackgroundColor:未选中Tab的背景颜色
      • showLabel:是否显示标题,默认显示
      • style:整个TabBar的样式
      • labelStyle:标签的样式
      • tabStyle:单个Tab的样式

使用TabBarTop代码示例

  1. import React, {Component} from "react";
  2. import {StackNavigator, TabBarTop, TabNavigator} from "react-navigation";
  3. import HomeScreen from "./index18/HomeScreen";
  4. import NearByScreen from "./index18/NearByScreen";
  5. import MineScreen from "./index18/MineScreen";
  6. export default class MainComponent extends Component {
  7. render() {
  8. return (
  9. <Navigator></Navigator>
  10. );
  11. }
  12. }
  13. const TabRouteConfigs = {
  14. Home: {
  15. screen: HomeScreen,
  16. navigationOptions: ({navigation}) => ({
  17. tabBarLabel: '首页',
  18. }),
  19. },
  20. NearBy: {
  21. screen: NearByScreen,
  22. navigationOptions: {
  23. tabBarLabel: '附近',
  24. },
  25. }
  26. ,
  27. Mine: {
  28. screen: MineScreen,
  29. navigationOptions: {
  30. tabBarLabel: '我的',
  31. },
  32. }
  33. };
  34. const TabNavigatorConfigs = {
  35. initialRouteName: 'Home',
  36. tabBarComponent: TabBarTop,
  37. tabBarPosition: 'top',
  38. lazy: true,
  39. tabBarOptions: {}
  40. };
  41. const Tab = TabNavigator(TabRouteConfigs, TabNavigatorConfigs);
  42. const StackRouteConfigs = {
  43. Tab: {
  44. screen: Tab,
  45. }
  46. };
  47. const StackNavigatorConfigs = {
  48. initialRouteName: 'Tab',
  49. navigationOptions: {
  50. title: '标题',
  51. headerStyle: {backgroundColor: '#5da8ff'},
  52. headerTitleStyle: {color: '#333333'},
  53. }
  54. };
  55. const Navigator = StackNavigator(StackRouteConfigs, StackNavigatorConfigs);

使用TabBarBottom代码示例

  1. import React, {Component} from 'react';
  2. import {StackNavigator, TabBarBottom, TabNavigator} from "react-navigation";
  3. import HomeScreen from "./index18/HomeScreen";
  4. import NearByScreen from "./index18/NearByScreen";
  5. import MineScreen from "./index18/MineScreen";
  6. import TabBarItem from "./index18/TabBarItem";
  7. export default class MainComponent extends Component {
  8. render() {
  9. return (
  10. <Navigator></Navigator>
  11. );
  12. }
  13. }
  14. const TabRouteConfigs = {
  15. Home: {
  16. screen: HomeScreen,
  17. navigationOptions: ({navigation}) => ({
  18. tabBarLabel: '首页',
  19. tabBarIcon: ({focused, tintColor}) => (
  20. <TabBarItem
  21. tintColor={tintColor}
  22. focused={focused}
  23. normalImage={require('./img/tabbar/pfb_tabbar_homepage_2x.png')}
  24. selectedImage={require('./img/tabbar/pfb_tabbar_homepage_selected_2x.png')}
  25. ></TabBarItem>
  26. ),
  27. }),
  28. },
  29. NearBy: {
  30. screen: NearByScreen,
  31. navigationOptions: {
  32. tabBarLabel: '附近',
  33. tabBarIcon: ({focused, tintColor}) => (
  34. <TabBarItem
  35. tintColor={tintColor}
  36. focused={focused}
  37. normalImage={require('./img/tabbar/pfb_tabbar_merchant_2x.png')}
  38. selectedImage={require('./img/tabbar/pfb_tabbar_merchant_selected_2x.png')}
  39. ></TabBarItem>
  40. ),
  41. },
  42. }
  43. ,
  44. Mine: {
  45. screen: MineScreen,
  46. navigationOptions: {
  47. tabBarLabel: '我的',
  48. tabBarIcon: ({focused, tintColor}) => (
  49. <TabBarItem
  50. tintColor={tintColor}
  51. focused={focused}
  52. normalImage={require('./img/tabbar/pfb_tabbar_mine_2x.png')}
  53. selectedImage={require('./img/tabbar/pfb_tabbar_mine_selected_2x.png')}
  54. ></TabBarItem>
  55. ),
  56. },
  57. }
  58. };
  59. const TabNavigatorConfigs = {
  60. initialRouteName: 'Home',
  61. tabBarComponent: TabBarBottom,
  62. tabBarPosition: 'bottom',
  63. lazy: true,
  64. };
  65. const Tab = TabNavigator(TabRouteConfigs, TabNavigatorConfigs);
  66. const StackRouteConfigs = {
  67. Tab: {
  68. screen: Tab,
  69. }
  70. };
  71. const StackNavigatorConfigs = {
  72. initialRouteName: 'Tab',
  73. navigationOptions: {
  74. title: '标题',
  75. headerStyle: {backgroundColor: '#5da8ff'},
  76. headerTitleStyle: {color: '#333333'},
  77. }
  78. };
  79. const Navigator = StackNavigator(StackRouteConfigs, StackNavigatorConfigs);

DrawerNavigator

API

DrawerNavigator(RouteConfigs, DrawerNavigatorConfig)

DrawerNavigatorStackNavigatorTabNavigator函数的使用方式一样,参数配置也类似

路由配置和StackNavigator中一样,配置路由以及对应的 screen 页面,navigationOptions 为对应路由页面的配置选项

RouteConfigs参数配置

  • title:抽屉标题,和headerTitle 、 drawerLabel一样
  • drawerLabel:标签字符串,或者自定义组件,可以根据{ focused: boolean, tintColor: string } 函数来返回一个自定义组件作为标签
  • drawerIcon:抽屉icon,可以根据 { focused: boolean, tintColor: string } 函数来返回一个自定义组件作为icon

DrawerNavigatorConfig参数配置

  • drawerWidth:抽屉宽度,可以使用Dimensions获取屏幕的宽度动态计算
  • drawerPosition:抽屉位置,可以是 left 或者 right
  • contentComponent:抽屉内容组件,可以自定义侧滑抽屉中的所有内容,默认为 DrawerItems
  • contentOptions:用来配置抽屉内容的属性。当用来配置 DrawerItems 是配置属性选项
    • items:抽屉栏目的路由名称数组,可以被修改
    • activeItemKey:当前选中页面的key id
    • activeTintColor:选中条目状态的文字颜色
    • activeBackgroundColor:选中条目的背景色
    • inactiveTintColor:未选中条目状态的文字颜色
    • inactiveBackgroundColor:未选中条目的背景色
    • onItemPress(route) :条目按下时会调用此方法
    • style:抽屉内容的样式
    • labelStyle:抽屉的条目标题/标签样式
  • initialRouteName:初始化展示的页面路由名称
  • order:抽屉导航栏目顺序,用路由名称数组表示
  • paths:路径
  • backBehavior:androd点击返回键时的处理,有initialRoute和none两个值
    • initailRoute:返回初始界面
    • none :退出

示例代码

  1. import React, {Component} from 'react';
  2. import {DrawerNavigator, StackNavigator, TabBarBottom, TabNavigator} from "react-navigation";
  3. import HomeScreen from "./index18/HomeScreen";
  4. import NearByScreen from "./index18/NearByScreen";
  5. import MineScreen from "./index18/MineScreen";
  6. import TabBarItem from "./index18/TabBarItem";
  7. const RouteConfigs = {
  8. Home: {
  9. screen: HomeScreen,
  10. navigationOptions: ({navigation}) => ({
  11. drawerLabel : '首页',
  12. drawerIcon : ({focused, tintColor}) => (
  13. <TabBarItem
  14. tintColor={tintColor}
  15. focused={focused}
  16. normalImage={require('./img/tabbar/pfb_tabbar_homepage_2x.png')}
  17. selectedImage={require('./img/tabbar/pfb_tabbar_homepage_selected_2x.png')}
  18. ></TabBarItem>
  19. ),
  20. }),
  21. },
  22. NearBy: {
  23. screen: NearByScreen,
  24. navigationOptions: {
  25. drawerLabel : '附近',
  26. drawerIcon : ({focused, tintColor}) => (
  27. <TabBarItem
  28. tintColor={tintColor}
  29. focused={focused}
  30. normalImage={require('./img/tabbar/pfb_tabbar_merchant_2x.png')}
  31. selectedImage={require('./img/tabbar/pfb_tabbar_merchant_selected_2x.png')}
  32. ></TabBarItem>
  33. ),
  34. },
  35. },
  36. Mine: {
  37. screen: MineScreen,
  38. navigationOptions: {
  39. drawerLabel : '我的',
  40. drawerIcon : ({focused, tintColor}) => (
  41. <TabBarItem
  42. tintColor={tintColor}
  43. focused={focused}
  44. normalImage={require('./img/tabbar/pfb_tabbar_mine_2x.png')}
  45. selectedImage={require('./img/tabbar/pfb_tabbar_mine_selected_2x.png')}
  46. ></TabBarItem>
  47. ),
  48. },
  49. }
  50. };
  51. const DrawerNavigatorConfigs = {
  52. initialRouteName: 'Home',
  53. tabBarComponent: TabBarBottom,
  54. tabBarPosition: 'bottom',
  55. lazy: true,
  56. tabBarOptions: {}
  57. };
  58. const Drawer = DrawerNavigator(RouteConfigs, DrawerNavigatorConfigs)
  59. const StackRouteConfigs = {
  60. Drawer: {
  61. screen: Drawer,
  62. }
  63. };
  64. const StackNavigatorConfigs = {
  65. initialRouteName: 'Drawer',
  66. navigationOptions: {
  67. title: '标题',
  68. headerStyle: {backgroundColor: '#5da8ff'},
  69. headerTitleStyle: {color: '#333333'},
  70. }
  71. }
  72. const Navigator = StackNavigator(StackRouteConfigs, StackNavigatorConfigs)
  73. export default class Main extends Component {
  74. render() {
  75. return (
  76. <Navigator></Navigator>
  77. )
  78. }
  79. }

navigation

在StackNavigator中注册过的组件都有navigation这个属性,navigation有5个主要参数

  • navigate
  • state
  • setParams
  • goBack
  • dispatch

我们平时使用react-navigation作为导航组件来开发时,经常使用到的也就是这5个属性的功能

navigate

导航到下一个页面

  1. <Button
  2. buttonStyle={{marginVertical: 10}}
  3. title={'跳转到Home2界面'}
  4. onPress={() => this.props.navigation.navigate('Home2')}
  5. />
  • 导航到下一个页面并传递参数
  1. <Button
  2. buttonStyle={{marginVertical: 10}}
  3. title={'跳转到Home3界面,并传递参数'}
  4. // 这里传递了参数`id`
  5. onPress={() => this.props.navigation.navigate('Home3', {id: 123})}
  6. />

navigation中的navigate函数可以接受三个参数

  • routeName:注册过的目标路由名称,也就是准备跳转到的页面路由地址(例如上面的Home3)
  • params:跳转到下一个页面,传递的参数(例如上面的id)
  • action:下文有讲到

state

state属性包含有传递过来的三个参数 params、key 、routeName

  • routeName:注册过的目标路由名称
  • key:路由身份标识
  • params:跳转时传递的参数

获取state中的参数:this.props.navigation.state.params.id 这样就能拿到上一个页面传递的参数:id

setParams

this.props.navigation.setParams(): 该方法允许界面更改router中的参数,可以用来动态的更改导航栏的内容。比如可以用来更新头部的按钮或者标题等

使用场景:重写导航按钮的返回按钮,自定义返回事件

  1. export default class Home5 extends Component {
  2. static navigationOptions = ({navigation, screenProps}) => ({
  3. title: 'Home5',
  4. headerLeft: (
  5. <Button
  6. title={'customAction'}
  7. onPress={() => navigation.state.params.customAction()}
  8. />
  9. )
  10. }
  11. )
  12. componentDidMount() {
  13. const {setParams} = this.props.navigation
  14. setParams({customAction: () => this.tempAction()})
  15. }
  16. tempAction() {
  17. alert('在导航栏按钮上调用Component内中的函数,因为static修饰的函数为静态函数,内部不能使用this')
  18. }
  19. render() {
  20. return (
  21. <View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
  22. <Text>Home5</Text>
  23. </View>
  24. )
  25. }
  26. }

goBack

退出当前页面,返回到上一个页面,可以不传参数,也可以传参数,还可以传 null

  • this.props.navigation.goBack(); // 回退到上一个页面
  • this.props.navigation.goBack(null); // 回退到任意一个页面
  • this.props.navigation.goBack(‘Home’); // 回退到Home页面
  1. <Button
  2. title={'返回上一页面'}
  3. onPress={() => goBack()}
  4. />

dispatch

this.props.navigation.dispatch: 可以dispatch一些action,主要支持的action有一下几种

  • Navigate
  1. import { NavigationActions } from 'react-navigation'
  2. const navigationAction = NavigationActions.navigate({
  3. routeName: 'Profile',
  4. params: {},
  5. // navigate can have a nested navigate action that will be run inside the child router
  6. action: NavigationActions.navigate({ routeName: 'SubProfileRoute'})
  7. })
  8. this.props.navigation.dispatch(navigationAction)
  • Reset

Reset方法会清除原来的路由记录,添加上新设置的路由信息, 可以指定多个action,index是指定默认显示的那个路由页面, 注意不要越界了

  1. import { NavigationActions } from 'react-navigation'
  2. const resetAction = NavigationActions.reset({
  3. index: 0,
  4. actions: [
  5. NavigationActions.navigate({ routeName: 'Profile'}),
  6. NavigationActions.navigate({ routeName: 'Two'})
  7. ]
  8. })
  9. this.props.navigation.dispatch(resetAction)
  • SetParams

为指定的router更新参数,该参数必须是已经存在于router的param中

  1. import { NavigationActions } from 'react-navigation'
  2. const setParamsAction = NavigationActions.setParams({
  3. params: {}, // these are the new params that will be merged into the existing route params
  4. // The key of the route that should get the new params
  5. key: 'screen-123',
  6. })
  7. this.props.navigation.dispatch(setParamsAction)
  • Back
  1. NavigationActions.back()
  • Init
  1. const initialState = Navigator.router.getStateForAction(NavigationActions.init());
  2. export default (state = initialState, actions) => {
  3. const nextState = Navigator.router.getStateForAction(actions, state);
  4. return nextState || state;
  5. }

注意: 如果你的项目中使用了与Redux框架结合,这里的dispatch就可以派发任何你想dispatch的Action了

使用场景:Counter 计数器

  1. class Counter extends Component {
  2. static navigationOptions = () => ({
  3. title: 'Counter加减计数器'
  4. })
  5. render() {
  6. const {dispatch} = this.props.navigation
  7. return (
  8. <View>
  9. <Text>Counter</Text>
  10. <Text style={{marginVertical: 20, color: 'red', fontSize: 30}}>{this.props.counterValue}</Text>
  11. <Button
  12. buttonStyle={{marginVertical: 10}}
  13. title={'+'}
  14. onPress={() => dispatch(increaseAction())}
  15. />
  16. <Button
  17. buttonStyle={{marginVertical: 10}}
  18. title={'-'}
  19. onPress={() => dispatch(decreaseAction())}
  20. />
  21. </View>
  22. )
  23. }
  24. }
  25. const mapStateToProps = state => {
  26. return {
  27. counterValue: state.home.counter.counterValue
  28. }
  29. }
  30. export default connect(mapStateToProps)(Counter)

常用功能:页面跳转、页面传值、参数回调

页面跳转与传值

  1. <Button
  2. buttonStyle={{marginVertical: 10}}
  3. title={'跳转到Home2界面'}
  4. onPress={() => this.props.navigation.navigate('Home2')}
  5. />
  6. <Button
  7. buttonStyle={{marginVertical: 10}}
  8. title={'跳转到Home3界面,并传递参数'}
  9. onPress={() => this.props.navigation.navigate('Home3', {id: 123})}
  10. />

在下一界面接收参数,通过this.props.navigation.state.params接收参数

  1. export default class Home3 extends Component {
  2. render() {
  3. const {navigate} = this.props.navigation
  4. return (
  5. <View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
  6. <Text>Home3</Text>
  7. <Text style={{marginVertical: 20}}>{`Home界面传递的参数为:${this.props.navigation.state.params.id}`}</Text>
  8. <Button
  9. buttonStyle={{marginVertical: 10}}
  10. title={'跳转到Home4界面'}
  11. onPress={() => navigate('Home4')}
  12. />
  13. </View>
  14. )
  15. }
  16. }

回调传参

当前界面进行跳转,并传递参数

  1. <Button
  2. buttonStyle={{marginVertical: 10}}
  3. title={'跳转到Home6界面,回调传参'}
  4. onPress={() => this.props.navigation.navigate('Home6',{callback: (response) => alert(response)})}
  5. />

下一界面在返回之前执行函数回调传参给上一个页面

  1. const {state, goBack} = this.props.navigation
  2. <Button
  3. title={'回调传参'}
  4. onPress={() => {
  5. state.params.callback && state.params.callback('这是回调参数')
  6. goBack()
  7. }}
  8. />

DeepLink

其他app或浏览器使用url打开次app并进入指定页面,类似于iOS中的URL导航一样。如浏览器输入url OneM://home/home2直接进入home2页面

iOS平台需要额外配置

  • 在info.plist文件中设置Schemes,示例中的Schemes为:OneM
  • 在AppDelegate添加代理函数
  1. - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url
  2. sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
  3. return [RCTLinkingManager application:application openURL:url
  4. sourceApplication:sourceApplication annotation:annotation];
  5. }

在js中配置

js组件在注册路由时设置唯一的路径path, 例如Home2: { screen: Home2, path:’home/Home2’}

在手机浏览器访问OneM://home/Home2, 弹窗选择打开, 就可以打开OneM app并进到Home2页面了

开发中遇到的问题及注意事项

  • 默认DrawerView不可滚动。要实现可滚动视图,必须使用contentComponent自定义容器
  1. {
  2. drawerWidth200,
  3. contentComponentprops => <ScrollView> <DrawerItems {... props}></DrawerItems> </ ScrollView>
  4. }
  • 可以覆盖导航使用的默认组件,使用DrawerItems自定义导航组件
  1. import {DrawerItems} from 'react-navigation';
  2. const CustomDrawerContentComponent = (props) => (
  3. <View style = {style.container}>
  4. <DrawerItems {... props} ></DrawerItems>
  5. </View>
  6. )
  • 嵌套抽屉导航 如果嵌套DrawerNavigation,抽屉将显示在父导航下方

  • 适配顶部导航栏标题

测试中发现,在iphone上标题栏的标题为居中状态,而在Android上则是居左对齐。所以需要我们修改源码,进行适配
【node_modules – react-navigation – src – views – Header.js】的326行代码处,修改为如下:

  1. title: {
  2. bottom: 0,
  3. left: TITLE_OFFSET,
  4. right: TITLE_OFFSET,
  5. top: 0,
  6. position: 'absolute',
  7. alignItems: 'center',
  8. }

上面方法通过修改源码的方式其实略有弊端,毕竟扩展性不好。还有另外一种方式就是,在navigationOptions中设置headerTitleStyle的alignSelf为 ’ center ‘即可解决

  • 去除返回键文字显示

【node_modules – react-navigation – src – views – HeaderBackButton.js】的91行代码处,修改为如下即可

  1. {
  2. Platform.OS === 'ios' &&
  3. title &&
  4. <Text
  5. onLayout={this._onTextLayout}
  6. style={[styles.title, { color: tintColor }]}
  7. numberOfLines={1}
  8. >
  9. {backButtonTitle}
  10. </Text>
  11. }

将上述代码删除即可

  • 动态设置头部按钮事件

当我们在头部设置左右按钮时,肯定避免不了要设置按钮的单击事件,但是此时会有一个问题,navigationOptions是被修饰为static类型的,所以我们在按钮的onPress的方法中不能直接通过this来调用Component中的方法。如何解决呢?在官方文档中,作者给出利用设置params的思想来动态设置头部标题。那么我们可以利用这种方式,将单击回调函数以参数的方式传递到params,然后在navigationOption中利用navigation来取出设置到onPress即可:

  1. export default class Home5 extends Component {
  2. static navigationOptions = ({navigation, screenProps}) => ({
  3. title: 'Home5',
  4. headerRight: (
  5. <Button
  6. title={'customAction'}
  7. onPress={() => navigation.state.params.customAction()}
  8. />
  9. )
  10. }
  11. )
  12. componentDidMount() {
  13. const {setParams} = this.props.navigation
  14. setParams({customAction: () => this.tempAction()})
  15. }
  16. tempAction() {
  17. alert('在导航栏按钮上调用Component内中的函数,因为static修饰的函数为静态函数,内部不能使用this')
  18. }
  19. render() {
  20. return (
  21. <View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
  22. <Text>Home5</Text>
  23. </View>
  24. )
  25. }
  26. }
  • 结合BackHandler处理返回和点击返回键两次退出App效果

点击返回键两次退出App效果的需求屡见不鲜。相信很多人在react-navigation下实现该功能都遇到了很多问题,例如,其他界面不能返回。也就是手机本身返回事件在react-navigation之前拦截了。如何结合react-natigation实现呢?和大家分享两种实现方式:

  1. 在注册StackNavigator的界面中,注册BackHandler
  1. componentWillMount(){
  2. BackHandler.addEventListener('hardwareBackPress', this._onBackAndroid );
  3. }
  4. componentUnWillMount(){
  5. BackHandler.addEventListener('hardwareBackPress', this._onBackAndroid);
  6. }
  7. _onBackAndroid=()=>{
  8. let now = new Date().getTime();
  9. if(now - lastBackPressed < 2500) {
  10. return false;
  11. }
  12. lastBackPressed = now;
  13. ToastAndroid.show('再点击一次退出应用',ToastAndroid.SHORT);
  14. return true;
  15. }
  1. 监听react-navigation的Router
  1. /**
  2. * 处理安卓返回键
  3. */
  4. const defaultStateAction = AppNavigator.router.getStateForAction;
  5. AppNavigator.router.getStateForAction = (action,state) => {
  6. if(state && action.type === NavigationActions.BACK && state.routes.length === 1) {
  7. if (lastBackPressed + 2000 < Date.now()) {
  8. ToastAndroid.show(Constant.hint_exit,ToastAndroid.SHORT);
  9. lastBackPressed = Date.now();
  10. const routes = [...state.routes];
  11. return {
  12. ...state,
  13. ...state.routes,
  14. index: routes.length - 1,
  15. };
  16. }
  17. }
  18. return defaultStateAction(action,state);
  19. }
  • 实现Android中界面跳转左右切换动画

react-navigation在android中默认的界面切换动画是上下。如何实现左右切换呢?很简单的配置即可:

  1. import CardStackStyleInterpolator from 'react-navigation/src/views/CardStackStyleInterpolator';

然后在StackNavigator的配置下添加如下代码:

  1. transitionConfig:()=>({
  2. screenInterpolator: CardStackStyleInterpolator.forHorizontal,
  3. })
  • 解决快速点击多次跳转

当我们快速点击跳转时,会开启多个重复的界面,如何解决呢。其实在官方Git中也有提示,解决这个问题需要修改react-navigation源码:
找到scr文件夹中的addNavigationHelpers.js文件,替换为如下文本即可:

  1. export default function<S: *>(navigation: NavigationProp<S, NavigationAction>) {
  2. // 添加点击判断
  3. let debounce = true;
  4. return {
  5. ...navigation,
  6. goBack: (key?: ?string): boolean =>
  7. navigation.dispatch(
  8. NavigationActions.back({
  9. key: key === undefined ? navigation.state.key : key,
  10. }),
  11. ),
  12. navigate: (routeName: string,
  13. params?: NavigationParams,
  14. action?: NavigationAction,): boolean => {
  15. if (debounce) {
  16. debounce = false;
  17. navigation.dispatch(
  18. NavigationActions.navigate({
  19. routeName,
  20. params,
  21. action,
  22. }),
  23. );
  24. setTimeout(
  25. () => {
  26. debounce = true;
  27. },
  28. 500,
  29. );
  30. return true;
  31. }
  32. return false;
  33. },
  34. /**
  35. * For updating current route params. For example the nav bar title and
  36. * buttons are based on the route params.
  37. * This means `setParams` can be used to update nav bar for example.
  38. */
  39. setParams: (params: NavigationParams): boolean =>
  40. navigation.dispatch(
  41. NavigationActions.setParams({
  42. params,
  43. key: navigation.state.key,
  44. }),
  45. ),
  46. }
  47. }

待补充问题

  • hook tabBar上点击事件
  • Android物理返回键处理
  • navigator与tabBar嵌套
  • tabBar上添加badge
  • pop多层页面
  • pop到指定页面
  • navigator与抽屉嵌套使用
  • 导航title 在Android 平台上不居中显示
  • 双击物理键,退出app
  • 懒加载tabbar上数据

针对上面的待补充问题,下面来逐一解答

  • hook tabBar上点击事件

有时我们点击tabBar上的tab来切换页面,但是在切换页面之前我们想先做一些逻辑处理,然后在切换到tab页面,这时我们就需要先hook到这个tab的点击事件,下面代码块就是告诉你如何hook到tab的点击事件,处理完事件在打开tab页面,这个使用具体使用方式在示例Demo中都有实际使用,不清楚的同学们直接去运行示例项目了解即可

  1. Chat: {
  2. screen: Chat,
  3. navigationOptions: ({ navigation }) => ({
  4. tabBarLabel: 'Chat',
  5. tabBarOnPress: () => {
  6. Alert.alert(
  7. '注意!',
  8. '这里做了hook tabBar的点击事件操作,我们可以hook到这个点击事件,处理我们想要处理的业务后再打开 Chat这个页面',
  9. [
  10. {text: '打开tab页面', onPress: () => navigation.navigate('Chat')},
  11. {text: 'Cancel', onPress: () => console.log('Cancel Pressed'), style: 'cancel'},
  12. ],
  13. { cancelable: false }
  14. )
  15. },
  16. tabBarIcon: ({ focused, tintColor }) => (
  17. <Ionicons
  18. name={focused ? 'ios-chatboxes' : 'ios-chatboxes-outline'}
  19. size={26}
  20. style={{ color: tintColor }}></Ionicons>
  21. )
  22. }),
  23. },
  • Android物理返回键处理(待更新)

  • navigator与tabBar嵌套

navigator与tabBar嵌套 具体的结合使用方式示例Demo中有给出具体示例,这个同学们直接运行示例Demo查看即可

  • tabBar上添加badge

之前有不少同学问我,怎么给一个tabBar设置badge,前段时间由于太忙,一直没有去处理这个问题,后面去实现了下自定义badge,感觉还是挺简单的,因为navigation的tabBarItem本来就是支持自定义的,既然能够自定义,那实现badge自然也是可行的了,下面就是具体实现代码块

  1. People: {
  2. screen: People,
  3. navigationOptions: ({ navigation }) => ({
  4. tabBarLabel: 'People',
  5. tabBarIcon: ({ focused, tintColor }) => (
  6. <View style={{position: 'absolute', top: -10}}>
  7. <Ionicons
  8. name={focused ? 'ios-people' : 'ios-people-outline'}
  9. size={26}
  10. style={{ color: tintColor }}></Ionicons>
  11. <View style={{backgroundColor: 'red', position: 'absolute', right: -10, top: -5, height: 15, width: 20, borderRadius: 8, overflow: 'hidden'}}>
  12. <Text style={{fontSize: 12, textAlign: 'center'}}>10</Text>
  13. </View>
  14. </View>
  15. )
  16. }),
  17. },
  • pop多层页面

有时候我们在开发的时候,难免会遇到在点击返回按钮的时候,想直接返回到指定的某一个页面,而不是返回上一级页面,这时我们就需要对goback()函数做些处理了,具体的代码实现如下

  1. const Navigator = StackNavigator(RouteConfigs, StackNavigatorConfig)
  2. const defaultStateAction = Navigator.router.getStateForAction;
  3. Navigator.router.getStateForAction = (action, state) => {
  4. if (state && action.key && action.type === 'Navigation/BACK') {
  5. const desiredRoute = state.routes.find((route) => route.routeName === action.key)
  6. if (desiredRoute) {
  7. const index = state.routes.indexOf(desiredRoute)
  8. const finalState = {
  9. ...state,
  10. routes: state.routes.slice(0, index + 1),
  11. index: index,
  12. };
  13. return finalState
  14. } else {
  15. if (state.routes.length > action.key) {
  16. const stacksLength = state.routes.length - action.key
  17. const stacks = state.routes.slice(0, stacksLength)
  18. const finalState = {
  19. ...state,
  20. routes: stacks,
  21. index: stacksLength - 1,
  22. };
  23. return finalState
  24. }
  25. }
  26. }
  27. return defaultStateAction(action, state)
  28. }
  • pop到指定页面

其实goback()函数,是很容易的就可以做到返回到指定页面,和返回指定层级的页面的,并不像网上其他的文章说的需要改源码啊,或者是需要结合redux才能实现啊,并不是这样的,只需要我们简单的维护下导航的路由栈即可解决问题,这个其实和原生iOS中处理导航的栈管理是一个道理

  1. const Navigator = StackNavigator(RouteConfigs, StackNavigatorConfig)
  2. const defaultStateAction = Navigator.router.getStateForAction;
  3. Navigator.router.getStateForAction = (action, state) => {
  4. if (state && action.key && action.type === 'Navigation/BACK') {
  5. const desiredRoute = state.routes.find((route) => route.routeName === action.key)
  6. if (desiredRoute) {
  7. const index = state.routes.indexOf(desiredRoute)
  8. const finalState = {
  9. ...state,
  10. routes: state.routes.slice(0, index + 1),
  11. index: index,
  12. };
  13. return finalState
  14. } else {
  15. if (state.routes.length > action.key) {
  16. const stacksLength = state.routes.length - action.key
  17. const stacks = state.routes.slice(0, stacksLength)
  18. const finalState = {
  19. ...state,
  20. routes: stacks,
  21. index: stacksLength - 1,
  22. };
  23. return finalState
  24. }
  25. }
  26. }
  27. return defaultStateAction(action, state)
  28. }
  • navigator与抽屉嵌套使用

navigator与抽屉嵌套使用的方式,示例Demo中已经有具体实现了,这个比较简单,就不做详细解答了

  • 导航title 在Android 平台上不居中显示

    简书上面的开发中遇到的问题及注意事项中有讲解决办法,不过作者还是建议大家将导航栏封装成一个组件,使用自定义的组件灵活性更高

  • 双击物理键,退出app(待更新)

  • 懒加载tabbar上数据

这个懒加载Tab,这个也没有什么好解答的,官方已经给我提供了设置属性,我们只需要设置个属性即可,具体代码如下

  1. const TabNavigatorConfigs = {
  2. initialRouteName: 'Home',
  3. lazy: true,
  4. tabBarOptions: {
  5. activeTintColor: Platform.OS === 'ios' ? '#e91e63' : '#fff',
  6. }
  7. }

总结

react-navigation导航组件的API相对较多,如果小伙伴们看完讲解还是不清楚如何使用,建议直接运行Demo项目,边调试边理解。

针对之前很多同学反映出关于react-navigation 使用上遇到的一些问题,上面基本上都逐一解答了,并且都在示例Demo实战的测试过是可行方案,后期还有其他的小伙伴遇到使用上的问题,欢迎进群讨论,或者是给我简书留言,谢谢大家的支持。