『React Navigation 4x系列』createStackNavigator开发

createStackNavigator

提供APP屏幕之间切换的能力,它是以栈的形式还管理屏幕之间的切换,新切换到的屏幕会放在栈的顶部。

屏幕转场风格

默认情况下,createStackNavigator提供了转场过渡效果,在Android和iOS上过渡效果是不同的,这也是React Native重平台性的一个体现,在Android上从屏幕底部淡入,在iOS上是从屏幕的右侧划入,当然你也可以通过配置让StackNavigator支持屏幕从底部滑入的效果。

createStackNavigator API

createStackNavigator(RouteConfigs,StackNavigatorConfig):
RouteConfigs(必选):路由配置对象是从路由名称到路由配置的映射,告诉导航器该路由呈现什么。
StackNavigatorConfig(可选):配置导航器的路由(如:默认首屏,navigationOptions,paths等)样式(如,转场模式mode、头部模式等)。

从createStackNavigator API上可以看出createStackNavigator 支持通过RouteConfigs和 StackNavigatorConfig两个参数来创建createStackNavigator导航器。

StackNavigatorConfig

从react-navigation源码中可以看出StackNavigatorConfig支持配置的参数有10个

function createStackNavigator(routeConfigMap, stackConfig = {}) {
  const {
    initialRouteKey,
    initialRouteName,
    initialRouteParams,
    paths,
    navigationOptions,
    disableKeyboardHandling,
    getCustomActionCreators
  } = stackConfig;

这7个参数可以根据作用不同分为路由配置、视图样式配置两类,首先看用于路由配置的参数:

  • initialRouteName: 设置默认的页面组件,必须是上面已注册的页面组件。
  • initialRouteParams: 初始路由的参数。
  • navigationOptions: 屏幕导航的默认选项,下文会详细讲解。
  • initialRouteKey - 初始路由的可选标识符。
  • paths: 用来设置支持schema跳转时使用,具体使用会在下文的有关Schema章节中讲到。

用于导航样式配置的参数:

  1. mode: 页面切换模式: 左右是card(相当于iOS中的push效果), 上下是modal(相当于iOS中的modal效果)
    card: 普通app常用的左右切换。
    modal: 上下切换。
  2. headerMode: 导航栏的显示模式: screen: 有渐变透明效果, float: 无透明效果, none: 隐藏导航栏。
    float: 无透明效果, 默认。
    screen: 有渐变透明效果, 如微信QQ的一样。
    none: 隐藏导航栏。
  3. headerBackTitleVisible : 提供合理的默认值以确定后退按钮标题是否可见,但如果要覆盖它,则可以使用true或` false 在此选项中。
  4. fade-in-place: 标题组件交叉淡入淡出而不移动,类似于iOS的Twitter,Instagram和Facebook应用程序。 这是默认值。
    uikit: iOS的默认行为的近似值。
  5. headerTransitionPreset: 指定在启用headerMode:float时header应如何从一个屏幕转换到另一个屏幕。
  6. cardStyle: 样式(iOS上页面切换会有白色渐变蒙层,想去掉则可以这样设置,cardStyle: { opacity: null },切换页面时的页面边框也在这里可以设置)。
  7. onTransitionStart: 页面切换开始时的回调函数 (我们可以在这里注册一些通知,告知我们切面切换的状态,方便后面处理页面切换事件)。
  8. onTransitionEnd: 页面切换结束时的回调函数。

navigationOptions(屏幕导航选项)

支持一下参数:

  • title: 可以作为headerTitle的备选字段(当没设置headerTitle时会用该字段作为标题),也可以作为TabNavigator的tabBarLabel以及DrawerNavigator的drawerLabel。
  • header: 自定义导航条,可以通过设置null来隐藏导航条;
  • headerTitle: 标题;
  • headerTitleAllowFontScaling: 标题是否允许缩放,默认true;
  • headerBackTitle: 定义在iOS上当前页面进入到下一页面的回退标题,可以通过设置null来禁用它;
  • headerTruncatedBackTitle: 当回退标题不能显示的时候显示此属性的标题,比如回退标题太长了;
  • headerBackImage:React 元素或组件在标题的后退按钮中显示自定义图片。 当组件被调用时,它会在渲染时收到许多 props 如:(tintColor,title)。 默认为带有 react-navigation/views/assets/back-icon.png 这张图片的组件,后者是平台的默认后图标图像(iOS上为向左的符号,Android上为箭头)。
  • headerRight: 定义导航栏右边视图;
  • headerLeft: 定义导航栏左边视图;
  • headerStyle: 定义导航栏的样式,比如背景色等;
  • headerTitleStyle: 定义标题的样式;
  • headerLeftContainerStyle:自定义 headerLeft 组件容器的样式,例如,增加 padding。
  • headerRightContainerStyle:自定义 headerRight 组件容器的样式,,例如,增加 padding。
  • headerTitleContainerStyle:自定义 headerTitle 组件容器的样式, 例如,增加 padding。
  • headerBackTitleStyle: 定义返回标题的样式;
  • headerPressColorAndroid:颜色为材料波纹 (Android >= 5.0);
  • headerTintColor: 定义导航条的tintColor,会覆盖headerTitleStyle中的颜色;
  • headerTransparent:默认为 false。如果 true, 则标头将不会有背景, 除非您显式提供 headerStyle 或 headerBackground。
  • headerBackground:与headerTransparent一起使用,以提供在标题后台呈现的组件。 例如,您可以使用模糊视图来创建半透明标题。
  • gesturesEnabled: 定义是否能侧滑返回,iOS默认true,Android默认false;
  • gestureResponseDistance: 定义滑动返回的有效距离,水平状态下默认:25,垂直状态默认135;
  • gestureDirection: 设置关闭手势的方向。默认从左向右,可以设置从右到左的滑动操作。

【案例】使用StackNavigator做界面导航、配置navigationOptions]

  1. 第一步:创建一个StackNavigator类型的导航器]
import React from 'react';
import { Button} from 'react-native';
import { createAppContainer } from 'react-navigation';
import { createStackNavigator } from 'react-navigation-stack';

import {AppBottomTabNavigator} from './BottomTabNavigator';
import {AppTopTabNavigator} from './TopTabNavigator';
import {SwitchNavigator} from './SwitchNavigator';
import {DrawerNav} from './DrawerNavigator';

import Page1 from  '../page/Page1';
import Page2 from  '../page/Page2';
import Page3 from  '../page/Page3';
import Page4 from  '../page/Page4';
import FlatListPage from  '../page/PageFlatList';
import SwipeableFlatListPage from  '../page/PageSwipeableFlatList';
import SectionListPage from  '../page/PageSectionList';
import HomePage from  '../page/HomePage';



export const AppStackNavigator = createStackNavigator({
    HomePage: {
        screen: HomePage,
    },
    Page1: {
        screen: Page1,
        navigationOptions: ({navigation}) => ({
            title: `${navigation.state.params.name}页面名A`//动态设置navigationOptions
        })
    },
    Page2: {
        screen: Page2,
        navigationOptions: {//在这里定义每个页面的导航属性,静态配置
            title: "This is Page2.",
        }
    },
    Page3: {
        screen: Page3,
        navigationOptions: (props) => {//在这里定义每个页面的导航属性,动态配置
            const {navigation} = props;
            const {state, setParams} = navigation;
            const {params} = state;
            return {
                title: params.title ? params.title : 'This is Page3',
                headerRight: (
                    <Button
                        title={params.mode === 'edit' ? '保存' : '编辑'}
                        onPress={() =>
                            setParams({mode: params.mode === 'edit' ? '' : 'edit'})}
                    />
                ),
            }
        }
    },
    Page4: {
        screen: Page4,
        navigationOptions: {//在这里定义每个页面的导航属性,静态配置
            title: "This is Page4",
        }
    }
}, {
    defaultNavigationOptions: {
        // header: null,// 可以通过将header设为null 来禁用StackNavigator的Navigation Bar
    }
});
  1. 第二步:配置navigationOptions:
    步骤一的代码中通过两种方式配值了navigationOptions:

静态配置:

对Page2的navigationOptions配置是通过静态配置完成的:

Page2: {
        screen: Page2,
        navigationOptions: {//在这里定义每个页面的导航属性,静态配置
            title: "This is Page2.",
        }
    },

这种方式被称为静态配置,因为navigationOptions中的参数是直接Hard Code的不依赖于变量。
动态配置:
对Page3的navigationOptions配置是通过动态配置完成的:

Page3: {
    screen: Page3,
    navigationOptions: (props) => {//在这里定义每个页面的导航属性,动态配置
        const {navigation} = props;
        const {state, setParams} = navigation;
        const {params} = state;
        return {
            title: params.title ? params.title : 'This is Page3',
            headerRight: (
                <Button
                    title={params.mode === 'edit' ? '保存' : '编辑'}
                    onPress={() =>
                        setParams({mode: params.mode === 'edit' ? '' : 'edit'})}
                />
            ),
        }
    }
},

第三步:界面跳转

/**
 * Created by mac on 2019/12/1.
 */
/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 *
 * @format
 * @flow
 */

import React,{Component} from 'react'
import {
    StyleSheet,
    Text,
    Button,
    View
} from 'react-native';

export default class HomePage extends React.Component {
    //在这里定义每个页面的导航属性
    static navigationOptions = {
        title: 'Home',
        headerBackTitle:'返回哈哈',//设置返回此页面的返回按钮文案,有长度限制
    }
    render() {
        const {navigation} = this.props;
        return <View style={styles.container}>
            <Text style={styles.text}>欢迎来到HomePage</Text>
            <Button
                title="Go To Page1"
                onPress={() => {
                    navigation.navigate('Page1', {name: '动态的'});
                }}
            />
            <Button
                title="Go To Page2"
                onPress={() => {
                    navigation.navigate('Page2');
                }}
            />
            <Button
                title="Go To Page3"
                onPress={() => {
                    navigation.navigate('Page3',{ name: 'Devio' });
                }}
            />
        </View>
    }
}

const styles = StyleSheet.create({
    container:{
        flex:1,
    },
    welcome:{
        fontSize:20,
        textAlign:'center',
        margin:10
    }
});

第四步:更新页面Params与返回

/**
 * Created by mac on 2019/12/1.
 */
/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 *
 * @format
 * @flow
 */

import React,{Component} from 'react'
import {
    StyleSheet,
    View,
    TextInput,
    Text,
} from 'react-native';



export default class Page3 extends Component{
  render(){
      const {navigation} = this.props;
      const {state,setParams} = navigation;
      const {params} = state;
      const shouwText = params && params.mode === 'edit' ? "正在编辑" : " 编辑完成";
      return(
          <View style={styles.container}>
             <Text style={styles.welcome}>Welcome to Page3</Text>
              <TextInput
                style = {styles.input}
                onChangeText = {
                    text => {
                        setParams({title:text});
                    }
                }
              />
          </View>
      );
  }
}

const styles = StyleSheet.create({
    container:{
        flex:1,
    },
    welcome:{
        fontSize:20,
        textAlign:'center',
        margin:10
    },
    input:{
        height:50,
        borderWidth:1,
        marginTop:10,
    }
});

其它用到的页面

  1. Page1
/**
 * Created by mac on 2019/12/1.
 */
/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 *
 * @format
 * @flow
 */

import React,{Component} from 'react'
import {
    StyleSheet,
    Button,
    View,
    Text,
} from 'react-native';

import Page4 from './Page4'



export default class Page1 extends Component{
  render(){
      const {navigation} = this.props;
      return(
          <View style={styles.container}>
             <Text style={styles.welcome}>Welcome to Page1</Text>
              <Button
               title={'Go Back'}
               onPress = { () =>{
                   navigation.goBack();
               }
               }
              />
              <Button
                  title={'跳转到页面4'}
                  onPress = { () =>{
                      navigation.navigate('Page4');
                  }
                  }
              />
          </View>
      );
  }
}

const styles = StyleSheet.create({
    container:{
        flex:1,
    },
    welcome:{
        fontSize:20,
        textAlign:'center',
        margin:10
    }
});
  1. Page2
 * Created by mac on 2019/12/1.
 */
/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 *
 * @format
 * @flow
 */

import React,{Component} from 'react'
import {
    StyleSheet,
    View,
    Text,
} from 'react-native';



export default class Page2 extends Component{
  render(){
      return(
          <View style={styles.container}>
             <Text style={styles.welcome}>Welcome to Page2</Text>
          </View>
      );
  }
}

const styles = StyleSheet.create({
    container:{
        flex:1,
    },
    welcome:{
        fontSize:20,
        textAlign:'center',
        margin:10
    }
});



  1. Page3
/**
 * Created by mac on 2019/12/1.
 */
/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 *
 * @format
 * @flow
 */

import React,{Component} from 'react'
import {
    StyleSheet,
    View,
    TextInput,
    Text,
} from 'react-native';



export default class Page3 extends Component{
  render(){
      const {navigation} = this.props;
      const {state,setParams} = navigation;
      const {params} = state;
      const shouwText = params && params.mode === 'edit' ? "正在编辑" : " 编辑完成";
      return(
          <View style={styles.container}>
             <Text style={styles.welcome}>Welcome to Page3</Text>
              <TextInput
                style = {styles.input}
                onChangeText = {
                    text => {
                        setParams({title:text});
                    }
                }
              />
          </View>
      );
  }
}

const styles = StyleSheet.create({
    container:{
        flex:1,
    },
    welcome:{
        fontSize:20,
        textAlign:'center',
        margin:10
    },
    input:{
        height:50,
        borderWidth:1,
        marginTop:10,
    }
});


  1. Page4
/**
 * Created by mac on 2019/12/1.
 */
/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 *
 * @format
 * @flow
 */

import React,{Component} from 'react'
import {
    StyleSheet,
    View,
    Button,
    Text,
} from 'react-native';


export default class Page4 extends Component{
    render() {
        const {navigation} = this.props;
        return <View style={styles.container}>
            <Text style={styles.text}>欢迎来到Page4</Text>
            <Button
                onPress={() => navigation.openDrawer()}
                title="Open drawer"
            />
            <Button
                onPress={() => navigation.toggleDrawer()}
                title="Toggle drawer"
            />
            <Button
                onPress={() => navigation.navigate('Page5')}
                title="Go to Page5"
            />
        </View>
    }
}

const styles = StyleSheet.create({
    container:{
        flex:1,
    },
    welcome:{
        fontSize:20,
        textAlign:'center',
        margin:10
    }
});


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

推荐阅读更多精彩内容