react-navigation (v2) 初级

本文是在react-navigation v2版本基础上编辑的。中文文档传送口

为了便于牢记react-navigation的功能,我将其分成了几个小块:

  • StackNavigator
    堆栈导航
  • Tab Navigator
    Tab菜单
  • Drawer Navigator
    侧边栏菜单
  • SwitchNavigator
  • navigation
    navigation属性
  • withNavigation
    传递navigation
  • android物理返回键重写
  • Stack navigator、Tab navigator等相互嵌套
  • SafeAreaView

1.StackNavigator

stack navigator 为你的应用提供了一种在屏幕之间切换并管理导航历史记录的方式。 如果您的应用程序只使用一个 stack navigator ,则它在概念上类似于Web浏览器处理导航状态的方式 - 当用户与它进行交互时,应用程序会从导航堆栈中新增和删除页面,这会导致用户看到不同的页面。 Web浏览器和 React Navigation 工作原理的一个主要区别是:React Navigation 的 stack navigator 提供了在 Android 和 iOS 设备上,在堆栈中的路由之间导航时你期望的手势和动画。

创建一个stack navigator
通过createStackNavigator(RouteConfigs, StackNavigatorConfig)函数,返回一个React组件。

RouteConfigs:

createStackNavigator({
  // 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,
});

screen添加到stack navigation中时,以key:object的形式添加,key必须唯一,object必须属性:screen,选配属性path、navigationOptions。此处的navigationOptions是针对本screen的配置。

StackNavigatorConfig:


StackNavigatorConfig
  • initialRouteName:设置堆栈的默认屏幕。 必须匹配路由配置中的某个 Key。
  • initialRouteParams: 初始路由的参数
  • navigationOptions: 用于屏幕的默认导航选项。是对stack navigator内所有screen的配置,可重写。
  • mode: 定义渲染和转换的样式;
    card - 使用标准的 iOS 和 Android 屏幕转换。 这是默认设置。
    modal -使屏幕从底部滑动, 这是一个常见的 iOS 模式。仅在 iOS 上工作, 对 Android 没有影响。
  • headerMode: 指定标题栏的呈现方式:
    float -呈现一个位于顶部的单个标题栏, 并在屏幕被更改时进行动画显示。这是 iOS 上常见的模式。
    screen -每个屏幕都有一个标头, 并且标题栏随屏幕一起淡入和淡出。这是 Android 的常见模式。
    none -不会呈现标题栏。

2. Tab Navigator

屏幕底部的一个简单的选项卡栏, 可以在不同的路由之间切换。路由被懒加载--他们的屏幕组件在第一次聚焦之前不会载入。(以底部选项卡进行说明)

tab navigator
  • 创建方式:有三种,分别对应了底部选项卡栏、屏幕底部的材料设计主题标签栏、屏幕顶部的材料设计主题标签栏。
  • initialRouteName:第一次加载时初始选项卡路由的 routeName。
  • order:定义选项卡顺序的 routeNames 数组。
  • backBehavior:控制 "返回" 按钮是否会导致 Tab 页切换到初始 Tab 页? 如果是, 设置为 initialRoute, 否则 none。 默认为 initialRoute的行为。
  • tabBarComponent: -可选,覆盖用作标签栏的组件。
  • BottomTabNavigatorConfig:tabbar配置选项。
  • navigationOptions:导航器内部的navigationOptions。

示例代码:

const MineStack = createStackNavigator(
    {
        Mine:MineScreen,
        Setting:SettingScreen
    }
)

const HomeStack = createStackNavigator(
    {
        Home:HomeScreen
    }
)

const TabNavigator = createBottomTabNavigator(
    {
        Home2:HomeStack,
        Detail: DetailScreen,
        Mine: MineStack,
    }
)

需要注意的是,TabNavigator中的tab页面是没有标题栏的。

3. Drawer Navigator

侧边抽屉式菜单导航。

drawer navigator
  • drawerWidth:抽屉的宽度或返回它的函数。

  • drawerPosition:选项为 左 或 右. 默认值为 左 位置。

  • contentComponent:用于呈现抽屉内容 (例如, 导航项) 的组件。 接收用于抽屉的 navigation 支柱。 默认为 DrawerItems。

  • contentOptions:配置抽屉内容

  • useNativeAnimations:启用本机动画。默认值为 true。

  • drawerBackgroundColor:使用某种颜色的抽屉背景。默认值为 白色。

  • initialRouteName:第一次加载时初始选项卡路由的 routeName。

  • order:定义选项卡顺序的 routeNames 数组。

  • backBehavior:控制 "返回" 按钮是否会导致 Tab 页切换到初始 Tab 页? 如果是, 设置为 initialRoute, 否则 none。 默认为 initialRoute的行为。

  • navigationOptions:页面导航选项.

  • title:可用作headerTitle和tabBarLabel的后备的通用标题。

  • drawerLabel:字符串,React 元素或给定{focused:boolean,tintColor:string}的函数返回一个React.Node,以显示在抽屉边栏中。 未定义时,使用场景title

  • drawerIcon:React 元素或给定{ focused: boolean, tintColor: string }的函数返回一个 React.Node,用于在标签栏中显示。

  • drawerLockMode:指定抽屉的锁定模式。 这也可以通过在顶级路由器上使用 screenProps.drawerLockMode 动态更新。

  • open/close:通过navigation的api开启/关闭drawer navigator。

4. SwitchNavigator

SwitchNavigator的目的是一次只显示一个屏幕。默认情况下,它不处理返回操作,并在你切换时将路由重置为默认状态。

SwitchNavigator
  • initialRouteName:第一次加载时初始选项卡路由的 routeName。
  • resetOnBlur:切换离开屏幕时,重置所有嵌套导航器的状态。 默认为true。
  • backBehavior:控制 "返回" 按钮是否会导致 Tab 页切换到初始 Tab 页? 如果是, 设置为 initialRoute, 否则 none。 默认为none行为。

5. Navigation Prop

重要的是要强调导航道具没有传递到所有组件;只有屏幕组件自动接收navigation!

Navigation Prop
  • navigate:转到另一个屏幕,如果已存在,转到已存在的屏幕,如果不存在,则增加。
navigation.navigate({routeName, params, action, key})
navigation.navigate(routeName, params, action)
  • addListener:订阅导航生命周期的更新。
    React Navigation发射事件以屏幕订阅它们的组件:willBlur、willFocus、didFocus、didBlur
const didBlurSubscription = this.props.navigation.addListener(
  'didBlur',
  payload => {
    console.debug('didBlur', payload);
  }
);

// Remove the listener when you are done
didBlurSubscription.remove();

The JSON payload:

{
  action: { type: 'Navigation/COMPLETE_TRANSITION', key: 'StackRouterRoot' },
  context: 'id-1518521010538-2:Navigation/COMPLETE_TRANSITION_Root',
  lastState: undefined,
  state: undefined,
  type: 'didBlur',
};
  • isFocused:如果屏幕聚焦则返回true,否则返回false。
  • state:屏幕的当前状态/路由
{
  // the name of the route config in the router
  routeName: 'profile',
  //a unique identifier used to sort routes
  key: 'main0',
  //an optional object of string options for this screen
  params: { hello: 'world' }
}
  • dispatch:向路由器发送一个动作
import { NavigationActions } from 'react-navigation';

const navigateAction = 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(navigateAction);
  • push:添加新路由到堆栈,不管堆栈中是否已有相同key的路由。
navigation.push(routeName, params, action)
  • pop:带你到堆栈中的前一个屏幕。如果您提供一个数字“n”,它将指定多少屏幕将您带回堆栈。
navigation.pop(n)
  • popToTop:将其调用以跳回堆栈中的顶部路径,关闭所有其他屏幕。。
  • replace:用新的路由替换当前的路由。
navigation.replace(routeName, params, action)

如果当前导航器是堆栈导航器,navigation prop才具有额外的 push、pop、popToTop、replace。

6. withNavigation

withNavigation是一个高阶组件,它可以将 navigation 这个 prop 传递到一个包装的组件。 当你无法直接将navigation 这个 prop 传递给组件,或者不想在深度嵌套的子组件中传递它时,它将非常有用。
withNavigation (Component)返回一个Component

import React from 'react';
import { Button } from 'react-native';
import { withNavigation } from 'react-navigation';

class MyBackButton extends React.Component {
  render() {
    return <Button title="Back" onPress={() => { this.props.navigation.goBack() }} />;
  }
}

// withNavigation returns a component that wraps MyBackButton and passes in the
// navigation prop
export default withNavigation(MyBackButton);

7. android物理返回键重写

默认情况下,当用户按下 Android 物理返回键时,react-navigation 会返回到新页面,如果没有可返回的页面,则退出应用。 这是一个合理的默认行为,但有些情况下你可能想要实现自定义处理。

推荐的第三方依赖:react-navigation-backhandler

我的方式是:定义BaseComponent,需要重写android返回键的,集成该组件。

let lastBackPressed = 0;

export default class BaseComponent extends Component {

    _didFocusSubscription;
    _willBlurSubscription;

    constructor(props) {
        super(props);
        this._didFocusSubscription = props.navigation.addListener('didFocus', payload =>
            BackHandler.addEventListener('hardwareBackPress', this.onBackButtonPressAndroid)
        );
    }

    componentDidMount() {
        this._willBlurSubscription = this.props.navigation.addListener('willBlur', payload =>
            BackHandler.removeEventListener('hardwareBackPress', this.onBackButtonPressAndroid)
        );
    }

