react-native学习 Day1

作者分享了搭建React Native环境的挑战,介绍了基本组件如StyleSheet、useColorScheme和View的用法,并详细展示了如何实现一个带有计时功能的简单Watch应用,包括FlatList、StatusBar等组件的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

react-native Day1

当我用了2天翻越了无数博客文档才搭建起react-native的开发环境之后(忘记记录踩坑指南了@_@),因为使用的是最新版的mac pro,一部分的坑网上的解决方法已经失效了,第二就是大家都懂得网络问题,这个只能多试试各种方法进行安装一些依赖,另外我使用小米手机报错‘Execution failed for task ':app:installDebug’时,在设置----更多设置----开发者选项----启用MIUI优化 关闭之后就可以解决,那么就可以正式开始我的react-native学习之旅。

Day1 完成一个计时器

首先按照官网进行脚手架创建项目: https://reactnative.dev/docs/environment-setup

如果你很熟悉react,那么react-native的语法应当不成问题,当然react-native在很多地方与web开发还是有很大的区别,例如入口可以用index.ios.js和index.android.js来区分安卓和ios的入口文件,还有ios和安卓的兼容问题,以及调用原生接口的问题等等,先把App.js本身的代码清干净,引入我们的第一天学习的文件代码

app.js

StyleSheet, useColorScheme, View 三个react-native的组件,分别对应api来看一下如何使用的。

StyleSheet 是类似于 CSS StyleSheets 的抽象,常用的就是const styles = StyleSheet.create({ fontSize: 30 }) ,使用时在对应的组件中写style={[ fontSize: 30 ]},在RN中样式布局和web中有一定的区别需要花费一定的时间进行学习。

useColorScheme自定义hooks,React钩子提供并订阅来自Appearance模块的配色方案更新。返回值表示当前用户首选的配色方案。该值可以稍后更新,既可以通过直接的用户操作(例如在设备设置中选择主题),也可以根据时间表(例如,遵循白天和黑夜循环的明暗主题)进行更新。

  • "light": The user prefers a light color theme.
  • "dark": The user prefers a dark color theme.
  • null: The user has not indicated a preferred color theme.

View是构建UI最基本的组件,它是一个容器,支持flexbox布局、样式、一些触摸处理和可访问控件。View直接映射到React native运行的平台上的原生视图,无论是UIView, <div>,android。视图,等等。类似web开发中的div,但是在react-native中布局主要是用flex布局,和web开发有一定的区别。

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 *
 * @format
 * @flow strict-local
 */

import React from 'react';
import {Node} from 'react';
import Day1 from './view/day1';

import { useColorScheme, View } from 'react-native';

import { Colors } from 'react-native/Libraries/NewAppScreen';

const App: () => Node = () => {
  const isDarkMode = useColorScheme() === 'dark';

  const backgroundStyle = {
    backgroundColor: isDarkMode ? Colors.darker : Colors.lighter,
  };

  return (
    <View style={backgroundStyle}>
      <Day1 />
    </View>
  );
};

export default App;

Day1.js

Platform, FlatList, StyleSheet, StatusBar, Text, TouchableHighlight 几个react-native的组件,分别对应api来看一下如何使用的。

Platform

Platform用于区分ios系统和Android系统,然后针对ios和android进行兼容。常用:

  1. 特定平台扩展名
React Native会检测某个文件是否具有.ios.或是.android.的扩展名,然后根据当前运行的平台加载正确对应的文件。
假设你的项目中有如下两个文件:
`BigButton.ios.js`
`BigButton.android.js`
这样命名组件后你就可以在其他组件中直接引用,而无需关心当前运行的平台是哪个。
`import BigButton from './components/BigButton';`
  1. 实用的方法是Platform.select()
var { Platform } = React;
var styles = StyleSheet.create({
  container: {
    flex: 1,
    ...Platform.select({
      ios: {
        backgroundColor: 'red',
      },
      android: {
        backgroundColor: 'blue',
      },
    }),
  },
});
//上面的代码会根据平台的不同返回不同的container样式——iOS上背景色为红色,而android为蓝色。
这一方法可以接受任何合法类型的参数,因此你也可以直接用它针对不同平台返回不同的组件,像下面这样:
var Component = Platform.select({
  ios: () => require('ComponentIOS'),
  android: () => require('ComponentAndroid'),
})();
<Component />
  1. 平台模块

React Native提供了一个检测当前运行平台的模块。如果组件只有一小部分代码需要依据平台定制,那么这个模块就可以派上用场。

import { Platform } from 'react-native';
var styles = StyleSheet.create({
  height: (Platform.OS === 'ios') ? 200 : 100,
});
//Platform.OS在iOS上会返回ios,而在Android设备或模拟器上则会返回android。
  1. 检测Android版本
//在Android上,平台模块还可以用来检测当前所运行的Android平台的版本:
import { Platform } from 'react-native';
if(Platform.Version === 21){
  console.log('Running on Lollipop!');
}

FlatList

StatusBar

  • 控制应用状态栏的组件。

reactNtive中文 StatusBar

