美文网首页
react-native 项目搭建

react-native 项目搭建

作者: Yinzhishan | 来源:发表于2022-09-16 12:43 被阅读0次

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

    视频播放器的使用

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

    先说结论:

    • 一定要使用 ^6.0.0-alpha.1 版本,运行npm i react-native-video@6.0.0-alpha.1 下载;
    • 下载之后,要运行一次 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<ReactPackage> getPackages() {
         @SuppressWarnings("UnnecessaryLocalVariable")
         List<ReactPackage> 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 组件;

    沉浸式和安全区

    沉浸式的操作

    使用<statusBar /> 组件来实现;

    1. statusBar 组件,会被后一次的设置覆盖;
    2. ios默认是沉浸式的,所以要给根组件设置一个默认的paddingTop
    3. android默认不是沉浸式的,需要设置以下代码变成沉浸式,然后根组件设置一个默认的paddingTop
     <StatusBar translucent={true} backgroundColor="transparent" />
    

    设置的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 (
        <SafeAreaView style={styles.container}>
          <Text>Page content</Text>
        </SafeAreaView>
      );
    }
    
    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={<View />} // 指示点的样式
    • activeDot={<View />} // 活跃的指示点的样式
    
    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 () => (
      <Swiper style={styles.wrapper} showsButtons loop={false}>
        <View testID="Hello" style={styles.slide1}>
          <Text style={styles.text}>Hello Swiper</Text>
        </View>
        <View testID="Beautiful" style={styles.slide2}>
          <Text style={styles.text}>Beautiful</Text>
        </View>
        <View testID="Simple" style={styles.slide3}>
          <Text style={styles.text}>And simple</Text>
        </View>
      </Swiper>
    )
    

    使用路由

    文档地址

    在页面上直接使用 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 (
        <View style={{flex: 1, alignItems: 'center', justifyContent: 'center'}}>
          <Text>嘿嘿12</Text>
          <Button
            title="Go to Details"
            onPress={() => navigation.navigate('Details')}
          />
        </View>
      );
    }
    
    function DetailsScreen(props: any) {
      const {navigation} = props;
      return (
        <View style={{flex: 1, alignItems: 'center', justifyContent: 'center'}}>
          <Text>Details Screen</Text>
          <Button
            title="Go to Details... again"
            onPress={() => navigation.push('Details')}
          />
          <Button title="Go to Home" onPress={() => navigation.navigate('Home')} />
          <Button title="Go back" onPress={() => navigation.goBack()} />
          <Button
            title="Go back to first screen in stack"
            onPress={() => navigation.popToTop()}
          />
        </View>
      );
    }
    
    const Stack = createNativeStackNavigator();
    
    function RouterView() {
      return (
        <NavigationContainer>
          <Stack.Navigator initialRouteName="Home">
            <Stack.Screen
              name="Home"
              component={HomeScreen}
              options={{title: 'Overview'}}
            />
            <Stack.Screen name="Details" component={DetailsScreen} />
          </Stack.Navigator>
        </NavigationContainer>
      );
    }
    
    export default RouterView;
    

    在组件中使用 useNavigate

    import * as React from 'react';
    import { Button } from 'react-native';
    import { useNavigation } from '@react-navigation/native';
    
    function MyBackButton() {
      const navigation = useNavigation();
    
      return (
        <Button
          title="Back"
          onPress={() => {
            navigation.goBack();
          }}
        />
      );
    }
    

    使用 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 <Ionicons name={iconName} size={size} color={color} />;
    }
    
    
    icons.png

    相关文章

      网友评论

          本文标题:react-native 项目搭建

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