接着上次的环境搭建完成之后, 我们就可以来写写简单的 RN 代码了, 这次的 Demo 中包括 RN 的布局, props, state 等一些东西, 记录一下使用过程
首先来看一下效果:
大概就是这样的一个卡片效果
首先, 新建一个工程, 把 index.ios.js
中自带的内容都删掉, 只剩一个 View 然后我们在建一个 Card 的 Class 然后把这个 Card 添加就可以了, 实现如下:
class Card extends Component {
render() {
return (
<View>
</View>
);
}
}
export default class Rn_card extends Component {
render() {
return (
// 内部样式: flex = 1 撑满整个空间 即这个底层的 View 是屏幕的大小
<View style=>
<Card>
</Card>
</View>
);
}
}
RN 的布局方式和 CSS 布局的方式基本一致, 上手不是太困难, 我们先来把主要的背景写出来, 随便写几个值就好:
class Card extends Component {
render() {
return (
<View style={styles.mainContainer}>
</View>
);
}
}
const styles = StyleSheet.create({
mainContainer: {
height: 200,
backgroundColor: 'skyblue',
marginTop: 20,
}
});
效果:
恩, 好的这样就有一个背景了
其实上面的效果图片我们可以先把他拆成两部分, 即头像和个人信息为一部分, 下面的个人简介为一部分, 所以最底部的 View 定下来了以后, 我们把这两部分写出来:
class Card extends Component {
render() {
return (
<View style={styles.mainContainer}>
<View style={styles.cardInfoContainer}>
</View>
<View style={styles.cardDecContainer}>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
mainContainer: {
height: 200,
backgroundColor: 'skyblue',
marginTop: 20,
},
cardInfoContainer: {
flex: 4,
backgroundColor: 'red',
marginLeft:15,
marginRight:15
},
cardDecContainer: {
flex: 3,
backgroundColor: 'yellow',
marginLeft:15,
marginRight:15
}
});
样式中的属性都很直观, 至于这个 flex 指的是一个弹性宽高, 在其父视图有一定的大小的前提下(如果这个视图有一定的 width 或 height 或 flex 否则无效果), flex 可以指定一个子视图占用父视图的空间的百分比, 并且这个 flex 的值只能为整数, 现在我们看到的效果大概是这样的:
从这个图中我们可以了解到, RN 默认的布局方式是采用纵向布局的, 现在我们要在红色的部分写一个头像和一些跟人信息, 同样把个人信息和头像拆分成两部分, 然后红色 View 采用横向布局 个人信息的三列采用纵向布局
class Card extends Component {
render() {
return (
<View style={styles.mainContainer}>
<View style={styles.cardInfoContainer}>
<Image style={styles.avatar}>
</Image>
<View style={styles.cardPersonInfo}>
<Text style={styles.cardName}>
</Text>
<Text style={styles.cardGenderAndAge}>
</Text>
<Text style={styles.cardGenderAndAge}>
</Text>
</View>
</View>
<View style={styles.cardDecContainer}>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
mainContainer: {
height: 200,
backgroundColor: 'skyblue',
marginTop: 20,
},
cardInfoContainer: {
flex: 4,
backgroundColor: 'red',
marginLeft:15,
marginRight:15,
// 采用横向布局 (纵向布局 'column')
flexDirection: 'row'
},
cardDecContainer: {
flex: 3,
backgroundColor: 'yellow',
marginLeft:15,
marginRight:15
},
avatar: {
width: 80,
height: 80,
marginTop: 15,
backgroundColor: 'gray'
},
cardPersonInfo: {
// 这里有个坑, 前面头像的大小是固定的, 如果这里不用 flex = 1 填满剩下的空间或者不写绝对大小的话 这个视图不会显示
flex: 1,
marginTop: 15,
height: 80,
marginLeft: 10,
backgroundColor: 'orange'
},
cardName: {
marginTop: 0,
height: 20,
backgroundColor: 'green'
},
cardGenderAndAge: {
marginTop: 10,
height: 20,
backgroundColor: 'green'
}
});
如果直接运行的话是会报错的, 因为没有导入 Image 的包, 所以我们在头部导入一下:
import React, {Component} from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
Image // 导入 Image
} from 'react-native';
好了, 现在我们的布局基本上就写好了, 效果如图:
恩, 看起来很吃藕, 接下来我们搞一些文字上去并把这些背景颜色都去掉:
class Card extends Component {
render() {
return (
<View style={styles.mainContainer}>
<View style={styles.cardInfoContainer}>
<Image style={styles.avatar}>
</Image>
<View style={styles.cardPersonInfo}>
<Text style={styles.cardName}>
姓名:
</Text>
<Text style={styles.cardGenderAndAge}>
性别:
</Text>
<Text style={styles.cardGenderAndAge}>
年龄:
</Text>
</View>
</View>
<View style={styles.cardDecContainer}>
<Text>
简介:
</Text>
</View>
</View>
);
}
}
效果:
其实我们可以直接把名字之类的写上去, 但是实际开发中, 这些数据都是活的, 这时候据需要我们来定义一些 props(属性) 也就是参数, 比如这样:
class Card extends Component {
render() {
return (
<View style={styles.mainContainer}>
<View style={styles.cardInfoContainer}>
<Image style={styles.avatar} source=>
</Image>
<View style={styles.cardPersonInfo}>
<Text style={styles.cardName}>
姓名: {this.props.card['name']}
</Text>
<Text style={styles.cardGenderAndAge}>
性别: {this.props.card['gender']}
</Text>
<Text style={styles.cardGenderAndAge}>
年龄: {this.props.card['age']}
</Text>
</View>
</View>
<View style={styles.cardDecContainer}>
<Text>
简介: {this.props.card['dec']}
</Text>
</View>
</View>
);
}
}
这里相当于定义了一个 card 对象, 这个 card 对象里面有一些键值对, 我们只需要从外部传入一个 card 对象, 这个组件就能显示完整的信息了, 比如:
export default class Rn_card extends Component {
render() {
// 图片为 http 的链接的话不要忘了在 info.plist 里面设置 NSAllowsArbitraryLoads 为 true
let data = {
avatar: 'https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=1691037026,968218084&fm=23&gp=0.jpg',
name: '赵日天',
gender: '男',
age: '12',
dec: '我赵日天第一个不服',
};
return (
<View style=>
<Card card={data}>
</Card>
</View>
);
}
}
这样我们就能看到理想的效果啦:
但是这并没有完全结束, 因为我们平时的数据都是从网上请求下来的, 所以这些数据都是异步的, 不能直接赋值给 View 那么如何在得到数据之后再次给 View 赋值呢? 这个时候我们就需要用到__state(状态)__
每个 Class 都有一个 constructor 的构造方法, 它可以直接打出来
// 构造
constructor(props) {
super(props);
// 初始状态
this.state = {};
}
state的特点是: state中的数据是可变的, 一个类中一旦调用了 this.setState() 那么就会引起 render 的重新渲染, 来达到更新页面的目的
我们可以用 setTimeOut() 来模仿网络请求, 来写一个延迟三秒改变名片的功能:
export default class Rn_card extends Component {
// 构造
constructor(props) {
super(props);
// 初始状态
this.state = {
data: { // 初始化数据
avatar: 'https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=1691037026,968218084&fm=23&gp=0.jpg',
name: '赵日天',
gender: '男',
age: '12',
dec: '我赵日天第一个不服',
}
};
// 在ES6中,如果在自定义的函数里使用了 this 关键字,则需要对其进行“绑定”操作,否则 this 的指向不对
// 像下面这行代码一样,在 constructor 中使用 bind 是其中一种做法(还有一些其他做法,如使用箭头函数等)
this.loadData = this.loadData.bind(this);
}
// 已经加载虚拟 DOM 整个生命周期只会运行一次 通常用于完成异步网络请求
componentDidMount() {
this.loadData();
}
loadData() {
// 模仿网络请求
setTimeout(() => { // 函数必须用 ES6 的写法 否则在写 this.setState 时会提示没有定义的方法
let newData = { // 模仿网络请求得到的新对象
avatar: 'https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=1691037026,968218084&fm=23&gp=0.jpg',
name: '叶良辰',
gender: '男',
age: '12',
dec: '良辰有一万种方法让你待不下去',
};
this.setState({ // 注意 为了保证this在调用时仍然指向当前组件 需要在 constructor 中做绑定操作
data: newData // 赋值 新的对象
});
}, 3000) // 延迟三秒
}
render() {
return (
<View style=>
// 接收 state 中的 data
<Card card={this.state.data}>
</Card>
</View>
);
}
}
这样, 三秒之后, 我们就会看到变化了: