react-native 项目搭建

需要实现的功能
1、视频播放;
2、音乐播放;
3、蓝牙链接;
4、沉浸式和安全区
5、轮播图
6、路由系统
7、iconfont
8、持久储存
9、可以使用的css

视频播放器的使用

使用的视频播放器组件为 react-native-video
文档地址

先说结论:

  • 一定要使用 ^6.0.0-alpha.1 版本,运行npm i [email protected] 下载;
  • 下载之后,要运行一次 npx react-native start --reset-cache,然后 再次重启下项目;
  • 关联版本 为 RN :0.69.5 ; react:18.0.0
  • 如果不行,按照以下增加配置
 // android/settings.gradle 
 include ':react-native-video'
 project(':react-native-video').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-video/android')
   //  android/app/build.gradle 
   dependencies {
       ...
       implementation project(':react-native-video')
   }
   // 注意这里不是按照官网配置的
//  android/app/src/main/java/com/文件名/MainApplication.java

// 在头部 添加 import com.brentvatne.react.ReactVideoPackage;
// getPackages()中添加packages.add(new ReactVideoPackage());
+   import com.brentvatne.react.ReactVideoPackage;
   ...
   @Override
   protected List getPackages() {
     @SuppressWarnings("UnnecessaryLocalVariable")
     List packages = new PackageList(this).getPackages();
     // Packages that cannot be autolinked yet can be added manually here, for example:
     // packages.add(new MyReactNativePackage());
+     packages.add(new ReactVideoPackage());
     return packages;
   }

我安装时碰到的问题

开始是按照官网指南,直接使用 npm install --save react-native-video 命令下载组件。然后运行之后报错;
详细看了 Android installation 安装文档,在android项目中,添加了代码,

现在是 2022-9-8; 默认版本是 5.2

// android/app/build.gradle
    compile project(':react-native-video')
+   implementation "androidx.appcompat:appcompat:1.0.0"
-   implementation "com.android.support:appcompat-v7:${rootProject.ext.supportLibVersion}"

然后就 报了 compile() 函数的问题

could not find method compile()for arguments

无奈删掉这些,删掉node_moduies,然后再npm install;
然后起来了,报错

ERROR TypeError: undefined is not an object (evalating '_reactNative.Image.propTypes.resizeMode') 

然后搜索和 查看issues,找到了issues/2714 ,然后还是报错, 就继续向下看,最终找到 npx react-native start --reset-cache; 之后再次重启,成功看到视频,
期间还有 react-native-video 的类型找不到,执行 npm i --save @types/react-native-video之后就好了。

注意, 视频 source 一定要填写,否者会app崩溃

使用默认版本(5.2),还有一个问题是:

Could not find com.yqritc:android-scalablevideoview:1.0.4.

这里其实是因为jCenter不允许更新包,所有其他包应该从mavenCentral获取。因此还需要配置
android/build.gradle文件,allprojects下添加如下配置:
测试也是有用的,但是升级 6.x之后就不用配置了;

allprojects {
    repositories {
        .... # rest of your code
        jcenter() {
            content {
                includeModule("com.yqritc", "android-scalablevideoview")
            }
        }
    }
}

蓝牙链接

使用 react-native-ble-manager 组件;

沉浸式和安全区

沉浸式的操作

使用 组件来实现;

  1. statusBar 组件,会被后一次的设置覆盖;
  2. ios默认是沉浸式的,所以要给根组件设置一个默认的paddingTop
  3. android默认不是沉浸式的,需要设置以下代码变成沉浸式,然后根组件设置一个默认的paddingTop
 

设置的paddingTop值是statusBar的高度,获取方法如下:

import { Platform, NativeModules, StatusBar } from 'react-native';
// 系统信息
const OS = Platform.OS;
// 状态栏高度
const { StatusBarManager } = NativeModules;
let statusBarHeight = 0;
if (OS === 'ios') {
    StatusBarManager.getHeight((_statusBarHeight: number) => {
        statusBarHeight = _statusBarHeight;
    });
} else if (OS === 'android') {
    statusBarHeight = StatusBar.currentHeight || 0;
}

export { statusBarHeight, OS };

IOS安全区

安全区的是实现是通过 SafeAreaView 组件包裹;
SafeAreaView的目的是在一个“安全”的可视区域内渲染内容。具体来说就是因为目前有 iPhone X 这样的带有“刘海”的全面屏设备,所以需要避免内容渲染到不可见的“刘海”范围内。本组件目前仅支持 iOS 设备以及 iOS 11 或更高版本。
SafeAreaView会自动根据系统的各种导航栏、工具栏等预留出空间来渲染内部内容。更重要的是,它还会考虑到设备屏幕的局限,比如屏幕四周的圆角或是顶部中间不可显示的“刘海”区域。
只需简单地把你原有的视图用SafeAreaView包起来,同时设置一个flex: 1的样式。当然可能还需要一些和你的设计相匹配的背景色。