Text

  • 一个用于显示文本的 React 组件,并且它也支持嵌套、样式,以及触摸处理。

    reactNative中文 Text

TouchableHighlight

  • 本组件用于封装视图,使其可以正确响应触摸操作。当按下的时候,封装的视图的不透明度会降低,同时会有一个底层的颜色透过而被用户看到,使得视图变暗或变亮。其实就是button

    reactNative中文 TouchableHighlight

/**
 * Day 1
 * A stop watch
 */
'use strict';

import React, {Component} from 'react';
import {Platform, FlatList, StyleSheet, StatusBar, Text, TouchableHighlight, View} from 'react-native';
import Util from './utils';
import PropTypes from 'prop-types';  // 由于react的propTypes早就被移除了 所以需要从外部引

class WatchFace extends Component {
  static propTypes = {
    sectionTime: PropTypes.string.isRequired,
    totalTime: PropTypes.string.isRequired,
  };

  render() {
    return (
      <View style={styles.watchFaceContainer}>
        <Text style={styles.sectionTime}>{this.props.sectionTime}</Text>
        <Text style={styles.totalTime}>{this.props.totalTime}</Text>
      </View>
    );
  }
}

class WatchControl extends Component {
  static propTypes = {
    stopWatch: PropTypes.func.isRequired,
    clearRecord: PropTypes.func.isRequired,
    startWatch: PropTypes.func.isRequired,
    addRecord: PropTypes.func.isRequired,
  };

  constructor(props) {
    super(props);
    this.state = {
      watchOn: false,
      startBtnText: '启动',
      startBtnColor: '#60B644',
      stopBtnText: '计次',
      underlayColor: '#fff',
    };
  }

  _startWatch() {
    if (!this.state.watchOn) {
      this.props.startWatch();
      this.setState({
        startBtnText: '停止',
        startBtnColor: '#ff0044',
        stopBtnText: '计次',
        underlayColor: '#eee',
        watchOn: true,
      });
    } else {
      this.props.stopWatch();
      this.setState({
        startBtnText: '启动',
        startBtnColor: '#60B644',
        stopBtnText: '复位',
        underlayColor: '#eee',
        watchOn: false,
      });
    }
  }

  _addRecord() {
    if (this.state.watchOn) {
      this.props.addRecord();
    } else {
      this.props.clearRecord();
      this.setState({
        stopBtnText: '计次',
      });
    }
  }

  render() {
    return (
      <View style={styles.watchControlContainer}>
        <View style={{flex: 1, alignItems: 'flex-start'}}>
          <TouchableHighlight
            style={styles.btnStop}
            underlayColor={this.state.underlayColor}
            onPress={() => this._addRecord()}>
            <Text style={styles.btnStopText}>{this.state.stopBtnText}</Text>
          </TouchableHighlight>
        </View>
        <View style={{flex: 1, alignItems: 'flex-end'}}>
          <TouchableHighlight style={styles.btnStart} underlayColor="#eee" onPress={() => this._startWatch()}>
            <Text style={[styles.btnStartText, {color: this.state.startBtnColor}]}>{this.state.startBtnText}</Text>
          </TouchableHighlight>
        </View>
      </View>
    );
  }
}

class WatchRecord extends Component {
  static propTypes = {
    record: PropTypes.array.isRequired,
  };

  constructor(props) {
    super(props);
    this.Item = ({item}) => {
      return (
        <View style={styles.recordItem}>
          <Text style={styles.recordItemTitle}>{item.title || ''}</Text>
          <View style={{alignItems: 'center'}}>
            <Text style={styles.recordItemTime}>{item.time || ''}</Text>
          </View>
        </View>
      );
    };
  }

  render() {
    return (
      <FlatList
        keyExtractor={(item) => `${item.title}-${item.time}`}
        style={styles.recordList}
        data={this.props.record}
        renderItem={this.Item}
      />
    );
  }
}

export default class extends Component {
  constructor() {
    super();
    this.state = {
      stopWatch: false,
      resetWatch: true,
      intialTime: 0,
      currentTime: 0,
      recordTime: 0,
      timeAccumulation: 0,
      totalTime: '00:00.00',
      sectionTime: '00:00.00',
      recordCounter: 0,
      record: [],
    };
  }

  componentWillUnmount() {
    this._stopWatch();
    this._clearRecord();
  }

  componentDidMount() {
    if (Platform.OS === 'ios') {
      StatusBar.setBarStyle(0);
    }
  }

