返回 导航

React / React Native

hangge.com

React Native - 导航组件(Navigator)的使用详解

作者:hangge | 2017-08-17 08:10
React Native 提供了两种导航组件:NavigatorIOS Navigator(从 0.44.0 版本起又新增了 React Navigation)。关于 NavigatorIOS,我之前已写过一篇文章介绍它的用法:React Native - 导航组件(NavigatorIOS)的使用详解。本文接着来讲讲 Navigator

注意:如果 React Native 版本大于等于 0.44.0 时,官方推荐使用 react-navigation。因为 Navigator 官方不会再进行维护了。

一、Navigator 与 NavigatorIOS 的区别

1,NavigatorIOS

NavigatorIOS 是对 UIKit 导航组件的封装,因此它和其他原生应用的表现完全一致。正因如此,它有如下几个缺点:
  • 这个组件只支持 iOS,同时只能使用苹果开发好的动画和行为。
  • 默认包含一个导航栏,由于这个导航栏不是 React Native 视图组件,因此样式只能稍微进行修改。
  • 轻量、受限的 API 设置,使其相对于 Navigator 来说不太方便定制。

2,Navigator

(1)Navigator 是跨平台的导航组件,不管在 iOS 还是 Android 系统下都能使用。所以当 React Native 版本低于 0.44.0 时,开发中更建议使用 Navigator
(2)Navigator 默认没有包含导航栏,我们可以实现一个自定义导航栏,也可以使用 NavigatorBar 组件来实现。

二、Navigator 组件的引入

过去 Navigator 是在 react-native 包中,如果 React Native 版本升级到 0.43 以上的话,Navigator 就不能直接从 react-native 里获取了。否则会报“Navigator is deprecated and has been removed from this package. It can now be installed and imported from 'react-native-deprecated-custom-components'”错误。

解决办法:

(1)由于新版中 Navigator 被移到 react-native-deprecated-custom-components 包中,我们在项目根目录下执行如下命令安装:
npm install react-native-deprecated-custom-components --save

(2)使用时通过如下代码将 Navigator 引入进来:
 import { Navigator } from 'react-native-deprecated-custom-components';

三、Navigator 组件的属性

1,configureScene

该属性指定的回调函数被执行时,会收到一个导航路径参数,开发者可以根据导航路径中的各信息来决定场景切换使用的效果。具体可用的效果值如下:
  • PushFromRight
  • PushFromLeft
  • FloatFromRight
  • FloatFromLeft
  • FloatFromBottom
  • FloatFromBottomAndroid
  • FadeAndroid
  • HorizontalSwipeJump
  • HorizontalSwipeJumpFromLeft
  • VerticalUpSwipeJump
  • VerticalDownSwipeJump

2,renderScene

(1)导航组件必须要提供这个属性。
(2)该属性指定一个回调函数,用来为特定路径实现界面的渲染。
(3)当它被调用时,会提供一个 router 对象(导航路径)和一个 navigator 对象(导航器本身)

3,onDidFocus

(1)该属性用来指定一个回调函数,当导航组件导入初始场景后,或者每一个新的场景切换完成时,这个回调函数将被调用。
(2)它可以接收一个保存有新场景路径信息的参数。
注意:React Native 不建议开发者使用 onDidFoucs 属性,而是鼓励使用 navigationContext.addListener('didfocus',callback) 事件监听器来实现相同的功能。

4,onWillFocus

该属性用来指定一个回调函数,在导航组件准备进行场景切换前,这个回调函数将被调用。
注意:React Native 同样不建议开发者使用 onWillFocus 属性,而是鼓励使用 navigationContext.addListener('willfocus',callback) 事件监听器来实现相同的功能。

5,sceneStyle

style 类型的属性。如果提供了这个属性,指定的样式将被应用到每一个切换的场景中。

6,initialRoute 和 initialRouteStack

这两都是类类型属性,用来在 Navigator 组件初加载时提供导航路径。这两者关系如下:
  • 如果没有向 Navigator 组件提供 initialRoute 属性,就必须要提供 initialRouteStack 属性。
  • 如果提供了 initialRoute 但没有提供 initialRouteStackReact Native 就会生成一个只有 initialRoute 元素的数组作为 initialRouteStack
  • 如果给 Navigator 组件提供了 initialRouteStack 属性,那么 initialRoute 必须是 initialRouteStack 中的一个元素。
  • 如果提供了 initialRouteStack 但没有提供 initialRoute,那么 initialRouteStack 中的最后一个元素将默认为 initialRoute

7,NavigatorBar

该属性返回一个可以渲染的节点,这个节点可以用作所有界面的通用导航栏。

8,其他

如果当前组件是由父 Navigator 组件导航而产生的,则可以对其指定 navigator 属性,用来执行组件导航相关操作。

四、Navigator 组件的方法

1,getCurrentRoutes()

用来得到当前的路径列表。

2,jumpBack()

退回到上一个界面而不卸载当前界面。

3,jumpForward()

界面路径向前跳一个界面而不卸载当前界面。

4,jumpTo(route)

跳转到某个界面而不卸载任何界面。

5,push(route)

导航组件在路径列表最前端添加一个新的界面,并马上跳转到这个界面。

6,pop()

导航器退回一个界面并卸载原界面。

7,replace()

导航器将当前界面用一个新的界面替代。

8,replaceAtIndex(route, index)

使用一个新的界面代替路径列表中的第 index 个界面(下标从 0 开始),但不改变当前显示界面。