import React from 'react';
import { StyleSheet, Text, SafeAreaView } from 'react-native';

const App = () => {
  return (
    
      Page content
    
  );
}

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

export default App;

可以用的属性

文档地址
图片样式属性
布局属性
阴影样式属性
Text 样式属性
View 样式属性

轮播图组件 react-native-swiper

在首页,需要用轮播图做视频背景的切换,
使用 react-native-swiper 组件,文档地址;

  • style={styles.swiper} // 样式
  • height={200} // 组件高度
  • loop={true} // 如果设置为false,那么滑动到最后一张时,再次滑动将不会滑到第一张图片。
  • autoplay={true} // 自动轮播
  • autoplayTimeout={4} // 每隔4秒切换
  • horizontal={true} // 水平方向,为false可设置为竖直方向
  • paginationStyle={{bottom: 10}} // 小圆点的位置:距离底部10px
  • showsButtons={false} // 为false时不显示控制按钮
  • showsPagination={false} // 为false不显示下方圆点
  • dot={} // 指示点的样式
  • activeDot={} // 活跃的指示点的样式

import React from 'react'
import { Text, View } from 'react-native'
import Swiper from 'react-native-swiper'

var styles = {
  wrapper: {},
  slide1: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#9DD6EB'
  },
  slide2: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#97CAE5'
  },
  slide3: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#92BBD9'
  },
  text: {
    color: '#fff',
    fontSize: 30,
    fontWeight: 'bold'
  }
}

export default () => (
  
    
      Hello Swiper
    
    
      Beautiful
    
    
      And simple
    
  
)

使用路由

文档地址

在页面上直接使用 navigate

  1. navigate
  2. push
  3. goBack
  4. popToTop
import * as React from 'react';
import {View, Text, Button} from 'react-native';
import {NavigationContainer} from '@react-navigation/native';
import {createNativeStackNavigator} from '@react-navigation/native-stack';

function HomeScreen(props: any) {
  const {navigation} = props;
  return (
    
      嘿嘿12
      

在组件中使用 useNavigate

import * as React from 'react';
import { Button } from 'react-native';
import { useNavigation } from '@react-navigation/native';

function MyBackButton() {
  const navigation = useNavigation();

  return (
    

使用 react-native-storage做持久储存

保存、读取和删除

单独处理数据

保存

使用key来保存数据(key-only)。这些数据一般是全局独有的,需要谨慎单独处理的数据
批量数据请使用key和id来保存(key-id),具体请往后看
除非你手动移除,这些数据会被永久保存,而且默认不会过期。

storage.save({
  key: 'loginState', // 注意:请不要在key中使用_下划线符号!
  data: {
    from: 'some other site',
    userid: 'some userid',
    token: 'some token',
  },
  // 如果不指定过期时间,则会使用defaultExpires参数
  // 如果设为null,则永不过期
  expires: 1000 * 3600,
});
读取
// 读取
storage
  .load({
    key: 'loginState',

    // autoSync(默认为true)意味着在没有找到数据或数据过期时自动调用相应的sync方法
    autoSync: true, // 设置为false的话,则等待sync方法提供的最新数据(当然会需要更多时间)。

    // syncInBackground(默认为true)意味着如果数据过期,
    // 在调用sync方法的同时先返回已经过期的数据。
    syncInBackground: true,
    // 你还可以给sync方法传递额外的参数
    syncParams: {
      extraFetchOptions: {
        // 各种参数
      },
      someFlag: true,
    },
  })
  .then(ret => {
    // 如果找到数据,则在then方法中返回
    // 注意:这是异步返回的结果(不了解异步请自行搜索学习)
    // 你只能在then这个方法内继续处理ret数据
    // 而不能在then以外处理
    // 也没有办法“变成”同步返回
    // 你也可以使用“看似”同步的async/await语法

    console.log(ret.userid);
    this.setState({ user: ret });
  })
  .catch(err => {
    //如果没有找到数据且没有sync方法,
    //或者有其他异常,则在catch中返回
    console.warn(err.message);
    switch (err.name) {
      case 'NotFoundError':
        // TODO;
        break;
      case 'ExpiredError':
        // TODO
        break;
    }
  });

删除
  // 删除单个数据
  storage.remove({
    key: 'lastPage',
  });
  storage.remove({
    key: 'user',
    id: '1001',
  });

批量操作数据

保存

使用key和id来保存数据,一般是保存同类别(key)的大量数据。
所有这些"key-id"数据共有一个保存上限(无论是否相同key)
即在初始化storage时传入的size参数。
在默认上限参数下,第1001个数据会覆盖第1个数据。
覆盖之后,再读取第1个数据,会返回catch或是相应的sync方法。

  var userA = {
    name: 'A',
    age: 20,
    tags: ['geek', 'nerd', 'otaku'],
  };

  storage.save({
    key: 'user', // 注意:请不要在key中使用_下划线符号!
    id: '1001', // 注意:请不要在id中使用_下划线符号!
    data: userA,
    expires: 1000 * 60,
  });

读取
  //load 读取
  storage
    .load({
      key: 'user',
      id: '1001',
    })
    .then(ret => {
      // 如果找到数据,则在then方法中返回
      console.log(ret.userid);
    })
    .catch(err => {
      // 如果没有找到数据且没有sync方法,
      // 或者有其他异常,则在catch中返回
      console.warn(err.message);
      switch (err.name) {
        case 'NotFoundError':
          // TODO;
          break;
        case 'ExpiredError':
          // TODO
          break;
      }
    });

  // --------------------------------------------------

  // 获取某个key下的所有id(仅key-id数据)
  storage.getIdsForKey('user').then(ids => {
    console.log(ids);
  });

  // 获取某个key下的所有数据(仅key-id数据)
  storage.getAllDataForKey('user').then(users => {
    console.log(users);
  });

删除

  // !! 清除某个key下的所有数据(仅key-id数据)
  storage.clearMapForKey('user');
  // -------------------------------------------------
  // !! 清空map,移除所有"key-id"数据(但会保留只有key的数据)
  storage.clearMap();

同步远程数据(刷新)

storage.sync = {
  // sync方法的名字必须和所存数据的key完全相同
  // 参数从params中解构取出
  // 最后返回所需数据或一个promise
  async user(params) {
    const {
      id,
      syncParams: { extraFetchOptions, someFlag }
    } = params;
    const response = await fetch('user/?id=' + id, {
      ...extraFetchOptions
    });
    const responseText = await response.text();
    console.log(`user${id} sync resp: `, responseText);
    const json = JSON.parse(responseText);
    if (json && json.user) {
      storage.save({
        key: 'user',
        id,
        data: json.user
      });
      if (someFlag) {
        // 根据一些自定义标志变量操作
      }
      // 返回所需数据
      return json.user;
    } else {
      // 出错时抛出异常
      throw new Error(`error syncing user${id}`);
    }
  }
};

有了上面这个 sync 方法,以后再调用 storage.load 时,如果本地并没有存储相应的 user,那么会自动触发 storage.sync.user 去远程取回数据并无缝返回。

  storage.load({
    key: 'user',
    id: '1002'
  }).then(...)

读取批量数据

使用和load方法一样的参数读取批量数据,但是参数是以数组的方式提供。
会在需要时分别调用相应的sync方法,最后统一返回一个有序数组。

storage.getBatchData([
    { key: 'loginState' },
    { key: 'checkPoint', syncInBackground: false },
    { key: 'balance' },
    { key: 'user', id: '1009' }
])
.then(results => {
  results.forEach( result => {
    console.log(result);
  })
})

//根据key和一个id数组来读取批量数据
storage.getBatchDataWithIds({
  key: 'user',
  ids: ['1001', '1002', '1003']
})
.then( ... )

这两个方法除了参数形式不同,还有个值得注意的差异。getBatchData会在数据缺失时挨个调用不同的 sync 方法(因为 key 不同)。但是getBatchDataWithIds却会把缺失的数据统计起来,将它们的 id 收集到一个数组中,然后一次传递给对应的 sync 方法(避免挨个查询导致同时发起大量请求),所以你需要在服务端实现通过数组来查询返回,还要注意对应的 sync 方法的参数处理(因为 id 参数可能是一个字符串,也可能是一个数组的字符串)。

字体图标 react-native-vector-icons

使用的是 字体图标库react-native-vector-icons;
技术文档;
图标文档;

安装方式

先安装库

npm install --save react-native-vector-icons

安卓
Edit android/app/build.gradle ( NOT android/build.gradle ) and add the following:

apply from: "../../node_modules/react-native-vector-icons/fonts.gradle"

IOS
还未实践

使用

  1. 进入图标官网
  2. 选择自己要用的图标
  3. 看自己要用的图标在哪一个组件中(在页面最上边的红色部分);
  4. 在项目中引用组件,将name赋值;
    如下:

import Ionicons from 'react-native-vector-icons/Ionicons';
const icon = ()=>{
  const iconName = 'shirt-outline'
  const size = 24
  const color = 'red'
  return ;
}

icons.png

你可能感兴趣的:(react-native 项目搭建)