  _startWatch() {
    if (this.state.resetWatch) {
      this.setState({
        stopWatch: false,
        resetWatch: false,
        timeAccumulation: 0,
        initialTime: new Date().getTime(),
      });
    } else {
      this.setState({
        stopWatch: false,
        initialTime: new Date().getTime(),
      });
    }
    let milSecond, second, minute, countingTime, secmilSecond, secsecond, secminute, seccountingTime;
    let interval = setInterval(() => {
      this.setState({
        currentTime: new Date().getTime(),
      });
      countingTime = this.state.timeAccumulation + this.state.currentTime - this.state.initialTime;
      minute = Math.floor(countingTime / (60 * 1000));
      second = Math.floor((countingTime - 6000 * minute) / 1000);
      milSecond = Math.floor((countingTime % 1000) / 10);
      seccountingTime = countingTime - this.state.recordTime;
      secminute = Math.floor(seccountingTime / (60 * 1000));
      secsecond = Math.floor((seccountingTime - 6000 * secminute) / 1000);
      secmilSecond = Math.floor((seccountingTime % 1000) / 10);
      this.setState({
        totalTime:
          (minute < 10 ? '0' + minute : minute) +
          ':' +
          (second < 10 ? '0' + second : second) +
          '.' +
          (milSecond < 10 ? '0' + milSecond : milSecond),
        sectionTime:
          (secminute < 10 ? '0' + secminute : secminute) +
          ':' +
          (secsecond < 10 ? '0' + secsecond : secsecond) +
          '.' +
          (secmilSecond < 10 ? '0' + secmilSecond : secmilSecond),
      });
      if (this.state.stopWatch) {
        this.setState({
          timeAccumulation: countingTime,
        });
        clearInterval(interval);
      }
    }, 10);
  }

  _stopWatch() {
    this.setState({
      stopWatch: true,
    });
  }

  _addRecord() {
    let {recordCounter, record} = this.state;
    recordCounter++;
    if (recordCounter > 5) {
      record.pop();
    }
    record.unshift({title: '计次' + recordCounter, time: this.state.sectionTime});
    this.setState({
      recordTime: this.state.timeAccumulation + this.state.currentTime - this.state.initialTime,
      recordCounter: recordCounter,
      record: record,
    });
    //use refs to call functions within other sub component
    //can force to update the states
    // this.refs.record._updateData();
  }

  _clearRecord() {
    this.setState({
      stopWatch: false,
      resetWatch: true,
      intialTime: 0,
      currentTime: 0,
      recordTime: 0,
      timeAccumulation: 0,
      totalTime: '00:00.00',
      sectionTime: '00:00.00',
      recordCounter: 0,
      record: [],
    });
  }

  render() {
    return (
      <View style={styles.watchContainer}>
        <WatchFace totalTime={this.state.totalTime} sectionTime={this.state.sectionTime} />
        <WatchControl
          addRecord={() => this._addRecord()}
          clearRecord={() => this._clearRecord()}
          startWatch={() => this._startWatch()}
          stopWatch={() => this._stopWatch()}
        />
        <WatchRecord record={this.state.record} />
      </View>
    );
  }
}

const styles = StyleSheet.create({
  watchContainer: {
    alignItems: 'center',
    backgroundColor: '#f3f3f3',
    marginTop: 60,
  },
  watchFaceContainer: {
    width: Util.size.width,
    paddingTop: 50,
    paddingLeft: 30,
    paddingRight: 30,
    paddingBottom: 40,
    backgroundColor: '#fff',
    borderBottomWidth: 1,
    borderBottomColor: '#ddd',
    height: 170,
  },
  sectionTime: {
    fontSize: 20,
    fontWeight: '100',
    paddingRight: 30,
    color: '#555',
    position: 'absolute',
    left: Util.size.width - 140,
    top: 30,
  },
  totalTime: {
    fontSize: Util.size.width === 375 ? 70 : 60,
    fontWeight: '100',
    color: '#222',
    paddingLeft: 20,
  },
  watchControlContainer: {
    width: Util.size.width,
    height: 100,
    flexDirection: 'row',
    backgroundColor: '#f3f3f3',
    paddingTop: 30,
    paddingLeft: 60,
    paddingRight: 60,
    paddingBottom: 0,
  },
  btnStart: {
    width: 70,
    height: 70,
    borderRadius: 35,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
  btnStop: {
    width: 70,
    height: 70,
    borderRadius: 35,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
  btnStartText: {
    fontSize: 14,
    backgroundColor: 'transparent',
  },
  btnStopText: {
    fontSize: 14,
    backgroundColor: 'transparent',
    color: '#555',
  },
  recordList: {
    width: Util.size.width,
    height: Util.size.height - 300,
    paddingLeft: 15,
  },
  recordItem: {
    height: 40,
    borderBottomWidth: Util.pixel,
    borderBottomColor: '#bbb',
    paddingTop: 5,
    paddingLeft: 10,
    paddingRight: 10,
    paddingBottom: 5,
    flexDirection: 'row',
    alignItems: 'center',
  },
  recordItemTitle: {
    backgroundColor: 'transparent',
    flex: 1,
    textAlign: 'left',
    paddingLeft: 20,
    color: '#777',
  },
  recordItemTime: {
    backgroundColor: 'transparent',
    flex: 1,
    textAlign: 'right',
    paddingRight: 20,
    color: '#222',
  },
});

效果

在这里插入图片描述

小结

  • 重点要熟悉Native和web布局的区别,学习如何使用Native布局达到想要的效果。
  • 熟悉了一些Native自带的组件的使用方法,总结这个太麻烦了,后面的总结中不再总结组件使用,直接官网阅读即可。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

MaxLoongLvs

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值