react-native系列(14)导航篇:页面导航StackNavigator参数及使用详解

react-navigation导航是RN官网推荐使用的导航插件,目前最新版本为3.x,它包括3种类型的导航:

  • StackNavigator
  • TabNavigator
  • DrawerNavigator

本篇主要介绍的是StackNavigator,假设你已经对它的用法已经有了基本的认识,或重新去官网看一下

官网地址:https://reactnavigation.org/zh-Hans/

StackNavigator嵌套

一般app的导航都只有一个导航堆栈,结构如下:

有些逻辑比较复杂的app,存在导航嵌套的情况,结构如下:

这种情况下,可以使用StackNavigator嵌套,把子导航堆栈做为根导航堆栈的其中一个页面:

// 子导航堆栈
const itemStack= createStackNavigator(
    {
      Screen1: { screen: Screen1}
    },
    ...
);

// 根导航堆栈
const rootStack = createStackNavigator(
    {
      Screen1: { screen: itemStack}
    },
    ...
);

createStackNavigator函数详解

createStackNavigator函数用于创建Stack导航,它包含了两个参数对象:第一个参数用来配置路由页面,第二个参数用来配置路由的其它信息。

创建时,相关参数详解如下:

const rootStack = createStackNavigator(
    // 路由页面
    {
      Page1: { screen: PageView1 },
      Page2: { screen: PageView2, params: {}} // 第二个参数params为页面入参 
    },
    // 路由其它信息
    {
      initialRouteName: 'Page1', // 初始页,必须是路由配置页面中的某一个
      initialRouteParams: {}, // 初始页入参
      // mode: '', // 系统的页面切换动画风格,目前只有默认card和模态风格modal两种。但本人3.X版本测试结果是仅剩modal一种,一般这个配置不做设置
      // headerMode: 'none' // none时表示隐藏头部的导航栏,默认情况下不做设置
      // 导航栏配置
      defaultNavigationOptions: {
        headerStyle:{}, // 导航栏样式
        headerTitle:'', // 或使用title,导航栏标题
        headerTitleStyle:{}, // 导航栏标题样式
        headerLeft:(<View />), // 导航栏左边如返回等的显示容器
        headerRight:(<View />), // 导航栏右边如工具等的显示容器
        gesturesEnabled: false, // 是否支持滑动返回手势,iOS默认支持,安卓默认关闭
      },
      // 自定义页面切换动画配置
      transitionConfig: () => ({
        // some code
      })
    }
);

测试代码:

import React, { Component } from 'react';
import { View, Easing, Animated } from 'react-native';
import { createStackNavigator, createAppContainer } from 'react-navigation';
import { AuthScreen, ListScreen, ItemScreen, ModalScreen } from './StackScreen';

const mainNavigator = createStackNavigator(
    {
      Auth: { screen: AuthScreen },
      List: { screen: ListScreen, params: {defaultParam: 'this is defaultParam'}},
      Item: { screen: ItemScreen }
    },
    {
      initialRouteName: 'Auth',
      initialRouteParams: { param: 'this is initParam' },
      defaultNavigationOptions: {
        gesturesEnabled: false
      },
      // 类似于微信的左右切换路由页
      transitionConfig: () => ({
        transitionSpec: {
            duration: 300,
            easing: Easing.out(Easing.poly(4)),
            timing: Animated.timing,
        },
        screenInterpolator: sceneProps => {
            const {layout, position, scene} = sceneProps;
            const {index} = scene;
            const Width = layout.initWidth;
            //沿X轴平移
            const translateX = position.interpolate({
                inputRange: [index - 1, index, index + 1],
                outputRange: [Width, 0, -(Width)], // Width,0指新页面进入坐标变化; 0,-(Width)是指旧页面推出坐标变化
            });
            //透明度
            const opacity = position.interpolate({
                inputRange: [index - 1, index - 0.99, index],
                outputRange: [0, 1, 1],
            });
            return {opacity, transform: [{translateX}]};
        }
      })
    }
);

