背景
世界杯期间开始了从安卓开发转到了React-Native开发的学习,现在记录一下学习过程。基本上都是参考React-Native的中文文档,链接如下:
https://reactnative.cn/docs/0.31/getting-started.html#content
我的开发环境是mac,并且之前已经安装了homeBrew、Android Studio,并且已然把AS的sdk目录添加到了环境变量$PATH中
搭建环境
1、安装node
brew install node
2、设置npm镜像
npm config set registry https://registry.npm.taobao.org --global npm config set disturl https://npm.taobao.org/dist --global
3、安装react-native-cli
npm install -g react-native-cli
4、安装watchman、flow
brew install watchman brew install flow
5、新建目录,初始化项目
react-native init StudyOfRNListView
StudyOfRNListView是个项目名,随便取就行,这条命令执行完会在当前目录下,生成一个StudyOfRNListView文件夹,里面就是RN项目框架
这个过程可能很慢,不愿意等的可以直接从网上把别人的项目拷下来,把里面的逻辑代码都删掉
6、在StudyOfRNListView/android根目录下,新建local.properties文件,把sdk目录填进去,示例如下
sdk.dir=/Users/songzeceng/Library/Android/sdk
7、运行项目
这时要保证一个android手机或avd已经连接
cd StudyOfRNListView react-native run-android
不出意外的话,应用就已经安装好了,点击就能运行
修改App.js
打开StudyOfRNListView目录,目录结构如下
主界面是index.js,内容如下
import { AppRegistry } from 'react-native'; import App from './App'; AppRegistry.registerComponent('StudyOfRNListView', () => App);
第一行从react-native里导入了AppRegistry,用{}扩起来是为了在后面可以作为一个对象使用
第二行从当前目录下的App导入了App(其实就是复制粘贴,但没有{}扩住,就不能调用里面的东西)
第三行是注册组件,组件名是App,然后把它作为StudyOfRNListView的入口类
而后我们就打开App.js,把里面的内容删到只剩下
import React, {Component} from 'react'; import { AppRegistry, Text } from 'react-native'; class StudyOfRNListView extends Component { render() { // render()方法用来渲染UI } }
这样,应用就只剩下一个白板,我们开始正儿八经学习rn,而主要的改动,就是在这个App.js里面,以StudyOfRNListView为中心进行的
每修改一些东西,晃一晃手机,就会显示菜单,选择reload就可以刷新界面(avd的话,直接在键盘上双击r就可以刷新)
如果reload报错,说连不上服务器,可以先登录这个网站看看出错原因:
http://localhost:8081/index.android.bundle?platform=android
如果显示是找不到index.android.js,解决方法可以参考这篇文章,也就是重启服务器并清空缓存,然后在另一个命令行下,启动项目
显示图片
首先要从react-native中导入Image
import React, {Component} from 'react'; import { AppRegistry, Text, Image } from 'react-native';
然后把StudyOfRNListView改成下面的样子
export default class App extends Component<Props> { render() { let pic = { uri: "https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=746951351,3068781139&fm=200&gp=0.jpg" // 注意是uri,不是url,这里赋值用: } return ( <View> <Image source={pic} style={{width:200, height:200}}></Image> // source指定数据源,style用来指定样式 </View> ); } }效果如下
自定义模块
rn支持我们自定义模块类,方才说的StudyOfRNListView相当于一个主模块类
定义一个城市名的模块类,可以是这样:
class City extends Component { // 模块类必须继承自Component render() { // 至少要有一个render方法,这个方法用来渲染UI return ( // return的就是我们想要渲染的东西 <Text>{this.props.name}</Text> // <Text>用来显示文字,开始标签和结束标签之间的内容表示显示的是props.name,这个我们可以在使用的时候指定 ); } }
这里用到了属性props,这个东西一经设定就不能再改了,相应的可以改动的是状态state.
然后我们可以在主模块类StudyOfRNListView中使用
export default class App extends Component<Props> { render() { let pic = { uri: "https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=746951351,3068781139&fm=200&gp=0.jpg" } return ( <View> <Image source={pic} style={{width: 200, height: 200}}></Image> <City name={"安阳"}/> <City name={"新乡"}/> <City name={"洛阳"}/> </View> ); } }
效果如下
state的使用-定时闪烁
属性一经设定不能再变,但状态可以。
新建一个组件Blink,添加构造方法和render方法如下
class Blink extends Component { constructor(props) { super(props); // 必须先调用父类构造方法 this.state = {showText: true}; // 设定初始化state setInterval(() => { // 设置间隔的函数 this.setState({showText: !this.state.showText}); // 参数1:回调函数,内容:setState()用来设置state,把showText取反 }, 1000); // 参数2:间隔时间 } render() { let display = this.state.showText ? "黄埃散漫风萧索,云栈萦纡登剑阁" : ""; // 根据state里的showText决定显示内容 return ( <Text>{display}</Text> // 显示display ); } }
在主模块调用
export default class App extends Component<Props> { render() { let pic = { uri: "https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=746951351,3068781139&fm=200&gp=0.jpg" } return ( <View> <Image source={pic} style={{width: 200, height: 200}}></Image> // rn里的尺寸都是无单位的逻辑像素点,和设备无关 <City name={"安阳"}/> <City name={"新乡"}/> <City name={"洛阳"}/> <Blink/> // 调用Blink </View> ); } }
效果
样式
我们可以向css那样自定义样式,调用的是StyleSheet.create()方法,所以要事先从'react-native'中导入StyleSheet
import { AppRegistry, StyleSheet, Text, View } from 'react-native';
然后就可以自定义样式了,格式是json格式
const styles = StyleSheet.create({ bigBlue: { color: "blue", fontWeight: "bold", fontSize: 30 }, red: { color: "red", } });
样式在使用的时候,可以以数组的形式传入,在以数组的形式传入时,后面元素会覆盖前面的元素(当然只有相同类型的元素才会覆盖)
先在主模块里调用:
export default class App extends Component<Props> { render() { return ( <View> <Text style={styles.bigBlue}>蓝色大号</Text> <Text style={styles.red}>红色</Text> <Text style={[styles.bigBlue, styles.red]}>蓝色大号,红色</Text> // 数组形式,先传入bigBlue,再传入red <Text style={[styles.red, styles.bigBlue]}>红色,蓝色大号</Text> // 数组形式,先传入red,再传入bigBlue </View> ); } }
效果
flexBox
flexBox用来指定子结点的展示形式,以根结点为例,用法如下
class StudyOfRNListView extends Component { render() { // render()方法用来渲染UI let pic = { uri: 'https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=746951351,3068781139&fm=200&gp=0.jpg' }; return ( // return的内容就是渲染的内容 <View style={{ alignItems: 'center', flex: 1, // flex是权重 flexDirection: 'row', // flexDirection是排列方向,排成一列column或者排成一排row flexWrap: 'wrap', // flexWrap:内容包裹形式。nowrap:不换行,此为默认 wrap:换行,wrap-reverse:换行,第一行在下面 justifyContent: 'space-around' // justifyContent意为内容的分布方式,可选项有:flex-start、center、flex-end、space-around、space-between // 以横向排列为例,意思依次是左对齐,居中,右对齐,每个子项两侧间隔相等(所以不会靠两侧),每个子项之间的间隔相等(所以会靠两侧) }}> <Image source={pic} style={{width: 200, height: 200}}/> <City name='安阳' fontSize={50}/> <City name='新乡' fontSize={30}/> <City name='洛阳' fontSize={20}/> <Text style={styles.red}>红色</Text> <Text style={styles.bigBlue}>蓝色大号</Text> <Text style={[styles.red, styles.bigBlue]}>红色、蓝色大号</Text> <Text style={[styles.bigBlue, styles.red]}>蓝色大号、红色</Text> </View> ); } }
效果如下
处理输入
正如Android里面的EditText一样,RN里面专门用来让用户输入的组件叫做TextInput,里面可以设置默认文字,事件处理等。
自定义一个组件,用来显示用户输入的内容
class CityInput extends Component { constructor(props) { super(props); this.state = {text: ""}; // 默认text是空 } render() { return ( <View> <TextInput style={{height: 40}} placeholder={"输入你要去的城市"} // 占位字符串 // onChangeText={ // 输入内容有变时调用 // (text) => this.setState({text}) // } onSubmitEditing={ // 按下软键盘上的确认键后调用,参数是event (event) => this.setState({text:event.nativeEvent.text}) // 通过调用event.nativeEvent.text获取输入的字符串 } ></TextInput> <Text style={{padding: 10, fontSize: 30}}> { this.state.text.split('-').map( // split函数:分割,map函数:转换 (str) => "那就去" + str ).join('\n') // 字符串拼接 } </Text> </View> ); } }
主模块中调用
export default class App extends Component<Props> { render() { return ( <View style={{ alignItems: 'center', flex: 1, // flex是权重 flexDirection: 'row', // flexDirection是排列方向,排成一列column或者排成一排row flexWrap: 'wrap', // flexWrap:内容包裹形式。nowrap:不换行,此为默认 wrap:换行,wrap-reverse:换行,第一行在下面 justifyContent: 'space-around' // justifyContent意为内容的分布方式,可选项有:flex-start、center、flex-end、space-around、space-between // 以横向排列为例,意思依次是左对齐,居中,右对齐,每个子项两侧间隔相等(所以不会靠两侧),每个子项之间的间隔相等(所以会靠两侧) }}> <CityInput/> </View> ); } }
效果
结语
这是rn最基本的使用,下面我会记录rn中ListView及其他东西的用法