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进行兼容。常用:
- 特定平台扩展名
React Native会检测某个文件是否具有.ios.或是.android.的扩展名,然后根据当前运行的平台加载正确对应的文件。
假设你的项目中有如下两个文件:
`BigButton.ios.js`
`BigButton.android.js`
这样命名组件后你就可以在其他组件中直接引用,而无需关心当前运行的平台是哪个。
`import BigButton from './components/BigButton';`
- 实用的方法是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 />
- 平台模块
React Native提供了一个检测当前运行平台的模块。如果组件只有一小部分代码需要依据平台定制,那么这个模块就可以派上用场。
import { Platform } from 'react-native';
var styles = StyleSheet.create({
height: (Platform.OS === 'ios') ? 200 : 100,
});
//Platform.OS在iOS上会返回ios,而在Android设备或模拟器上则会返回android。
- 检测Android版本
//在Android上,平台模块还可以用来检测当前所运行的Android平台的版本:
import { Platform } from 'react-native';
if(Platform.Version === 21){
console.log('Running on Lollipop!');
}
FlatList
-
用于展示列表,同时兼容多个平台,并且可以支持选择点击高亮。
StatusBar
- 控制应用状态栏的组件。
Text
-
一个用于显示文本的 React 组件,并且它也支持嵌套、样式,以及触摸处理。
TouchableHighlight
-
本组件用于封装视图,使其可以正确响应触摸操作。当按下的时候,封装的视图的不透明度会降低,同时会有一个底层的颜色透过而被用户看到,使得视图变暗或变亮。其实就是button
/**
* 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自带的组件的使用方法,总结这个太麻烦了,后面的总结中不再总结组件使用,直接官网阅读即可。