写点什么

ReactNative 进阶(七):导航组件 react-navigation

  • 2022 年 1 月 07 日
  • 本文字数:15275 字

    阅读完需:约 50 分钟

ReactNative进阶(七):导航组件 react-navigation

一、前言

RN项目开发过程中,经常会看到如下形式的路由跳转。


render() {   return (     <View>       <Text>2</Text>       <Button           title = "跳转到指定的页面"           onPress = {() => this.props.navigation.push('ChangePassword')}       />       <Button           title = "跳转到指定的页面"           onPress = {() => this.props.navigation.navigate('Home')}       />       <Button           title = "返回上一个页面"           onPress = {() => this.props.navigation.goBack()}       />
<Button title = "返回第一个页面" onPress = {() => this.props.navigation.popToTop()} /> </View> )}
复制代码


在 RN 多页面应用程序开发过程中,页面跳转是通过路由或导航器来实现的。在 0.44 版本之前,开发者可以直接使用官方提供的Navigator组件来实现页面跳转,不过从 0.44 版本开始,Navigator被官方从react native的核心组件库中剥离出来,放到react-native-deprecated-custom-components模块中。


如果开发者需要继续使用Navigator,则需要先执行yarn add react-native-deprecated-custom-components命令后再使用。不过,官方并不建议开发者这么做,而是建议开发者直接使用导航库react-navigationreact-navigation是 React Native 社区非常著名的页面导航库,可以用来实现各种页面的跳转操作。


今天就结合RN官方推荐的路由导航组件react-navigation,深入了解下相关技能知识。

二、总览

React Native 中,官方推荐使用 react-navigation 来实现各个界面的跳转和不同板块的切换。react-navigation据称有原生般的性能体验效果。可能会成为未来React Native导航组件的主流军。


目前,react-navigation支持三种类型的导航器,分别是StackNavigatorTabNavigatorDrawerNavigator。具体区别如下:


1、StackNavigator: 类似于普通的Navigator,屏幕上方导航栏,用于实现各个页面之间的跳转;2、TabNavigator: 相当于iOS里面的TabBarController,屏幕下方的标签栏,主要用于同一个页面上不同界面之间的切换;3、 DrawerNavigator: 抽屉效果,侧边滑出;

三、StackNavigator 导航栏

StackNavigator(RouteConfigs, StackNavigatorConfig)
复制代码


StackNavigator 组件采用堆栈式的页面导航来实现各个界面跳转。如果想要使用StackNavigator,一定要先注册导航


其中,createStackNavigator用于配置栈管理的页面。在createStackNavigator模式下,为了方便对页面进行统一管理,首先新建一个RouterConfig.js文件,并使用createStackNavigator注册页面。对于应用的初始页面还需要使用initialRouteName进行申明。同时,导航器栈还需要使用createAppContainer函数进行包裹。示例代码如下:


RouterConfig.js文件


import {createAppContainer,createStackNavigator} from 'react-navigation';
import MainPage from './MainPage'import DetailPage from "./DetailPage";
const AppNavigator = createStackNavigator({ MainPage: MainPage, DetailPage:DetailPage},{ initialRouteName: "MainPage",},);export default createAppContainer(AppNavigator);
复制代码


接下来,在入口文件中以组件的方式引入StackNavigatorPage.js文件即可。例如:


import StackNavigatorPage from './src/StackNavigatorPage'
export default class App extends Component<Props> { render() { return ( <StackNavigatorPage/> ); }}
复制代码


然后,就可以进行导航配置了。


