React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

在上一次https://www.cnblogs.com/webor2006/p/14733646.html咱们对react-navigation导航器进行了一个基础的认识,而它本身又是一个比较复杂又常用的组件,所以这次专门来对它进行一个整体的学习,为下一次项目的启动做准备。

堆栈导航器:

效果:

使用堆栈导航器主要是实现如下几个场景,也是最基础的一个导航器:

1、页面跳转:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

其中包含页面之间的传值。

2、页面内容与导航栏之间的交互:

另外还有就是,标题栏与内容区域的交互问题,如下:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

其中在内容输入文本中输入内容时,会能反馈到标题上,而在标题上操作时,也能将其状态反馈到内容区域上。

实现:

先准备多个页面:

既然要实现页面之间的跳转,首先先准备几个测试页面,工程是基于上一次的,如下:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

里面的内容很简单:

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

export default class Page1 extends React.Component {
  render() {
    return <View style={{ flex: 1, backgroundColor: "gray", paddingTop: 30 }}>
      <Text style={styles.text}>欢迎来到Page1</Text>
    </View>;
  }
}

const styles = StyleSheet.create({
  text: {
    fontsize: 20,
    color: "white",
  },
});

如果对这块的代码还是有些模糊的话,可以先复习一下基础,这就是一个React的基础页面,接下来再增加一个返回上一页的操作按钮:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

然后再准备一个按钮,用来跳转其它页面:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

好,可以以此为模板,快速再造几个页面:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

接下来再来准备一个入口首页面:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

创建堆栈导航器:

对于这些个页面,运行app时首先展示哪个页面呢?此时就需要用到导航器了,由于下面还有几个导航器需要学习,所以为了统一管理,将其都放到一个新目录中,如下:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

接下来则来创建堆栈导航器:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

另外它还可有配置第二个参数,不过这里暂且只是先了解一下,先不配置:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

比如说可以通过这个全局配置对所有页面的标题栏给隐藏,这个待用到时再说。

使用导航器:

接下来将咱们创建的导航器给使用一下,如下:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

这样就可以将导航器转换为页面的元素了,之后使用导航器就跟使用正常的页面一样。

运行:

接下来咱们运行看一下效果,发现报错了:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

其实是少了一个库,这里执行一下这个命令:"yarn add react-native-safe-area-context":

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

然后注意,此时需要进入到ios目录,执行一下“pod install”:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

然后再运行,发现又报另一个错了。。

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

笔误:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

其它界面可以都改一下,再运行就正常啦,如下:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

定义标题:

其中可以看到,在ios中,在中转到子页面时,在返回按钮处会显示上一个页面的title:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

那如果想自定义这个文案呢?可以这样:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

这是对所有页面都生效的,那,如果某个页面想单独定义,可以这样在具体的页面这样定义:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

此时再运行看一下:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器 

其实还有一种设置方法,就是在创建导航器那块进行设置:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

另外还可以给具体的页面设置一个标题,方式也跟返回标题文案一样,增加一个属性既可:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

运行:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

动态设置navigationOptions:

另外,还可以在跳转时通过传递动态的参数,来进行navigationOptions属性的设置,这里还是以设置页面标题为例:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

接下来,在这块可以接收这个参数:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

然后接收在这块:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器 

运行:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

页面内容与导航栏之间的交互:

如上面效果所示,接下来完成相关的效果,这也是实际项目中非常常见的一种场景。

1、右上角增加一个操作按钮:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

运行,发现报错了,也是一个非常经典的错:ReferenceError: Can't find variable: React

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

少了一个import:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

运行看一下:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

2、增加点击事件:

当点击保存时,这里则会有两个状态,如下:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

运行:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

其中有个细节就是:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

3、根据点击的状态,实时反馈在页面上,实现两者的初步交互:

接下来进行一次交互,就是把点击的状态反应在页面上,所以,先到页面中定义一个文件控件展示状态信息:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

运行:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器 

4、页面进行编辑,将状态反馈到标题栏上:

接下来将操作的主体变换一下,变成对页面进行操作,然后再将操作的状态反馈到标题上面,先在页面中声明一下文件输入框:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

其中在导航器那块也进行相关的修改:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

再运行:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

全局的导航器配置:

最后,这里再来对导航器的全局配置作一个了解,也就是它:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器 

如之前也已经使用过一回了,在这里面的配置是针对所有导航器的页面有效的,比如我们要隐藏所有页面的标题栏,可以这样配置:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器 

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

那如果只想针对某个页面来进行设置,则就在具体的页面中设置属性既可:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

底部导航器:

效果:

接下来再来学习另一种导航器,底部Tab效果,应该人人都见过:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

其中在页面中也可以通过程序来动态切换指定的Tab:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

实现:

导入底部导航器:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

配置路由信息:

这里先来配置一个Tab:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

具体配置如下:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

然后使用一下:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

运行,发现报错了:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

网上搜了一下,貌似得安装个东东:https://newbedev.com/shell-error-error-unable-to-resolve-module-react-native-screens-from-node-modules-react-navigation-tabs-lib-module-navigators-createbottomtabnavigator-js-react-native-screens-could-not-be-found-within-the-project-code-example

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

此时在项目依赖包中可以看到:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

记得执行一下"pod install":

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

再来编译一下,运行一下就可以了【注意:不要用reload的方式,需要重新使用npx react-native run-ios运行一下】:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

接下来再来配置另一个Tab:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

运行:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

更改tab的风格:

更改tab选中的颜色:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

更改tab非选中的颜色:

可以这样设置:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

通过代码来跳转到指定Tab:

其实很简单,就是直接用路由跳转既可:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

顶部导航器:

效果:

接着另一个常用的导航器就是顶部tab效果,如下:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

实现:

配置顶部导航器:

跟底部导航器类似,配置如下:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器 

const MaterialTopTabNavigator = createMaterialTopTabNavigator(
  {
    //在这里配置页面路由信息
    Page1: {
      screen: Page1,
      navigationOptions: {
        tabBarLabel: "Page1",
        tabBarIcon: ({ tintColor, focused }) => (
          <Ionicons
            name={"ios-home"}
            size={26}
            style={{ color: tintColor }}
          />
        ),
      },
    },
    Page2: {
      screen: Page2,
      navigationOptions: {
        tabBarLabel: ({ tintColor, focused }) => (//自定义tab文字
          <Text size={26} style={{ color: focused ? "orange" : "grey" }}>Page2</Text>
        ),
        tabBarIcon: ({ tintColor, focused }) => (
          <Ionicons
            name={"ios-people"}
            size={26}
            style={{ color: focused ? "orange" : "grey" }}
          />
        ),
      },
    },
    Page3: {
      screen: Page3,
      navigationOptions: {
        tabBarLabel: ({ tintColor, focused }) => (//自定义tab文字
          <Text size={26} style={{ color: focused ? "orange" : "grey" }}>Page2</Text>
        ),
        tabBarIcon: ({ tintColor, focused }) => (
          <Ionicons
            name={"ios-people"}
            size={26}
            style={{ color: focused ? "orange" : "grey" }}
          />
        ),
      },
    },
  },
  {
    tabBarOptions: {
      activeTintColor: "red",
    },
  },
);

然后在堆栈导航器路由信息处,这样配置:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

然后将顶部和底部导航器都配置到HomePage中:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

运行:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

定义样式:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

然后效果如下:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

而对于顶部导航器中的图标是不支持的,所以咱们可以将图标的设置给去掉:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器 

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

切换导航器:

接下来再来学习另一个导航器,就是从A页面切到B页面,但是B页面是不能返回到A页面的,这个通常在APP登录页面时会有此需求,也就是通常登录之后进入首页了,首页是不会让用户返回到登录页的, 

新建导航器文件:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

准备一个登录页面:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

配置导航器:

import { createStackNavigator } from "react-navigation-stack";
import HomePage from "../pages/HomePage";
import Page1 from "../pages/Page1";
import Login from "../pages/Login";

