RN 版本:0.50
操作环境:Windows 10
React Navigation 版本:1.0.0-beta.20
本节主要讲 StackNavigator 与定义相关的详细属性。
在新的页面被放入栈顶之后,StackNavigator 给你的应用程序提供了切换页面的方法。在 iOS 与 Android 平台上,StackNavigator 默认对应着它们各自的风格,比如在 iOS 上新的页面从右侧滑入,而在 Android 上则是从底部淡入。
class HomeScreen extends Component {
static navigationOptions = {
title: 'Welcome'
};
render() {
const {navigate} = this.props.navigation;
return <View>
<Text>Hello,Chat App!</Text>
<Button
onPress={() => navigate('Chat',{user: 'Lucy'})}
title="Chat with Lucy"
/>
</View>
}
}
const RootNavigator = StackNavigator({
Home: {
screen: HomeScreen
},Chat: {
path: 'people/:name',screen: ChatScreen
}
});
API 定义
StackNavigator(RouteConfigs,StackNavigatorConfig)
这里有两个参数 RouteConfigs
与 StackNavigatorConfig
,接下来分别对它们进行介绍。
RouteConfigs
RouteConfigs(路由配置) 是一组由路由名称与路由设置组成的键值对,它的作用是告诉导航器 Navigator 要显示的页面,格式与结构参考以下代码,注释写得很详细了。
StackNavigator({
// For each screen that you can navigate to,create a new entry like this: // 如果你想导航到一个页面,就按照下面的方式创建它 Chat: { // `ChatScreen` is a React component that will be the main content of the screen. // ChatScreen 就是一个将要显示在屏幕上的 React 组件 screen: ChatScreen,// When `ChatScreen` is loaded by the StackNavigator,it will be given a `navigation` prop. // 当 ChatScreen 被 StackNavigator 加载的时候,它会被赋予一个 navigation 属性 // Optional: When deep linking or using react-navigation in a web app,this path is used: // 可选的:当在 Web 应用程序中进行深度链接或使用 react-navigation 时,才会用到 path path: 'people/:name',// The action and route params are extracted from the path. // action 和 route 的参数是从 path 中提取的 // Optional: Override the `navigationOptions` for the screen // 可选的:可以在这里重写 navigationOptions,你也可以直接写在 ChatScreen 中(上一篇文章讲的) navigationOptions: ({navigation}) => ({ title: `Chat with ${navigation.state.params.user}`,}) },// 以下省略其他的路由(页面) ...MyOtherRoutes });
接下来介绍第二个参数 StackNavigatorConfig
。
StackNavigatorConfig
路由相关选项:
initialRouteName
- 设置栈中的默认显示路由(页面),必须匹配路由配置中的一个键值initialRouteParams
- 默认路由的参数navigationOptions
- 用于屏幕的默认导航选项paths
- 覆盖路由配置中设置的 path
视觉相关选项:
mode
- 定义渲染和页面切换的风格,以下两个选项是其可以使用的两个枚举值,下面同理:
card
- 使用标准的 iOS 和 Android 屏幕切换风格。这是默认的。modal
- 使屏幕从底部滑入,这是一种常见的 iOS 模式。只适用于 iOS,对 Android 没有任何影响。
headerMode
- 指定标题栏的渲染方式:
cardStyle
- 使用这个属性重写或继承栈中单个卡片(页面)的默认样式。transitionConfig
- 返回值是与默认页面切换相关的对象的函数。提供的函数将传递以下参数:
onTransitionStart
- 当切换动画开始时调用的函数。onTransitionEnd
- 当切换动画结束时调用的函数。
拿上一篇中的例子来说,有两个页面 Home 与 Chat,我们可以对其进行设置将 Chat 设为首页,并传递参数、设置标题栏等等。
var routeConfigs = {
Home: {
screen: HomeScreen
},Chat: {
screen: ChatScreen,},};
var stackNavigatorConfig = {
initialRouteName: 'Chat',initialRouteParams: {user: 'Join'},// 这里可以对标题栏进行通用设置
// 但是当某个页面也设置了 navigationOptions,则会失效,优先使用页面设置
navigationOptions: {
title: 'Hello Navigation!'
},// 不带标题栏
// headerMode: 'none'
}
const RootNavigator = StackNavigator(routeConfigs,stackNavigatorConfig);
export default RootNavigator;
export default class ChatScreen extends Component {
render() {
const {params} = this.props.navigation.state;
return (
<View>
<Text>Chat with {params.user}</Text>
</View>
);
}
}
效果就像下面这样,具体的各个属性都有什么实际效果,可以自己去尝试一下,有的属性很少会用到。
Screen Navigation Options
还记得 navigationOptions
这个属性吗?通过上面我们知道可以对其进行全局的设置,或者对每个页面单独设置。我们简单地理解成对标题工具栏的设置,它有下面这些可以进行设置的选项。
title
字符串,可以作为 headerTitle
的备用(可替代)选项(即这两种写法产生的效果是一样的)。另外,在 TabNavigator 中使用时可以作为 tabBarLabel
备用选项,在 DrawerNavigator 中使用时可以作为 drawerLabel
的备用选项。
header
它的值是一个 React 元素或者是让 HeaderProps
返回一个React 元素的函数,用来展示在头部上。值为 null
则隐藏头部。
headerTitle
可以在头部展示的字符串、React 属性或者组件。默认和 title
的应用场景是一样的,但是 title
仅仅是个字符串,而 headerTitle
范围更广。当值为组件的时候,它接受名为 allowFontScaling
,style
以及 children
的属性,title 的字符串被传递给了 children
。
headerTitleAllowFontScaling
标题文字的大小是否应该缩放以适应 Text Size 辅助性性设置,默认值是 true。
headerBackTitle
iOS 平台特有,返回键旁边显示的文本字符串,或者使用 null
来禁用它。默认值显示上一个页面的 headerTitle
。
headerTruncatedBackTitle
当 headerBackTitle
不适应屏幕时,返回按钮所使用的文本。默认值是 'Back'
。
headerRight
展示在标题栏右侧的 React 元素。
headerLeft
展示在标题栏左侧的 React 元素或者组件。当使用一个组件的时,它被渲染时接受许多属性(onPress
,title
,titleStyle
等,更多可以参考 Header.js
)。
headerStyle
头部标题栏的样式(Style)。
headerTitleStyle
标题的样式。
headerBackTitleStyle
返回键文本的样式。
headerTintColor
头部的 tint color 。
headerPressColorAndroid
Android 特性,按下水波纹效果的颜色(版本须大于 5.0)。
gesturesEnabled
是否支持手势滑动关闭页面。iOS 平台默认 true,Android 则是 false。
gestureResponseDistance
允许你重写手势到屏幕边缘的距离,它是一个 Object 对象,有下面两个属性:
- horizontal
- number 类型 - 距离边缘的水平距离,默认是 25。
- vertical
- number 类型 - 距离边缘的竖直距离,默认是 135。
Navigator Props
当 navigator 组件通过 StackNavigator(...)
方式创建的时候,它就具有了以下属性:
const SomeStack = StackNavigator({
// config
});
<SomeStack
screenProps={/* 这些属性将被传递给在 StackNavigator 中注册的页面, 在这些页面中可以直接通过 this.props.screenProps.xxx 来获取你在这里写的属性 */}
/>
一个自定义页面切换的例子
这个例子是官网的,对 transitionConfig
这个属性进行了很多自定义的设置,大家可以参考一下。由于里面有些变量不知道是在哪里定义的,所以我没有跑得起来,也没法看到实际效果了。
const ModalNavigator = StackNavigator(
{
Main: { screen: Main },Login: { screen: Login },{
headerMode: 'none',mode: 'modal',navigationOptions: {
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 height = layout.initHeight;
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 }] };
},}),}
);
一个自定义标题栏的例子
请记住,Header(头部) 是 StackNavigator 特有的。
这个就比较简单了,只是给标题栏加了一个右侧的按钮,我们在上一节的例子上进行修改。
static navigationOptions = ({navigation}) => ({
title: `Chat with ${navigation.state.params.user}`,headerRight: <Button title='Info'/>
});
然后,给右侧按钮添加点击事件。点击按钮之后可以改变标题和按钮的文本。
static navigationOptions = ({navigation}) => {
const {state,setParams} = navigation;
const isInfo = state.params.mode === 'info';
const {user} = state.params;
return {
title: isInfo ? `${user}'s Contact Info` : `Chat with ${state.params.user}`,headerRight: (
<Button
title={isInfo ? 'Done' : `${user}'s info`}
onPress={() => setParams({mode: isInfo ? 'none' : 'info'})}
/>
)
}
};
这里我们用到了 navigation
的 state
与 setParams
属性,state
在上一节已经见过了,setParams
这个参数顾名思义就是用来改变参数的,并且使用之后会更新标题栏。navigation
的更多属性打算在下一篇文章介绍。看一下效果:
一个顶部标题栏与组件交互的例子
比方说,我们要创建一个 “编辑联系人信息” 的页面,在标题中有一个保存按钮。我们希望在按下保存按钮之后,按钮被一个 ActivityIndicator
(圆形加载组件) 替代。
export default class EditInfoScreen extends Component {
static navigationOptions = ({navigation}) => {
const {params = {}} = navigation.state;
let headerRight = (
<Button
title="Save"
onPress={params.handleSave ? params.handleSave : () => null}
/>
);
if (params.isSaving) {
headerRight = <ActivityIndicator/>;
}
return {
title: 'Example',headerRight: headerRight
}
};
state = {
nickname: 'Lucy jacuzzi'
}
_handleSave = () => {
// Update state,show ActivityIndicator
this.props.navigation.setParams({isSaving: true});
// Fictional function to save information in a store somewhere
// saveInfo().then(() => { // this.props.navigation.setParams({isSaving: false}); // }) } componentDidMount() { // We can only set the function after the component has been initialized this.props.navigation.setParams({handleSave: this._handleSave}); } render() { return ( <TextInput onChangeText={(nickname) => this.setState({nickname})} placeholder={'Nickname'} value={this.state.nickname} /> ); } }
代码也很简单,如果你前面的都看过了,再看这里肯定是毫无压力了。效果图:
注意:在这个例子里,参数 handleSave
是在组件被装载完成之后才被设置的,所以最开始在 navigationOptions
函数中没办法取到它。在 handleSave
被设置之前,我们先给 Button
的点击事件传递了一个空函数来避免渲染时的闪烁问题。
本节暂时讲到这里,StackNavigator 的东西太多太杂,梳理起来比较麻烦,所以写的也是比较乱,请大家多多见谅吧。