const Navigator = StackNavigator(    {        Tab: { screen: Tab },        Web: { screen: WebScene },        GroupPurchase: { screen: GroupPurchaseScene },    },    {        navigationOptions: {            // headerStyle: { backgroundColor: color.theme }            headerBackTitle: null,            headerTintColor: '#333333',            showIcon: true,        },    });
复制代码


这里给导航器配置了三个界面,如果需要更多界面,可以在之后进行添加。主要有一个用于主界面之间能够切换的Tab界面和其他两个能够展示数据的界面。

3.1 StackNavigatorConfig 导航器配置

  • initialRouteName - 导航器组件中初始显示页面的路由名称,如果不设置,则默认第一个路由页面为初始显示页面;

  • initialRouteParams - 给初始路由的参数,在初始显示的页面中可以通过this.props.navigation.state.params 来获取;

  • navigationOptions -路由页面的配置选项,它会被 RouteConfigs 参数中navigationOptions 的对应属性覆盖;

  • paths -路由中设置的路径覆盖映射配置;

  • mode - 页面跳转方式,有 cardmodal 两种,默认为 card

  • card - 原生系统默认的的跳转,左右互换;

  • modal - 只针对 iOS 平台,模态跳转,上下切换;

  • headerMode - 页面跳转时,头部的动画模式,有 float 、 screen 、 none 三种:

  • float -渐变,类似iOS的原生效果,无透明;

  • screen - 标题与屏幕一起淡入淡出,如微信一样;

  • none - 没有动画;

  • cardStyle -为各个页面设置统一的样式,比如背景色,字体大小等;

  • transitionConfig - 配置页面跳转的动画,覆盖默认的动画效果;

  • onTransitionStart - 页面跳转动画即将开始时调用;

  • onTransitionEnd - 页面跳转动画一旦完成会马上调用;


代码示例:


const StackNavigatorConfig = {    initialRouteName: 'Home',    initialRouteParams: {initPara: '初始页面参数'},    navigationOptions: {        title: '标题',        headerTitleStyle: {fontSize: 18, color: '#666666'},        headerStyle: {height: 48, backgroundColor: '#'},    },    paths: 'page/main',    mode: 'card',    headerMode: 'screen',    cardStyle: {backgroundColor: "#ffffff"},    transitionConfig: (() => ({        screenInterpolator: CardStackStyleInterpolator.forHorizontal,    })),    onTransitionStart: (() => {        console.log('页面跳转动画开始');    }),    onTransitionEnd: (() => {        console.log('页面跳转动画结束');    }),};
复制代码

3.2 navigationOptions 为对应路由页面的配置选项

  • title - 可以作为头部标题 headerTitle ,或者Tab标题 tabBarLabel;

  • header -自定义的头部组件,使用该属性后系统的头部组件会消失,如果想在页面中自定义,可以设置为null,这样就不会出现页面中留有一个高度为 64 navigationBar 的高度;

  • headerTitle - 头部的标题,即页面的标题 headerBackTitle - 返回标题,默认为 title;

  • headerTruncatedBackTitle - 返回标题不能显示时(比如返回标题太长了)显示此标题,默认为 “Back”;

  • headerRight - 头部右边组件;

  • headerLeft - 头部左边组件;

  • headerStyle - 头部组件的样式;

  • headerTitleStyle - 头部标题的样式;

  • headerBackTitleStyle - 头部返回标题的样式;

  • headerTintColor - 头部颜色 headerPressColorAndroid - Android 5.0以上MD风格的波纹颜色 ;

  • gesturesEnabled - 否能侧滑返回, iOS 默认 trueAndroid 默认 false;


使用示例


// 注册导航const Navs = StackNavigator({    Home: { screen: Tabs },    HomeTwo: {        screen: HomeTwo,  // 必须, 其他都是非必须        path:'app/homeTwo', // 使用url导航时用到, 如 web app 和 Deep Linking        navigationOptions: {}  // 此处参数设置会覆盖组件内的`static navigationOptions`设置. 具体参数详见下文;    },    HomeThree: { screen: HomeThree },    HomeFour: { screen: HomeFour }  }, {    initialRouteName: 'Home', // 默认显示界面    navigationOptions: {  // 屏幕导航的默认选项, 也可以在组件内用 static navigationOptions 设置(会覆盖此处的设置)        header: {  // 导航栏相关设置项            backTitle: '返回',  // 左上角返回键文字            style: {                backgroundColor: '#fff'            },            titleStyle: {                color: 'green'            }        },        cardStack: {            gesturesEnabled: true        }    },     mode: 'card',  // 页面切换模式, 左右是card(相当于iOS中的push效果), 上下是modal(相当于iOS中的modal效果)    headerMode: 'screen', // 导航栏的显示模式, screen: 有渐变透明效果, float: 无透明效果, none: 隐藏导航栏    onTransitionStart: ()=>{ console.log('导航栏切换开始'); },  // 回调    onTransitionEnd: ()=>{ console.log('导航栏切换结束'); }  // 回调});
复制代码


当然页面配置选项 navigationOptions 还可以在对应页面中去静态配置。


    // 配置页面导航选项    static navigationOptions = ({navigation}) => ({        title: 'HOME',        titleStyle: {color: '#ff00ff'},        headerStyle:{backgroundColor:'#000000'}    });
render() { return ( <View></View> ) };}
复制代码


在页面采用静态方式配置 navigationOptions 中的属性,会覆盖 StackNavigator 构造函数中两个参数 RouteConfigsStackNavigatorConfig 配置的 navigationOptions 里面的对应属性。


navigationOptions 中属性的优先级是:


页面中静态配置 > RouteConfigs > StackNavigatorConfig


至此,就可以在组件中直接使用该配置了,当然我们也可以像文章开头提过的那样使用:


const Navigator = StackNavigator(RouteConfigs, StackNavigatorConfig);
export default class Main extends Component { render() { return ( <Navigator/> ) };}
复制代码


至此,我们已经配置好导航器和对应的路由页面,但是我们还需要navigation才能实现页面的跳转。

3.3 navigation 控制页面跳转

在导航器中的每一个页面,都有 navigation 属性,该属性有以下几个属性/方法,可以在组件下console.log(this.props)查看。


  • navigate - 跳转到其他页面;

  • state - 当前页面导航器的状态;

  • setParams - 更改路由的参数;

  • goBack - 返回;

  • dispatch - 发送一个action


navigate


this.props.navigation.navigate(‘Two’, { name: ‘two’ }) // push下一个页面
复制代码


  • routeName: 注册过的目标路由名称;

  • params: 传递的参数,传递到下一级界面 ;

  • action:如果该界面是一个navigator的话,将运行这个sub-action;


statestate 里面包含有传递过来的参数 paramskey 、路由名称 routeName


  • routeName: 路由名;

  • key: 路由身份标识;

  • params: 参数;


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


goBack返回上一页,可以不传参数,也可以传参数,还可以传 null


this.props.navigation.goBack();       // 回退到上一个页面this.props.navigation.goBack(null);   // 回退到任意一个页面this.props.navigation.goBack('Home'); // 回退到Home页面
复制代码


dispatch


this.props.navigation.dispatch
复制代码


可以dispatch一些action,主要支持的action有:


1. Navigate


import { NavigationActions } from 'react-navigation'
const navigationAction = NavigationActions.navigate({ routeName: 'Profile', params: {},
// navigate can have a nested navigate action that will be run inside the child router action: NavigationActions.navigate({ routeName: 'SubProfileRoute'}) }) this.props.navigation.dispatch(navigationAction)
复制代码


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


import { NavigationActions } from 'react-navigation'
const resetAction = NavigationActions.reset({ index: 0, actions: [ NavigationActions.navigate({ routeName: 'Profile'}), NavigationActions.navigate({ routeName: 'Two'}) ] }) this.props.navigation.dispatch(resetAction)
复制代码


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


  import { NavigationActions } from 'react-navigation'
const setParamsAction = NavigationActions.setParams({ params: {}, // these are the new params that will be merged into the existing route params // The key of the route that should get the new params key: 'screen-123', }) this.props.navigation.dispatch(setParamsAction)
复制代码

3.4 页面跳转,传值,回调传参

跳转,传值


  const {navigate} = this.props.navigation;  <TouchableHighlight onPress={()=>{       navigate('PlanDetail',{name:'leslie',id:100});  }}>
复制代码


  // 返回上一页  this.props.navigation.goBack();
复制代码


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


export default class Home1 extends Component {      static navigationOptions = {          // title 可以这样设置成一个函数, state 会自动传过来          title: ({state}) => `${state.params.name}`,      };
componentDidMount() { const {params} = this.props.navigation.state; const id = params.id; } }
复制代码

3.5 回调传参

当前界面进行跳转


navigate('Detail',{   // 跳转的时候携带一个参数去下个页面   callback: (data)=>{         console.log(data); // 打印值为:'回调参数'     }  });
复制代码


下一界面在返回之前传参


const {navigate,goBack,state} = this.props.navigation;// 在第二个页面,在goBack之前,将上个页面的方法取到,并回传参数,这样回传的参数会重走render方法state.params.callback('回调参数');goBack();
复制代码

四、TabNavigator 即 Tab 选项卡

TabNavigator(RouteConfigs, TabNavigatorConfig)
复制代码


apiStackNavigator 类似,参数 RouteConfigs 是路由配置,参数 TabNavigatorConfigTab选项卡配置。


如果要实现底部选项卡切换功能,可以直接使用react-navigation提供的createBottomTabNavigator接口,并且此导航器需要使用createAppContainer函数包裹后才能作为React组件被正常调用。例如:


import React, {PureComponent} from 'react';import {StyleSheet, Image} from 'react-native';import {createAppContainer, createBottomTabNavigator} from 'react-navigation'
import Home from './tab/HomePage' import Mine from './tab/MinePage'
const BottomTabNavigator = createBottomTabNavigator( { Home: { screen: Home, navigationOptions: () => ({ tabBarLabel: '首页', tabBarIcon:({focused})=>{ if(focused){ return( <Image/> //选中的图片 ) }else{ return( <Image/> //默认图片 ) } } }), }, Mine: { screen: Mine, navigationOptions: () => ({ tabBarLabel: '我的', tabBarIcon:({focused})=>{ } }) } }, { //默认参数设置 initialRouteName: 'Home', tabBarPosition: 'bottom', showIcon: true, showLabel: true, pressOpacity: 0.8, tabBarOptions: { activeTintColor: 'green', style: { backgroundColor: '#fff', }, } });
const AppContainer = createAppContainer(BottomTabNavigator);
export default class TabBottomNavigatorPage extends PureComponent { render() { return ( <AppContainer/> ); }}
复制代码

4.1 RouteConfigs 路由配置

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


  • title - Tab标题,可用作headerTitletabBarLabel 回退标题;

  • tabBarVisible -Tab是否可见,没有设置的话默认为 true;

  • tabBarIcon - Tabicon组件,可以根据 {focused:boolean, tintColor: string} 方法来返回一个icon组件;

  • tabBarLabel -Tab中显示的标题字符串或者组件,也可以根据 { focused: boolean, tintColor: string };方法返回一个组件;


代码示例:


Mine: {   screen: MineScene,   navigationOptions: ({ navigation }) => ({       tabBarLabel: '我的',       tabBarIcon: ({ focused, tintColor }) => (           <TabBarItem               tintColor={tintColor}               focused={focused}               normalImage={require('./img/tabbar/pfb_tabbar_mine@2x.png')}               selectedImage={require('./img/tabbar/pfb_tabbar_mine_selected@2x.png')}           />       )   }),},
复制代码


TabBarItem自定义组件


class TabBarItem extends PureComponent {    render() {        let selectedImage = this.props.selectedImage ? this.props.selectedImage : this.props.normalImage        return (            <Image                source={this.props.focused                    ? selectedImage                    : this.props.normalImage}                style={{ tintColor: this.props.tintColor, width: 25, height: 25 }}            />        );    }}
复制代码

五、TabNavigatorConfig Tab 选项卡配置

  • tabBarComponent - Tab 选项卡组件,有 TabBarBottomTabBarTop 两个值,在iOS中默认为 TabBarBottom ,在Android中默认为 TabBarTop

  • TabBarTop - 在页面顶部;

  • TabBarBottom - 在页面底部;

  • tabBarPosition - Tab 选项卡的位置,有topbottom两个值

  • top:上面

  • bottom:下面

  • swipeEnabled - 是否可以滑动切换Tab选项卡;

  • animationEnabled - 点击Tab选项卡切换界面是否需要动画;

  • lazy - 是否懒加载页面;

  • initialRouteName - 初始显示的Tab对应的页面路由名称;

  • order - 用路由名称数组来表示Tab选项卡的顺序,默认为路由配置顺序;

  • paths - 路径配置;

  • backBehavior - android点击返回键时的处理,有 initialRoutenone 两个值:

  • initailRoute - 返回初始界面;

  • none - 退出;

  • tabBarOptions - Tab 配置属性,用在TabBarTopTabBarBottom时有些属性不一致:


用于 TabBarTop 时:


  • activeTintColor - 选中的文字颜色;

  • inactiveTintColor - 未选中的文字颜色;

  • showIcon -是否显示图标,默认显示;

  • showLabel - 是否显示标签,默认显示;

  • upperCaseLabel - 是否使用大写字母,默认使用;

  • pressColor - android 5.0以上的 MD 风格波纹颜色;

  • pressOpacity - android5.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 的样式;


使用底部选项卡:


import React, {Component} from 'react';import {StackNavigator, TabBarBottom, TabNavigator} from "react-navigation";import HomeScreen from "./index18/HomeScreen";import NearByScreen from "./index18/NearByScreen";import MineScreen from "./index18/MineScreen";import TabBarItem from "./index18/TabBarItem";export default class MainComponent extends Component {    render() {        return (            <Navigator/>        );    }}
const TabRouteConfigs = { Home: { screen: HomeScreen, navigationOptions: ({navigation}) => ({ tabBarLabel: '首页', tabBarIcon: ({focused, tintColor}) => ( <TabBarItem tintColor={tintColor} focused={focused} normalImage={require('./img/tabbar/pfb_tabbar_homepage_2x.png')} selectedImage={require('./img/tabbar/pfb_tabbar_homepage_selected_2x.png')} /> ), }), }, NearBy: { screen: NearByScreen, navigationOptions: { tabBarLabel: '附近', tabBarIcon: ({focused, tintColor}) => ( <TabBarItem tintColor={tintColor} focused={focused} normalImage={require('./img/tabbar/pfb_tabbar_merchant_2x.png')} selectedImage={require('./img/tabbar/pfb_tabbar_merchant_selected_2x.png')} /> ), }, } , Mine: { screen: MineScreen, navigationOptions: { tabBarLabel: '我的', tabBarIcon: ({focused, tintColor}) => ( <TabBarItem tintColor={tintColor} focused={focused} normalImage={require('./img/tabbar/pfb_tabbar_mine_2x.png')} selectedImage={require('./img/tabbar/pfb_tabbar_mine_selected_2x.png')} /> ), }, }};const TabNavigatorConfigs = { initialRouteName: 'Home', tabBarComponent: TabBarBottom, tabBarPosition: 'bottom', lazy: true,};const Tab = TabNavigator(TabRouteConfigs, TabNavigatorConfigs);const StackRouteConfigs = { Tab: { screen: Tab, }};const StackNavigatorConfigs = { initialRouteName: 'Tab', navigationOptions: { title: '标题', headerStyle: {backgroundColor: '#5da8ff'}, headerTitleStyle: {color: '#333333'}, }};const Navigator = StackNavigator(StackRouteConfigs, StackNavigatorConfigs);
复制代码


使用顶部选项卡:


import React, {Component} from "react";import {StackNavigator, TabBarTop, TabNavigator} from "react-navigation";import HomeScreen from "./index18/HomeScreen";import NearByScreen from "./index18/NearByScreen";import MineScreen from "./index18/MineScreen";export default class MainComponent extends Component {    render() {        return (            <Navigator/>        );    }}
const TabRouteConfigs = { Home: { screen: HomeScreen, navigationOptions: ({navigation}) => ({ tabBarLabel: '首页', }), }, NearBy: { screen: NearByScreen, navigationOptions: { tabBarLabel: '附近', }, } , Mine: { screen: MineScreen, navigationOptions: { tabBarLabel: '我的', }, }};const TabNavigatorConfigs = { initialRouteName: 'Home', tabBarComponent: TabBarTop, tabBarPosition: 'top', lazy: true, tabBarOptions: {}};const Tab = TabNavigator(TabRouteConfigs, TabNavigatorConfigs);const StackRouteConfigs = { Tab: { screen: Tab, }};const StackNavigatorConfigs = { initialRouteName: 'Tab', navigationOptions: { title: '标题', headerStyle: {backgroundColor: '#5da8ff'}, headerTitleStyle: {color: '#333333'}, }};const Navigator = StackNavigator(StackRouteConfigs, StackNavigatorConfigs);
复制代码


当然,除了支持创建底部选项卡之外,react-navigation还支持创建顶部选项卡,此时只需要使用react-navigation提供的createMaterialTopTabNavigator即可。如果要使用实现抽屉式菜单功能,还可以使用react-navigation提供的createDrawerNavigator

六、DrawerNavigator 抽屉导航

有一些APP都会采用侧滑抽屉来实现主页面导航,利用 DrawerNavigatorRN中可以很方便实现抽屉导航。


DrawerNavigator(RouteConfigs, DrawerNavigatorConfig)
复制代码


TabNavigator的构造函数一样,参数配置也类似。


RouteConfigs抽屉导航的路由配置 RouteConfigs ,和 TabNavigator 的路由配置完全一样, screen 对应路由页面配置, navigationOptions 对应页面的抽屉配置:


  • title - 抽屉标题,和 headerTitledrawerLabel 一样;

  • 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 -android点击返回键时的处理,有initialRoutenone两个值:

  • initailRoute:返回初始界面;

  • none :退出


抽屉导航示例:


import React, {Component} from 'react';import {DrawerNavigator, StackNavigator, TabBarBottom, TabNavigator} from "react-navigation";import HomeScreen from "./index18/HomeScreen";import NearByScreen from "./index18/NearByScreen";import MineScreen from "./index18/MineScreen";import TabBarItem from "./index18/TabBarItem";export default class MainComponent extends Component {    render() {        return (            <Navigator/>        );    }}const DrawerRouteConfigs = {    Home: {        screen: HomeScreen,        navigationOptions: ({navigation}) => ({            drawerLabel : '首页',            drawerIcon : ({focused, tintColor}) => (                <TabBarItem                    tintColor={tintColor}                    focused={focused}                    normalImage={require('./img/tabbar/pfb_tabbar_homepage_2x.png')}                    selectedImage={require('./img/tabbar/pfb_tabbar_homepage_selected_2x.png')}                />            ),        }),    },    NearBy: {        screen: NearByScreen,        navigationOptions: {            drawerLabel : '附近',            drawerIcon : ({focused, tintColor}) => (                <TabBarItem                    tintColor={tintColor}                    focused={focused}                    normalImage={require('./img/tabbar/pfb_tabbar_merchant_2x.png')}                    selectedImage={require('./img/tabbar/pfb_tabbar_merchant_selected_2x.png')}                />            ),        },    },    Mine: {        screen: MineScreen,        navigationOptions: {            drawerLabel : '我的',            drawerIcon : ({focused, tintColor}) => (                <TabBarItem                    tintColor={tintColor}                    focused={focused}                    normalImage={require('./img/tabbar/pfb_tabbar_mine_2x.png')}                    selectedImage={require('./img/tabbar/pfb_tabbar_mine_selected_2x.png')}                />            ),        },    }};const DrawerNavigatorConfigs = {    initialRouteName: 'Home',    tabBarComponent: TabBarBottom,    tabBarPosition: 'bottom',    lazy: true,    tabBarOptions: {}};const Drawer = DrawerNavigator(DrawerRouteConfigs, DrawerNavigatorConfigs);const StackRouteConfigs = {    Drawer: {        screen: Drawer,    }};const StackNavigatorConfigs = {    initialRouteName: 'Drawer',    navigationOptions: {        title: '标题',        headerStyle: {backgroundColor: '#5da8ff'},        headerTitleStyle: {color: '#333333'},    }};const Navigator = StackNavigator(StackRouteConfigs, StackNavigatorConfigs);
复制代码

6.1 扩展功能

默认DrawerView不可滚动。要实现可滚动视图,必须使用contentComponent自定义容器:


{    drawerWidth:200,    抽屉位置:“对”    contentComponent:props => <ScrollView>   <DrawerItems {... props}></DrawerItems> </ ScrollView>  }
复制代码


可以覆盖导航使用的默认组件,使用DrawerItems自定义导航组件:


import {DrawerItems} from 'react-navigation';  
const CustomDrawerContentComponent = (props) => ( <View style = {style.container}> <DrawerItems {... props} /> </View> );
复制代码

6.2 嵌套抽屉导航

如果嵌套DrawerNavigation,抽屉将显示在父导航下方。


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


title: {     bottom: 0,     left: TITLE_OFFSET,     right: TITLE_OFFSET,     top: 0,     position: 'absolute',     alignItems: 'center',   } 
复制代码


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


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


 {   Platform.OS === 'ios' &&       title &&       <Text         onLayout={this._onTextLayout}         style={[styles.title, { color: tintColor }]}         numberOfLines={1}       >         {backButtonTitle}       </Text>  } 
复制代码


动态设置头部按钮事件当我们在头部设置左右按钮时,肯定避免不了要设置按钮的单击事件,但是此时会有一个问题,navigationOptions是被修饰为static类型的,所以在按钮的onPress方法中不能直接通过this来调用Component中的方法。如何解决呢?


在官方文档中,作者给出利用设置params的思想来动态设置头部标题。我们可以利用这种方式,将单击回调函数以参数的方式传递到params,然后在navigationOption中利用navigation来取出,并设置到onPress即可:


componentDidMount () {     /**      * 将单击回调函数作为参数传递      */     this.props.navigation.setParams({             switch: () => this.switchView()     });}  
/** * 切换视图 */ switchView() { alert('切换') }
static navigationOptions = ({navigation,screenProps}) => ({ headerTitle: '企业服务', headerTitleStyle: CommonStyles.headerTitleStyle, headerRight: ( <NavigatorItem icon={ Images.ic_navigator } onPress={ ()=> navigation.state.params.switch() }/> ), headerStyle: CommonStyles.headerStyle });
复制代码


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


(1)在注册StackNavigator的界面中,注册BackHandler


componentWillMount(){      BackHandler.addEventListener('hardwareBackPress', this._onBackAndroid );  }  
componentUnWillMount(){ BackHandler.addEventListener('hardwareBackPress', this._onBackAndroid); }
_onBackAndroid=()=>{ let now = new Date().getTime(); if(now - lastBackPressed < 2500) { return false; } lastBackPressed = now; ToastAndroid.show('再点击一次退出应用',ToastAndroid.SHORT); return true; }
复制代码


(2)监听react-navigation的Router


/**  * 处理安卓返回键  */  const defaultStateAction = AppNavigator.router.getStateForAction;  AppNavigator.router.getStateForAction = (action,state) => {      if(state && action.type === NavigationActions.BACK && state.routes.length === 1) {          if (lastBackPressed + 2000 < Date.now()) {              ToastAndroid.show(Constant.hint_exit,ToastAndroid.SHORT);              lastBackPressed = Date.now();              const routes = [...state.routes];              return {                  ...state,                  ...state.routes,                  index: routes.length - 1,              };          }      }      return defaultStateAction(action,state);  }; 
复制代码


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


import CardStackStyleInterpolator from 'react-navigation/src/views/CardStackStyleInterpolator';  
复制代码


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


transitionConfig:()=>({      screenInterpolator: CardStackStyleInterpolator.forHorizontal,  }) 
复制代码


解决快速点击多次跳转当我们快速点击跳转时,会开启多个重复的界面,如何解决呢?其实在官方Git中也有提示,解决这个问题需要修改react-navigation源码。


找到scr文件夹中的addNavigationHelpers.js文件,替换为如下文本即可:


export default function<S: *>(navigation: NavigationProp<S, NavigationAction>) {    // 添加点击判断    let debounce = true;    return {        ...navigation,        goBack: (key?: ?string): boolean =>            navigation.dispatch(                NavigationActions.back({                    key: key === undefined ? navigation.state.key : key,                }),            ),        navigate: (routeName: string,                   params?: NavigationParams,                   action?: NavigationAction,): boolean => {            if (debounce) {                debounce = false;                navigation.dispatch(                    NavigationActions.navigate({                        routeName,                        params,                        action,                    }),                );                setTimeout(                    () => {                        debounce = true;                    },                500,                );                return true;            }            return false;        },      /**      * For updating current route params. For example the nav bar title and      * buttons are based on the route params.      * This means `setParams` can be used to update nav bar for example.      */      setParams: (params: NavigationParams): boolean =>        navigation.dispatch(          NavigationActions.setParams({            params,            key: navigation.state.key,          }),        ),    };  }
复制代码

七、拓展阅读

发布于: 刚刚阅读数: 2
用户头像

No Silver Bullet 2021.07.09 加入

岂曰无衣 与子同袍

评论

发布
暂无评论
ReactNative进阶(七):导航组件 react-navigation