//登录之后的导航器
const AppStack = createStackNavigator(
  {
    Home: {
      screen: HomePage,
    },
    Page1: {
      screen: Page1,
    },
  },
);

//登录导航器
const AuthStack = createStackNavigator(
  {
    Login: {
      screen: Login,
    },
  },
);

其中登录之后跳到HomePage时,里面可以跳转到Page1,修改一下页面代码:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

好,配置了两个导航器怎么用呢?此时就可以使用到这个切换导航器了,如下:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

其中在之前登录页面中点击按钮已经设置跳转到首页了:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

最后,别忘了在这块配置咱们的这个切换导航器:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器 

运行:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器 

可以看到,确实是在首页就无法再返回到登录页面了。

另外最后,有一个细节需要说明一下:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

其实是两种写法,也可以如下定义:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

抽屉导航器:

效果:

侧拉抽屉效果也是APP中常用的一种布局方式,如下:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器 

实现:

准备page4、page5页面:

这里在侧滑列表中会用到另外两个页面,先准备好:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器 

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

配置导航器:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

其中侧拉的每一项有一个图标和文字:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

可以这样配置:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

将各种导航器汇集到HomePage中:

为了便于以后的复习,将所有学到的导航器都弄汇集到一起,具体如下:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

然后在HomePage中来调用一下:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

最后还得修改一下它:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

运行:

发现报错了。。

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

网上搜了一下,在这个帖子找到相关的解决方案:https://github.com/react-navigation/react-navigation/issues/9665

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

我目前使用的是高于它的:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

所以咱们将版本降低到2.1.0试一下:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

此时确保版本为:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

此时再运行,发现好使了,如果发现运行还是不好使,最后往下翻贴子,发现了新大路,就是它:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

执行完它之后,再运行就正常了,效果如下:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

说实话,RN的坑真的是无处不在呀~~

调用抽屉常用API:

接下来回到页面中,进行抽屉行为的控制,如打开与关闭抽屉,常用的抽屉API有:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

具体来使用一下:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

此时运行一下:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

自定义侧拉栏选项卡:

对于展开的侧拉栏,咱们也可以进行自定义,首先需要导入一些组件:

1、SafeAreaView:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

在导航器中会使用它来适配刘海屏。

2、ScrollView:

当侧拉选项过多时,需要可以上下滚动,所以需要导入它:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

3、DrawerNavigatorItems:

接下来再来导入它:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

4、开始DIY:

接下来则来配置一下,先来改变一下整体的背景色:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

运行看一下:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

其中对于每一个顶DrawerNavigatorItems也可以样式自定义设置的,这里就看一下它的相关属性,不演示了:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

另外,还可以针对所有侧边项进行全局的配置,比如目前菜单选中是样式看不太清:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

这里将选中时的图标和文字变成白色,如下:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

此时的效果:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

在Android上运行:

最后,咱们再看一下在Android上运行的效果,先将服务启动“react-native start”,然后再执行“npx react-native run-android”,发现报错了。。

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

主是因为咱们使用到了react-native-screens:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

它应该是有gradle版本的要求,那此时咱们需要到android目录中改一下gradle的版本为:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

此时是不是可以看出,要想RN学得好,其实你要是能对Native平台有相关了解会更加妥,因为有时候是需要修改native的东东的,好,当你修改了gradle版本之后,有一个非常要注意的地方来了:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

为啥?因为有可能有缓存干扰你运行【有时候会遇到安装不上的问题,反正各种诡异情况出现时,第一时间想到它】,咱们再来运行看一下,可能你会遇到这种错误:

(base) xiongweideMacBook-Pro:react_navigation_demo xiongwei$ npx react-native run-android
info Running jetifier to migrate libraries to AndroidX. You can disable it using "--no-jetifier" flag.
Jetifier found 1101 file(s) to forward-jetify. Using 4 workers...
info Starting JS server...
info Installing the app...