// 根导航堆栈
const rootStack = createStackNavigator(
    {
      Main: { screen: mainNavigator }, 
      Modal: { screen: ModalScreen } // modal屏操作,常会使用不同的屏间切换样式,且隐藏头部导航栏
    },
    {
      initialRouteName: 'Main',
      initialRouteParams: { defaultParam: 'defaultParam' },
      mode: 'modal', // 定义跳转风格为modal
      headerMode: 'none' // 隐藏该导航堆栈中的标题栏
    }
);

const AppNavigatorContainer  = createAppContainer(rootStack);

class StackNavigator extends Component {
    render(){
        return(
            <View style={{flex:1}}>
                <AppNavigatorContainer />
            </View>
        );
    }
}

export default StackNavigator;

这段代码用到了导航嵌套,目的是把模态框弹窗Modal也作为导航的一部分。有些人会说也可以使用Modal组件,为什么写在这里做为导航的一个页面?因为如果不把Modal做为一个页面,那么模态框仅会弹出在导航栏下的页面容器中而不会遮挡导航栏。

导航过程效果:

NavigationOptions

navigationOptions为静态函数,写在路由页中,主要用于设置导航栏样式,配置参数:

static navigationOptions = ({ navigation }) => ({
    header: null, // 为null时表示隐藏导航栏
    headerStyle:{}, // 导航栏样式
    headerTitle:'', // 或使用title,导航栏标题
    headerTitleStyle:{}, // 导航栏标题样式
    headerLeft:(<View />), // 导航栏左边如'返回'的显示容器
    headerRight:(<View />), // 导航栏右边如'其它辅助功能'的显示容器
    gesturesEnabled: false, // 是否支持滑动返回手势,iOS默认支持,安卓默认关闭
})

测试代码:

import React, { Component } from 'react';
import { View, Text, TouchableOpacity, StyleSheet } from 'react-native';
import PropTypes from 'prop-types';

class ListScreen extends Component {
    static navigationOptions = ({ navigation }) => ({
        headerTitle: 'ListScreen',
        headerStyle: {
            backgroundColor: '#f4511e'
        },
        headerTitleStyle: {
            flex: 1,
            textAlign: 'center',
            color: '#FFFFFF',
            fontSize: 26
        },
        headerLeft:(
            <TouchableOpacity 
                style={styles.buttonContainer}
                onPress={()=>{navigation.goBack();}}
            >
                <Text style={styles.headerLeftTextStyle}>返回{navigation.getParam('prevScreenTitle','为空时显示这段文字')}</Text>
            </TouchableOpacity>
        ),
        headerRight: (
            <TouchableOpacity
                style={styles.buttonContainer} 
                onPress={()=>{navigation.navigate('Item', { prevScreenTitle: 'ListScreen' });}}
            >
                <Text style={styles.headerRightTextStyle}>进入ItemScreen</Text>
            </TouchableOpacity>
        )
    })

    render(){
        const { navigation } = this.props;
        console.log(navigation.getParam('defaultParam'));
        return(
            <View style={styles.viewStyle}>
                <Text>This is ListScreen!</Text>
            </View>
        );
    }
}

ListScreen.propTypes = {
    navigation: PropTypes.object
};

const styles = StyleSheet.create({
    viewStyle: {
        flex: 1,
        justifyContent: 'center',
        alignItems: 'center'
    },
    buttonContainer: {
        backgroundColor: '#FFFFFF',
        justifyContent: 'center',
        alignItems: 'center',
        padding: 5
    },
    headerLeftTextStyle: {
        color: 'blue'
    },
    headerRightTextStyle: {
        color: 'green'
    }
});

export default ListScreen;

导航栏样式效果:

页面跳转及参数传递

路由页面跳转使用navigation.navigate函数,第一个参数为跳转的页面名称,第二个参数为传递的参数:

navigation.navigate('List', {param:'some data'}); 

关于接收参数,无论是在创建时设置的默认入参还是跳转页面时传递的参数,都通过getParam函数来获取,第一个参数为参数名称,第二个参数为当没有参数时,以该参数内容作为入参:

const initData = navigation.getParam('param','当没有获取到参数时显示这段文字')

返回上一个页面使用goBack函数:

navigation.goBack()

导航栏与页面内容的数据交互

页面内容通过navigation.setParams来设置导航栏所需数据:

