React Navigation 入门(三) - StackNa

作者: ayuhani | 来源:发表于2017-11-28 17:57 被阅读1121次

    RN 版本:0.50
    操作环境:Windows 10
    React Navigation 版本:1.0.0-beta.20

    文章已同步至 CSDN:http://blog.csdn.net/qq_24867873/article/details/78657683

    本节主要讲 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)
    

    这里有两个参数 RouteConfigsStackNavigatorConfig,接下来分别对它们进行介绍。

    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 - 指定标题栏的渲染方式:
      • float - 在页面切换时渲染一个固定在顶部的单个标题栏和动画。这是 iOS 上的常见模式。
      • screen - 每个页面都有各自的标题栏,并且伴随着页面切换一起淡入淡出。这是 Android 上的常见模式。
      • none - 不渲染标题栏,当你需要自定义标题栏的时候,可以设置这个选项。
    • cardStyle - 使用这个属性重写或继承栈中单个卡片(页面)的默认样式。
    • transitionConfig - 返回值是与默认页面切换相关的对象的函数。提供的函数将传递以下参数:
      • transitionProps - 给新页面的切换相关属性。
      • prevTransitionProps - 给旧页面的切换相关属性。
      • isModal - 切换风格是否是 modal。
    • 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>
        );
      }
    }
    

    效果就像下面这样,具体的各个属性都有什么实际效果,可以自己去尝试一下,有的属性很少会用到。

    ChatScreen

    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(...) 方式创建的时候,它就具有了以下属性:

    • screenProps - 向子页面传递额外的属性,比如:
    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'})}
              />
          )
        }
      };
    

    这里我们用到了 navigationstatesetParams 属性,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 的东西太多太杂,梳理起来比较麻烦,所以写的也是比较乱,请大家多多见谅吧。

    欢迎关注我的微信公众号

    相关文章

      网友评论

        本文标题:React Navigation 入门(三) - StackNa

        本文链接:https://www.haomeiwen.com/subject/rayxbxtx.html