    componentWillUnmount() {
        this._didFocusSubscription && this._didFocusSubscription.remove();
        this._willBlurSubscription && this._willBlurSubscription.remove();
    }

    onBackButtonPressAndroid = () => {
        let now = new Date().getTime();
        if(now - lastBackPressed < 2500) {
            return false;
        }
        lastBackPressed = now;
        ToastAndroid.show('再点击一次退出应用',ToastAndroid.SHORT);
        return true;
    };

}

其中使用到了navigation的addListener属性。
onBackButtonPressAndroid返回true,则react-navigation将不会接收到返回事件,当返回false时,react-navigation将处理返回事件,返回上个路由或者退出应用。

8. Stack navigator、Tab navigator等相互嵌套

navigator之间的相互嵌套示例:

const DrawNavigator = createDrawerNavigator(
    {
        Draw1:DrawScreen1,
        Draw2: DrawScreen2,
    }
)

const MineStack = createStackNavigator(
    {
        Mine:MineScreen,
        Setting:SettingScreen
    }
)

const HomeStack = createStackNavigator(
    {
        Home:HomeScreen
    }
)

HomeStack.navigationOptions={tabBarLabel: 'Home!',}

//****stack navigator嵌套在Tab navigator中
const TabNavigator = createBottomTabNavigator(
    {
        Home2:HomeStack,
        Detail: DetailScreen,
        Mine: MineStack,
    },

)

TabNavigator.navigationOptions = {header:null}

//****Draw navigator 和Tab navigator 嵌套在stack navigator中
const RootStack = createStackNavigator(
    {
        Draw:DrawNavigator,
        Tab: TabNavigator
    },{
        initialRouteName: 'Tab',
    }
);


export default class App extends React.Component {
    
    render() {
        return <RootStack />;
    }

}

关注下TabNavigator.navigationOptions = {header:null}代码设置,其效果是将父navigator(stack navigator)的标题栏隐藏。
Why?
TabNavigator是一个React组件,TabNavigator.navigationOptions配置的是TabNavigator组件的的导航配置,是相对于父navigator(组件)的导航配置。我们其称之为TabNavigator外部navigationOptions
那么内部的navigationOptions如何理解呢?

TabNavigator的内部navigationOptions指的是tab navigator包含的组件(screen)中的tabBar相关的configs(tab navigator包含的screen是没有标题栏的)。那么tabNavigator的内部navigationOptions该如何设置呢?示例代码如下:

const TabNavigator = createBottomTabNavigator(
    {
        Home2:HomeStack,
        Detail: DetailScreen,
        Mine: MineStack,
    },{
        navigationOptions:{
            title:'Test'
        }
    }

)

上述代码中navigationOptions是针对TabNavigator内部所有的screen而言,可在screen中重写效果。

关于外部navigationOptions内部navigationOptions的概念,同样适用于其他navigator,如HomeStack.navigationOptions={tabBarLabel: 'Home!',},HomeStack是Tab navigator的包含的一个组件,外部的navigationOptions就是设置这个组件的tabbar配置。

9. SafeAreaView

默认情况下,React Navigation 会帮助确保您的应用程序在iPhoneX上正确显示。 它通过在可能与传感器集群(“齐刘海”)或 Home 主页指示器交互的UI元素内部使用SafeAreaView来实现。

当我们隐藏/自定义导航栏或选项卡栏时,可能存在以下问题:


错误示例

Landscape Mode(横屏模式)时,可能的错误:


错误示例

此时,我们可以 通过使用SafeAreaView包装内容,避免这些问题。示例代码如下:

import { SafeAreaView } from 'react-navigation';

class App extends Component {
  render() {
    return (
      <SafeAreaView style={styles.container}>
        <Text style={styles.paragraph}>
          This is top text.
        </Text>
        <Text style={styles.paragraph}>
          This is bottom text.
        </Text>
      </SafeAreaView>
    );
  }
}

使用 forceInset 获得更多控制
在某些情况下,你可能需要更多控制应用哪些填充。 例如,你可以通过将forceInset 这个 prop 传递给SafeAreaView来移除底部填充。

<SafeAreaView style={styles.container} forceInset={{ bottom: 'never' }}>
  <Text style={styles.paragraph}>
    This is top text.
  </Text>
  <Text style={styles.paragraph}>
    This is bottom text.
  </Text>
</SafeAreaView>

forceInset使用 key(取值:top | bottom | left | right | vertical | horizontal)和value(取值:'always' | 'never')组成一个对象 或者,您可以通过传递一个整数来完全重写填充。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,425评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,058评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,186评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,848评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,249评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,554评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,830评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,536评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,239评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,505评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,004评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,346评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,999评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,060评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,821评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,574评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,480评论 2 267

推荐阅读更多精彩内容