state = {
    day: '星期一'
}

componentDidMount() {
    this.props.navigation.setParams({ day: this.state.day});
}

导航栏通过navigation.getParam来获取数据:

const day = navigation.getParam('day'); // 星期一

测试代码:

import React, { Component } from 'react';
import { View, Text, TouchableOpacity, StyleSheet } from 'react-native';
import PropTypes from 'prop-types';

class ItemScreen extends Component {

    static navigationOptions = ({ navigation }) => ({
        headerTitle: 'ItemScreen',
        headerStyle: {
            backgroundColor: '#f4511e'
        },
        headerTitleStyle: {
            flex: 1,
            textAlign: 'center',
            color: '#FFFFFF',
            fontSize: 26
        }, 
        headerLeft:(
            <TouchableOpacity 
                style={styles.buttonContainer}
                onPress={()=>{navigation.goBack();}}
            >
                <Text style={styles.headerLeftTextStyle}>返回{navigation.getParam('prevScreenTitle','为空时显示这段文字')}</Text>
            </TouchableOpacity>
        ),
        headerRight: (
            <TouchableOpacity 
                style={styles.buttonContainer}
                onPress={navigation.getParam('increaseCount')}
            >
            <Text style={styles.headerRightTextStyle}>计数器 +1</Text> 
        </TouchableOpacity>
        )
    })

    constructor(props){
        super(props);
        this.state = {count:0};
    }

    componentDidMount() {
        this.props.navigation.setParams({ increaseCount: this._increaseCount });
    }

    _increaseCount = () => {
        this.setState({ count: this.state.count + 1 });
    };

    render(){
        const { navigation } = this.props;

        return(
            <View style={styles.viewContainer}>
                <Text>计数器结果:{this.state.count}</Text>
                <TouchableOpacity 
                    style={[styles.buttonContainer, styles.modalStyle]}
                    onPress={()=> navigation.navigate('Modal')}
                >
                    <Text style={styles.modalTextStyle}>进入ModalScreen</Text>
                </TouchableOpacity>
            </View>
        );
    }
}

ItemScreen.propTypes = {
    navigation: PropTypes.object
};

const styles = StyleSheet.create({
    viewContainer:{
        flex: 1,
        justifyContent: 'center',
        alignItems: 'center'
    },
    buttonContainer: {
        backgroundColor: '#FFFFFF',
        justifyContent: 'center',
        alignItems: 'center',
        padding: 5
    },
    headerLeftTextStyle: {
        color: 'blue'
    },
    headerRightTextStyle: {
        color: 'green'
    },
    modalStyle: {
        marginTop: 10,
        borderWidth: 1,
        padding: 5
    },
    modalTextStyle: {
        color: 'red'
    },
});

export default ItemScreen;

数据交互效果:

页面切换动画

可在transitionConfig属性配置,不了解结构可以看开篇处的createStackNavigator函数详解,详解测试代码中已经定义了一种动画效果。这里再贴出一份上下切换的动画作为比较:

// 上下切换路由页
transitionConfig: () => ({
  transitionSpec: {
      duration: 300,
      easing: Easing.out(Easing.poly(4)),
      timing: Animated.timing,
  },
  screenInterpolator: sceneProps => {
      const { layout, position, scene } = sceneProps;
      const { index } = scene;

      const height = layout.initHeight;
      //沿Y轴平移
      const translateY = position.interpolate({
          inputRange: [index - 1, index, index + 1],
          outputRange: [height, 0, 0],
      });
      //透明度
      const opacity = position.interpolate({
          inputRange: [index - 1, index - 0.99, index],
          outputRange: [0, 1, 1],
      });
      return { opacity, transform: [{ translateY }] };
  },
})

效果:

若要自定义切换动画,可以使用先了解一下Animated库。也可以参考 react-native系列(13)动画篇:Animated动画库和LayoutAnimation布局动画详解

(小贴士)react-navigation默认情况下负责为您处理 Android 返回按钮,即设备屏幕外的返回键,如果没有处理或不符合您的期望,可以使用RN官网中的BackHandler函数监听实现。

以上就是StackNavigator的一些实用经验,希望能对你有一定参考价值。

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值