> Task :app:installDebug FAILED
02:53:22 V/ddms: execute: running am get-config
02:53:22 V/ddms: execute 'am get-config' on '4ee36565' : EOF hit. Read: -1
02:53:22 V/ddms: execute: returning
Installing APK 'app-debug.apk' on 'Redmi Note 7 - 9' for app:debug
02:53:22 D/app-debug.apk: Uploading app-debug.apk onto device '4ee36565'
02:53:22 D/Device: Uploading file onto device '4ee36565'
02:53:22 D/ddms: Reading file permision of /Users/xiongwei/Documents/workspace/reactnativestudy/reactive-native-study/react_navigation_demo/android/app/build/outputs/apk/debug/app-debug.apk as: rw-r--r--
02:53:23 V/ddms: execute: running pm install -r -t "/data/local/tmp/app-debug.apk"
02:53:23 V/ddms: execute 'pm install -r -t "/data/local/tmp/app-debug.apk"' on '4ee36565' : EOF hit. Read: -1
02:53:23 V/ddms: execute: returning

Deprecated Gradle features were used in this build, making it incompatible with Gradle 7.0.
Use '--warning-mode all' to show the individual deprecation warnings.
See https://docs.gradle.org/6.1.1/userguide/command_line_interface.html#sec:command_line_warnings
129 actionable tasks: 2 executed, 127 up-to-date
Unable to install /Users/xiongwei/Documents/workspace/reactnativestudy/reactive-native-study/react_navigation_demo/android/app/build/outputs/apk/debug/app-debug.apk
com.android.ddmlib.InstallException: Unknown failure: Error: Failed to parse APK file: /data/local/tmp/app-debug.apk
Exception occurred while executing:
java.lang.IllegalArgumentException: Error: Failed to parse APK file: /data/local/tmp/app-debug.apk
at com.android.server.pm.PackageManagerShellCommand.setParamsSize(PackageManagerShellCommand.java:338)
at com.android.server.pm.PackageManagerShellCommand.runInstall(PackageManagerShellCommand.java:906)
at com.android.server.pm.PackageManagerShellCommand.onCommand(PackageManagerShellCommand.java:158)
at android.os.ShellCommand.exec(ShellCommand.java:103)
at com.android.server.pm.PackageManagerService.onShellCommand(PackageManagerService.java:21933)
at android.os.Binder.shellCommand(Binder.java:634)
at android.os.Binder.onTransact(Binder.java:532)
at android.content.pm.IPackageManager$Stub.onTransact(IPackageManager.java:2809)
at com.android.server.pm.PackageManagerService.onTransact(PackageManagerService.java:4023)
at android.os.Binder.execTransact(Binder.java:735)
Caused by: android.content.pm.PackageParser$PackageParserException: Failed to parse /data/local/tmp/app-debug.apk
at android.content.pm.PackageParser.parseApkLiteInner(PackageParser.java:1612)
at android.content.pm.PackageParser.parseApkLite(PackageParser.java:1570)
at com.android.server.pm.PackageManagerShellCommand.setParamsSize(PackageManagerShellCommand.java:331)
... 9 more
Caused by: java.io.FileNotFoundException: AndroidManifest.xml
at android.content.res.ApkAssets.nativeOpenXml(Native Method)
at android.content.res.ApkAssets.openXml(ApkAssets.java:152)
at android.content.pm.PackageParser.parseApkLiteInner(PackageParser.java:1589)
... 11 more
    at com.android.ddmlib.Device.installRemotePackage(Device.java:1133)
    at com.android.ddmlib.Device.installPackage(Device.java:963)
    at com.android.ddmlib.Device.installPackage(Device.java:939)
    at com.android.ddmlib.Device.installPackage(Device.java:928)
    at com.android.builder.testing.ConnectedDevice.installPackage(ConnectedDevice.java:126)
    at com.android.build.gradle.internal.tasks.InstallVariantTask.install(InstallVariantTask.java:175)
    at com.android.build.gradle.internal.tasks.InstallVariantTask.doTaskAction(InstallVariantTask.java:106)
    at com.android.build.gradle.internal.tasks.NonIncrementalTask$taskAction$$inlined$recordTaskAction$1.invoke(AndroidVariantTask.kt:51)
    at com.android.build.gradle.internal.tasks.NonIncrementalTask$taskAction$$inlined$recordTaskAction$1.invoke(AndroidVariantTask.kt:31)
    at com.android.build.gradle.internal.tasks.Blocks.recordSpan(Blocks.java:91)
    at com.android.build.gradle.internal.tasks.NonIncrementalTask.taskAction(NonIncrementalTask.kt:34)
    at sun.reflect.GeneratedMethodAccessor75.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.gradle.internal.reflect.JavaMethod.invoke(JavaMethod.java:104)
    at org.gradle.api.internal.project.taskfactory.StandardTaskAction.doExecute(StandardTaskAction.java:49)
    at org.gradle.api.internal.project.taskfactory.StandardTaskAction.execute(StandardTaskAction.java:42)
    at org.gradle.api.internal.project.taskfactory.StandardTaskAction.execute(StandardTaskAction.java:28)
    at org.gradle.api.internal.AbstractTask$TaskActionWrapper.execute(AbstractTask.java:727)
    at org.gradle.api.internal.AbstractTask$TaskActionWrapper.execute(AbstractTask.java:694)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter$3.run(ExecuteActionsTaskExecuter.java:568)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:402)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:394)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor$1.execute(DefaultBuildOperationExecutor.java:165)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:250)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:158)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:92)
    at org.gradle.internal.operations.DelegatingBuildOperationExecutor.run(DelegatingBuildOperationExecutor.java:31)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeAction(ExecuteActionsTaskExecuter.java:553)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:536)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.access$300(ExecuteActionsTaskExecuter.java:109)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter$TaskExecution.executeWithPreviousOutputFiles(ExecuteActionsTaskExecuter.java:276)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter$TaskExecution.execute(ExecuteActionsTaskExecuter.java:265)
    at org.gradle.internal.execution.steps.ExecuteStep.lambda$execute$1(ExecuteStep.java:33)
    at java.util.Optional.orElseGet(Optional.java:267)
    at org.gradle.internal.execution.steps.ExecuteStep.execute(ExecuteStep.java:33)
    at org.gradle.internal.execution.steps.ExecuteStep.execute(ExecuteStep.java:26)
    at org.gradle.internal.execution.steps.CleanupOutputsStep.execute(CleanupOutputsStep.java:63)
    at org.gradle.internal.execution.steps.CleanupOutputsStep.execute(CleanupOutputsStep.java:35)
    at org.gradle.internal.execution.steps.ResolveInputChangesStep.execute(ResolveInputChangesStep.java:49)
    at org.gradle.internal.execution.steps.ResolveInputChangesStep.execute(ResolveInputChangesStep.java:34)
    at org.gradle.internal.execution.steps.CancelExecutionStep.execute(CancelExecutionStep.java:43)
    at org.gradle.internal.execution.steps.TimeoutStep.executeWithoutTimeout(TimeoutStep.java:73)
    at org.gradle.internal.execution.steps.TimeoutStep.execute(TimeoutStep.java:54)
    at org.gradle.internal.execution.steps.CatchExceptionStep.execute(CatchExceptionStep.java:34)
    at org.gradle.internal.execution.steps.CreateOutputsStep.execute(CreateOutputsStep.java:44)
    at org.gradle.internal.execution.steps.SnapshotOutputsStep.execute(SnapshotOutputsStep.java:54)
    at org.gradle.internal.execution.steps.SnapshotOutputsStep.execute(SnapshotOutputsStep.java:38)
    at org.gradle.internal.execution.steps.BroadcastChangingOutputsStep.execute(BroadcastChangingOutputsStep.java:49)
    at org.gradle.internal.execution.steps.CacheStep.executeWithoutCache(CacheStep.java:153)
    at org.gradle.internal.execution.steps.CacheStep.execute(CacheStep.java:67)
    at org.gradle.internal.execution.steps.CacheStep.execute(CacheStep.java:41)
    at org.gradle.internal.execution.steps.StoreExecutionStateStep.execute(StoreExecutionStateStep.java:44)
    at org.gradle.internal.execution.steps.StoreExecutionStateStep.execute(StoreExecutionStateStep.java:33)
    at org.gradle.internal.execution.steps.RecordOutputsStep.execute(RecordOutputsStep.java:38)
    at org.gradle.internal.execution.steps.RecordOutputsStep.execute(RecordOutputsStep.java:24)
    at org.gradle.internal.execution.steps.SkipUpToDateStep.executeBecause(SkipUpToDateStep.java:92)
    at org.gradle.internal.execution.steps.SkipUpToDateStep.lambda$execute$0(SkipUpToDateStep.java:85)
    at java.util.Optional.map(Optional.java:215)
    at org.gradle.internal.execution.steps.SkipUpToDateStep.execute(SkipUpToDateStep.java:55)
    at org.gradle.internal.execution.steps.SkipUpToDateStep.execute(SkipUpToDateStep.java:39)
    at org.gradle.internal.execution.steps.ResolveChangesStep.execute(ResolveChangesStep.java:76)
    at org.gradle.internal.execution.steps.ResolveChangesStep.execute(ResolveChangesStep.java:37)
    at org.gradle.internal.execution.steps.legacy.MarkSnapshottingInputsFinishedStep.execute(MarkSnapshottingInputsFinishedStep.java:36)
    at org.gradle.internal.execution.steps.legacy.MarkSnapshottingInputsFinishedStep.execute(MarkSnapshottingInputsFinishedStep.java:26)
    at org.gradle.internal.execution.steps.ResolveCachingStateStep.execute(ResolveCachingStateStep.java:94)
    at org.gradle.internal.execution.steps.ResolveCachingStateStep.execute(ResolveCachingStateStep.java:49)
    at org.gradle.internal.execution.steps.CaptureStateBeforeExecutionStep.execute(CaptureStateBeforeExecutionStep.java:79)
    at org.gradle.internal.execution.steps.CaptureStateBeforeExecutionStep.execute(CaptureStateBeforeExecutionStep.java:53)
    at org.gradle.internal.execution.steps.ValidateStep.execute(ValidateStep.java:74)
    at org.gradle.internal.execution.steps.SkipEmptyWorkStep.lambda$execute$2(SkipEmptyWorkStep.java:78)
    at java.util.Optional.orElseGet(Optional.java:267)
    at org.gradle.internal.execution.steps.SkipEmptyWorkStep.execute(SkipEmptyWorkStep.java:78)
    at org.gradle.internal.execution.steps.SkipEmptyWorkStep.execute(SkipEmptyWorkStep.java:34)
    at org.gradle.internal.execution.steps.legacy.MarkSnapshottingInputsStartedStep.execute(MarkSnapshottingInputsStartedStep.java:39)
    at org.gradle.internal.execution.steps.LoadExecutionStateStep.execute(LoadExecutionStateStep.java:40)
    at org.gradle.internal.execution.steps.LoadExecutionStateStep.execute(LoadExecutionStateStep.java:28)
    at org.gradle.internal.execution.impl.DefaultWorkExecutor.execute(DefaultWorkExecutor.java:33)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeIfValid(ExecuteActionsTaskExecuter.java:192)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:184)
    at org.gradle.api.internal.tasks.execution.CleanupStaleOutputsExecuter.execute(CleanupStaleOutputsExecuter.java:109)
    at org.gradle.api.internal.tasks.execution.FinalizePropertiesTaskExecuter.execute(FinalizePropertiesTaskExecuter.java:46)
    at org.gradle.api.internal.tasks.execution.ResolveTaskExecutionModeExecuter.execute(ResolveTaskExecutionModeExecuter.java:62)
    at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:57)
    at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:56)
    at org.gradle.api.internal.tasks.execution.CatchExceptionTaskExecuter.execute(CatchExceptionTaskExecuter.java:36)
    at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.executeTask(EventFiringTaskExecuter.java:77)
    at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:55)
    at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:52)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor$CallableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:416)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor$CallableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:406)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor$1.execute(DefaultBuildOperationExecutor.java:165)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:250)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:158)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:102)
    at org.gradle.internal.operations.DelegatingBuildOperationExecutor.call(DelegatingBuildOperationExecutor.java:36)
    at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter.execute(EventFiringTaskExecuter.java:52)
    at org.gradle.execution.plan.LocalTaskNodeExecutor.execute(LocalTaskNodeExecutor.java:41)
    at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:372)
    at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:359)
    at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:352)
    at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:338)
    at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.lambda$run$0(DefaultPlanExecutor.java:127)
    at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.execute(DefaultPlanExecutor.java:191)
    at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.executeNextNode(DefaultPlanExecutor.java:182)
    at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.run(DefaultPlanExecutor.java:124)
    at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
    at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:48)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:56)
    at java.lang.Thread.run(Thread.java:748)

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':app:installDebug'.
> com.android.builder.testing.api.DeviceException: com.android.ddmlib.InstallException: Unknown failure: Error: Failed to parse APK file: /data/local/tmp/app-debug.apk
  Exception occurred while executing:
  java.lang.IllegalArgumentException: Error: Failed to parse APK file: /data/local/tmp/app-debug.apk
  at com.android.server.pm.PackageManagerShellCommand.setParamsSize(PackageManagerShellCommand.java:338)
  at com.android.server.pm.PackageManagerShellCommand.runInstall(PackageManagerShellCommand.java:906)
  at com.android.server.pm.PackageManagerShellCommand.onCommand(PackageManagerShellCommand.java:158)
  at android.os.ShellCommand.exec(ShellCommand.java:103)
  at com.android.server.pm.PackageManagerService.onShellCommand(PackageManagerService.java:21933)
  at android.os.Binder.shellCommand(Binder.java:634)
  at android.os.Binder.onTransact(Binder.java:532)
  at android.content.pm.IPackageManager$Stub.onTransact(IPackageManager.java:2809)
  at com.android.server.pm.PackageManagerService.onTransact(PackageManagerService.java:4023)
  at android.os.Binder.execTransact(Binder.java:735)
  Caused by: android.content.pm.PackageParser$PackageParserException: Failed to parse /data/local/tmp/app-debug.apk
  at android.content.pm.PackageParser.parseApkLiteInner(PackageParser.java:1612)
  at android.content.pm.PackageParser.parseApkLite(PackageParser.java:1570)
  at com.android.server.pm.PackageManagerShellCommand.setParamsSize(PackageManagerShellCommand.java:331)
  ... 9 more
  Caused by: java.io.FileNotFoundException: AndroidManifest.xml
  at android.content.res.ApkAssets.nativeOpenXml(Native Method)
  at android.content.res.ApkAssets.openXml(ApkAssets.java:152)
  at android.content.pm.PackageParser.parseApkLiteInner(PackageParser.java:1589)
  ... 11 more

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Get more help at https://help.gradle.org

BUILD FAILED in 7s

error Failed to install the app. Make sure you have the Android development environment set up: https://reactnative.dev/docs/environment-setup. Run CLI with --verbose flag for more details.
Error: Command failed: ./gradlew app:installDebug -PreactNativeDevServerPort=8081
Unable to install /Users/xiongwei/Documents/workspace/reactnativestudy/reactive-native-study/react_navigation_demo/android/app/build/outputs/apk/debug/app-debug.apk

这时可以把这俩目录都给删掉:

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

再来编译运行一下貌似就好了,整体效果如下: 

React Native从入门到实战--常用导航器之堆栈导航器、底部导航器、顶部导航器、切换导航器、抽屉导航器

效果还是不错的。

总结:

至此,我们对于常用的导航器有了一定的了解了,在下一次则正式进入到项目开发来综合操练RN的知识了,有了之前扎实的基础,之后项目的学习也会变得比较踏实。

上一篇:macbook m1 JDK环境与AndResGuard编译遇到的Gradle同步报错


下一篇:Android-gradle入门到项目实战 笔记汇总