9,replacePrevious(route)

将当前导航路径的上一个界面使用指定的界面替代。

10,immediatelyResetRouteStack(routeStack)

使用给定的路径列表替换当前的路径列表。

11,popToRoute(route)

导航器将退回到指定的界面,并在这个过程中将回退过的界面都一一卸载。

12,popToTop()

导航器会回到界面路径列表中的第一个界面,并且卸载其他所有界面。

13,popN()

接收一个数值型参数,导航器会退回多个界面并卸载回退的多个界面。

五、使用样例

1,效果图

(1)程序启动后首先加载显示文章列表。
(2)点击任意一篇文章,进入到详情页面。
(3)在详情页的下方还添加了个返回按钮,点击后即可回到列表页面。
          

2,样例代码

这里我一共创建了 3 个独立的模块组件,分别保存在 3js 文件中。

(1)文章列表页(list.js
import React, { Component } from 'react';
import {
  AppRegistry,
  StyleSheet,
  Text,
  View,
  ScrollView,
} from 'react-native';

export default class List extends Component {
  render() {
    return (
      <ScrollView style={styles.flex}>
        <View style={styles.list_item}>
          <Text style={styles.list_item_font} onPress={this.goTo.bind(this)}>
            Swift - 滑块(UISlider)的用法
          </Text>
        </View>
        <View style={styles.list_item}>
          <Text style={styles.list_item_font} onPress={this.goTo.bind(this)}>
            Swift - 告警框(UIAlertView)的用法
          </Text>
        </View>
        <View style={styles.list_item}>
          <Text style={styles.list_item_font} onPress={this.goTo.bind(this)}>
            Swift - 选择框(UIPickerView)的用法
          </Text>
        </View>
      </ScrollView>
    );
  }

  goTo(){
    //push函数调用后,Navigator组件的renderScene函数将被调用,
    //并且push函数传入的变量成为renderScene函数的第一个参数。
    this.props.navigator.push({
      name:'detail'
    });
  }
}

const styles = StyleSheet.create({
  flex:{
    flex: 1,
  },
  list_item:{
    height:40,
    marginLeft:10,
    marginRight:10,
    borderBottomWidth:1,
    borderBottomColor: '#ddd',
    justifyContent: 'center'
  },
  list_item_font:{
    fontSize:16
  },
});

(2)详情页(detail.js
import React, { Component } from 'react';
import {
  AppRegistry,
  StyleSheet,
  Text,
  View,
  ScrollView,
} from 'react-native';

export default class Detail extends Component {
  render() {
    return (
      <ScrollView style={styles.flex}>
        <Text style={styles.detail_text}>这个是详情页。欢迎访问hangge.com</Text>
        <Text style={styles.back_text} onPress={this.goback.bind(this)}>
          返回
        </Text>
      </ScrollView>
    );
  }

  goback() {
    this.props.navigator.pop();
  }
}

const styles = StyleSheet.create({
  flex:{
    flex: 1,
  },
  detail_text:{
    fontSize: 16,
    margin: 10
  },
  back_text:{
    width: 80,
    backgroundColor: 'gray',
    color: 'white',
    textAlign: 'center',
    fontSize: 18,
    alignSelf: 'center',
    marginTop: 20
  }
});

(3)加载 Navigator 并作为程序入口(index.android.js
import React, { Component } from 'react';
 import {
   AppRegistry,
   StyleSheet,
   BackAndroid,
   Platform,
 } from 'react-native';

 import { Navigator } from 'react-native-deprecated-custom-components';

 //导入自定义的列表组件
 import List from './list'
 //导入自定义的内容详情组件
 import Detail from './detail'

 export default class NV extends Component {
   //组件初始渲染执行完毕后调用
   componentDidMount() {
     //如果当前是Android系统,则添加back键按下事件监听
     if(Platform.OS === "android") {
       BackAndroid.addEventListener('hardwareBackPress', ()=>{
         return this.handleBackAndroid(this.navigator);
       });
     }
   }

   //组件被卸载前会执行
   componentWillUnmount() {
     //如果当前是Android系统,则移除back键按下事件监听
     if(Platform.OS === "android") {
       BackAndroid.removeEventListener('hardwareBackPress', ()=>{});
     }
   }

   //back键按下事件响应
   handleBackAndroid(navigator) {
     //如果存在上一页则后退
     if(navigator.getCurrentRoutes().length > 1) {
       navigator.pop();
       return true; //接管默认行为
     }
     return false;  //使用默认行为(直接退出应用)
   }

   render() {
     return(
       <Navigator
         style = {{flex:1}}
         initialRoute={{name: 'list'}}
         configureScene={this.configureScene.bind(this)}
         renderScene={this.renderScene.bind(this)}
       />
     );
   }

   //返回视图转换时使用的效果
   configureScene(route) {
     return Navigator.SceneConfigs.FadeAndroid;
   }

   //该函数用来告知Navigator模块我们希望如何挂接当前的视图
   renderScene(route, navigator) {
     this.navigator = navigator; //NV组件的navigator变量赋值
     switch(route.name) {
       case "list":
         return <List navigator={navigator} />
       case "detail":
         return <Detail navigator={navigator} />
     }
   }
 }

 const styles = StyleSheet.create({
   flex:{
     flex: 1,
   },
 });

 AppRegistry.registerComponent('HelloWorld', () => NV);
评论

全部评论(0)

回到顶部