今天是重阳节,祝大家节日快乐,今天继续更新RN相关的博客。上篇博客《ReactNative之从HelloWorld中看环境搭建、组件封装、Props及State》中我们通过一个HelloWorld的一个示例介绍了RN的环境搭建、组件封装、Props以及States。经过这么多天,今天我们继续来看RN的东西,本篇博客是关于RN的Flex布局的,也就是说是关于RN中控件放哪儿的一篇博客。在RN中使用的是Flex布局,如果你之前接触过Web前端的话对FlexBox布局并不陌生,但是如果你之前没做过Web开发的话,也不影响看今天的博客。本篇博客也是RN开发的基础,算是比较重要的。
RN中控件的布局方式与Web前端开发中的div+css的盒式布局是极为相似的。本篇博客就来详细的讲解一下RN中的FlexBox布局,中文名“弹性布局”。RN中的FlexBox布局和CSS中的FlexBox大体相同,也是通过一些属性来控制控件的位置、大小以及各个控件之间的关系。在FlexBox中分为 容器属性(flexDirection、flexWrap、alignItems、justifyContent、alignContent)和 元素属性(flex、alignSelf、margin、padding、border、width、height等等)。顾名思义,容器属性是用来添加到 父组件上来控制子组件的位置的属性,而 元素属性则是添加到子组件本身控制本身的一种属性。稍后会详细介绍。
接下来我们将根据具代码来详细的介绍常用的几种FlexBox布局的属性,。根据常用性,下方会依次介绍RN中的Flex布局中的flex、flexDirection、justifyContent、alignContent、flexWrap、AlignItem、AlignSelf这些常用的属性。本篇博客所涉及的Demo会上传到博客最后方的github链接中。
先入为主,下方这个gif中所有的内容就是我们今天要结束的东西,全是关于Flex布局的。
一、Flex
首先我们先来看一下flex的使用方式,flex属性接收的是一个number类型的值, 该值表示弹性布局的比例系数。具体的我们还要看一下下方关于Flex的一个Demo。
下方就是flex的具体使用方式,其中的flexValue是一个number类型的值。
<View style={{ flex: flexValue />
接下来我们来看一下flex具体的一个示例。我们通过点击来修改中间的flex的值来观察flex对每个view的影响:
最后我们来简单的看一下该效果的实现,代码如下。
下方是上述示例的完整代码:
1 // flex
2 import { Component } from "react";
3 import { TouchableOpacity, View, Text } from "react-native";
4 import React from "react";
5
6 type FlexStateType = {
7 flexValue: number
8 }
9 export default class FlexTestComponent extends Component<null, FlexStateType> {
10 flexValue = 1;
11 constructor(props) {
12 super(props);
13 this.state = {
14 flexValue: this.flexValue
15 };
16 }
17
18 clickView = () => {
19 this.flexValue ++;
20 this.setState({flexValue: this.flexValue})
21 };
22
23 item = (flexValue: number) => {
24 return (
25 <View style={{ flex: flexValue, height: 50, backgroundColor: 'black', marginLeft:10, marginRight: 10 , justifyContent: 'center', alignItems:'center'}}>
26 <Text style = {{color: '#fff'}}>flex = {flexValue}</Text>
27 </View>
28 );
29 };
30
31 render () {
32 const {
33 flexValue
34 } = this.state;
35 return (
36 <TouchableOpacity onPress={this.clickView}>
37 <View style={{ height: 60, width: '100%', backgroundColor: '#e5e5e5', flexDirection: 'row' , alignItems: 'center'}}>
38 {this.item(1)}
39 {this.item(flexValue)}
40 {this.item(1)}
41 </View>
42 </TouchableOpacity>
43 );
44 }
45 }
二、FlexDirection
看完flex属性,接下来我们来看一下flexDirection属性。该属性在FlexBox布局中也是一个尤为重要而且比较常用的一个属性。flexDirection主要是用来控制子元素的布局方向的,主要分为横向布局和纵向布局,默认是纵向布局(column)。下方是flexDirection的属性值和使用方式。
属性值: flexDirection?: "row" | "column" | "row-reverse" | "column-reverse"; 用法示例: <View style={{ flexDirection: 'row' />
flexDirection的属性值主要有以下几个:
因该部分的demo对应的代码比较简单,介绍如下:
完整代码示例:
1 // flexDirection
2 import React, { Component } from 'react'
3 import { FlexStyle, StyleSheet, Text, View } from 'react-native'
4
5 export default function FlexDirectionTestComponent () {
6 return (
7 <View style={{ height: 180, backgroundColor: '#c1c1c1' , flexDirection: 'row'}}>
8 <View style={{ height: '100%', width: '50%', justifyContent: 'space-around'}}>
9 <FlexDirectionTestView value={{ flexDirection: 'row' }}/>
10 <FlexDirectionTestView value={{ flexDirection: 'row-reverse' }}/>
11 </View>
12
13 <View style={{ height: '100%', width: '50%', flexDirection: 'row' , justifyContent: 'space-around'}}>
14 <FlexDirectionTestView value={{ flexDirection: 'column' }}/>
15 <FlexDirectionTestView value={{ flexDirection: 'column-reverse' }}/>
16 </View>
17 </View>
18 )
19 }
20
21 type FlexDirectionProps = {
22 value?: FlexStyle
23 }
24
25 class FlexDirectionTestView extends Component<FlexDirectionProps> {
26 render () {
27 return (
28 <View style={[myStyle.flexDirectionProps, { flexDirection: this.props.value.flexDirection }]}>
29 <SubView value={'1'}/>
30 <SubView value={'2'}/>
31 <SubView value={'3'}/>
32 </View>
33 )
34 }
35 }
36
37 type SubViewProps = {
38 value: string
39 }
40 class SubView extends Component<SubViewProps> {
41 render () {
42 return(
43 <View style={myStyle.subViewStyle}>
44 <Text style={{ color: 'white', fontSize: 17 }}> {this.props.value} </Text>
45 </View>
46 )
47 }
48 }
49
50 const myStyle = StyleSheet.create({
51 subViewStyle: {
52 margin: 10,
53 borderRadius: 25,
54 width: 25,
55 height: 25,
56 backgroundColor: 'red',
57 justifyContent: 'center',
58 alignItems: 'center'
59 },
60 flexDirectionProps: {
61 backgroundColor: 'gray',
62 margin: 5
63 }
64 });
三、JustifyContent
今天这篇博客的干货还是比较足的,接下来我们来看一下第三个比较重要的属性justifyContent。该属性也是比较常用的,通常被用来控制子元素的左右方向的布局,这个与上面的flexDirection不同,justifyContent会控制整体子元素左右方向上的一个约束关系。下方是justifyContent的属性值和使用方式
属性值: justifyContent?: "flex-start" | "flex-end" | "center" | "space-between" | "space-around" | "space-evenly" 用法示例: <View style={{ justifyContent: 'flex-start' />
具体的还得看下方这个GIF图,该图中就列举了justifyContent所有的属性值,并且展示了每个属性值的不同表现形式,接下来详细的介绍一下每个属性值的作用。
介绍完上述属性,我们来简单的看一下该示例的实现代码,从上述操作来看本部分的Demo会相对复杂一些。首先来看一下上述按钮区域对应的代码片段:
看完按钮区域的代码,接下来我们就来看一下布局区域的代码:
完整代码如下:
1 // justifyContent
2 import { Component } from "react";
3 import { Text, TouchableOpacity, View } from "react-native";
4 import React from "react";
5
6 type JustifyContentType = "flex-start" | "flex-end" | "center" | "space-between" | "space-around" | "space-evenly";
7 type JustifyContentCompontStateType = {
8 justifyContentValue: JustifyContentType
9 }
10 export default class JustifyContentTestComponent extends Component<null, JustifyContentCompontStateType> {
11 constructor(props) {
12 super(props);
13 this.state = {
14 justifyContentValue: 'flex-start'
15 };
16 }
17
18 clickView = (value: JustifyContentType) => () => {
19 this.setState({ justifyContentValue: value});
20 };
21
22 customButton = (title: JustifyContentType) => {
23 return (
24 <TouchableOpacity onPress={this.clickView(title)}>
25 <View style = {{ width: 120, height: 30, backgroundColor: 'green', margin: 5, justifyContent:'center', alignItems:'center'}}>
26 <Text style={{color: '#fff', fontSize: 17}}>{title}</Text>
27 </View>
28 </TouchableOpacity>
29 );
30 };
31
32 operaView = () => {
33 return (
34 <View style={{
35 height: 90,
36 width: '100%',
37 justifyContent: 'center',
38 alignItems: 'center',
39 flexDirection:'row',
40 flexWrap:'wrap'}}>
41 {this.customButton('flex-start')}
42 {this.customButton('flex-end')}
43 {this.customButton('center')}
44 {this.customButton('space-between')}
45 {this.customButton('space-around')}
46 {this.customButton('space-evenly')}
47 </View>
48 );
49 };
50
51 item = (width: number) => {
52 return (
53 <View style = {{ height: 30, width: width, backgroundColor: 'green' , margin: 2}}/>
54 );
55 };
56
57 resultDisplayView = () => {
58 const {
59 justifyContentValue
60 } = this.state;
61 return (
62 <View style={{ height: 110, width: '100%', justifyContent: justifyContentValue, flexDirection: 'row', flexWrap:'wrap' }}>
63 {this.item(60)}
64 {this.item(100)}
65 {this.item(30)}
66 {this.item(80)}
67 {this.item(100)}
68 {this.item(90)}
69 {this.item(30)}
70 {this.item(80)}
71 </View>
72 );
73 };
74
75 render () {
76 return (
77 <View style={{ height: 200, backgroundColor: '#e5e5e5' }}>
78 {this.operaView()}
79 {this.resultDisplayView()}
80 </View>
81 );
82 }
83 }
四、AlignContent
接下来来看一下AlignContent这个属性及其相关的属性值。该属性与上面的JustifyContent属性的功能差不多,JustifyContent负责左右方向的子元素之间的关系,而AlignContent则负责上下方向上的子元素之间的布局。下方是AlignContent的相关属性值和使用方式:
属性值: alignContent?:"flex-start" | "flex-end" | "center" | "stretch" | "space-between" | "space-around" 用法示例: <View style={{ alignContent: 'flex-start' />
按照上述的思路,我们还是通过一个Demo来看一下每个属性值的具体的作用。下方就是本部分对应的Demo,每个按钮对应着AlignContent的一个属性值,点击相关按钮后,下方的子元素就会按照点击的按钮进行设置。下方是具体介绍:
代码和之前的Demo的实现思路差不多,在此就不做过多赘述了,下方是该部分的完整示例:
1 // alignItem
2 import { FlexAlignType, Text, TouchableOpacity, View } from "react-native";
3 import { Component } from "react";
4 import React from "react";
5
6 type AlignContentType = "flex-start" | "flex-end" | "center" | "stretch" | "space-between" | "space-around";
7
8 type AlignContentTestCompontStateType = {
9 alignContentValue: AlignContentType
10 }
11 export default class AlignContentTestComponent extends Component<null, AlignContentTestCompontStateType> {
12 constructor(props) {
13 super(props);
14 this.state = {
15 alignContentValue: 'flex-start'
16 };
17 }
18
19 clickView = (title: AlignContentType) => () => {
20 this.setState({ alignContentValue: title});
21 };
22
23 customButton = (title: AlignContentType) => {
24 return (
25 <TouchableOpacity onPress={this.clickView(title)}>
26 <View style = {{ width: 120, height: 30, backgroundColor: 'green', margin: 5, justifyContent:'center', alignItems:'center'}}>
27 <Text style={{color: '#fff', fontSize: 17}}>{title}</Text>
28 </View>
29 </TouchableOpacity>
30 );
31 };
32
33 operaView = () => {
34 return (
35 <View style={{
36 height: '40%',
37 width: '100%',
38 justifyContent: 'center',
39 alignItems: 'center',
40 flexDirection:'row',
41 flexWrap:'wrap'}}>
42 {this.customButton('flex-start')}
43 {this.customButton('center')}
44 {this.customButton('flex-end')}
45 {this.customButton('space-between')}
46 {this.customButton('space-around')}
47 {this.customButton('stretch')}
48 </View>
49 );
50 };
51
52 item = (width: number, height: number) => {
53 return (
54 <View style = {{ height: height, width: width, backgroundColor: 'red' , margin: 2}}/>
55 );
56 };
57
58 resultDisplayView = () => {
59 const {
60 alignContentValue
61 } = this.state;
62
63 let height = 30;
64 if (alignContentValue === 'stretch') {
65 height = -1;
66 }
67 return (
68 <View style={{ height: "60%", width: '100%', alignContent: alignContentValue, backgroundColor: '#efefef', flexDirection: 'row', flexWrap:'wrap'}}>
69 {this.item(50, height)}
70 {this.item(80, height)}
71 {this.item(30, height)}
72 {this.item(60, height)}
73 {this.item(50, height)}
74 {this.item(100, height)}
75 {this.item(30, height)}
76 {this.item(50, height)}
77 {this.item(80, height)}
78 {this.item(30, height)}
79 </View>
80 );
81 };
82
83 render () {
84 return (
85 <View style={{ height: 200, backgroundColor: '#e5e5e5'}}>
86 {this.operaView()}
87 {this.resultDisplayView()}
88 </View>
89 );
90 }
91 }
五、flexWrap
接下来看一下flexWrap这个属性,该属性负责折行的。例如当一个View没有设置flexWrap属性时,子元素又是横排的情况时,会在一行上一直往后排,并不会折行。如果想折行的话,那么就得使用这个flexWrap属性了,下方是flexWrap属性的相关值和用法:
属性值: flexWrap?:"wrap" | "nowrap" | "wrap-reverse" 用法示例: <View style={{ flexWrap: 'wrap' />
flexWrap的属性值比较少,也比较好理解,下方就进行简单的描述:
下方就是flexWrap所对应的Demo, 该Demo中的View就设置了flexWrap的属性为wrap的值,没点击一次我们就随机的往后边添加一个随机宽度的子View。从下方gif中不难看出,当最后一个View放不下时会自动的换到下一行进行展示。具体如下所示:
该示例的完整代码:
1 // flexWrap
2 import { Component } from "react";
3 import { TouchableOpacity, View } from "react-native";
4 import React from "react";
5
6 type FlexWrapTestComponentStateType = {
7 allViews: Array<any>
8 }
9 export default class FlexWrapTestComponent extends Component<null, FlexWrapTestComponentStateType> {
10 constructor(props) {
11 super(props);
12 this.state = {
13 allViews : [this.item()]
14 };
15 }
16
17 clickView = () => {
18 let items = this.state.allViews;
19 items.push(this.item());
20 this.setState({allViews: items});
21 };
22
23 item = () => {
24 let randomWidth = Math.random() * 100 + 10;
25 return (
26 <View style={{backgroundColor: 'green', height: 30, width: randomWidth, margin: 5}} />
27 );
28 };
29
30 render () {
31 return (
32 <TouchableOpacity onPress={this.clickView}>
33 <View style={{ height: 100, backgroundColor: '#eaeaea' , flexDirection: 'row', flexWrap: 'wrap',}}>
34 {this.state.allViews}
35 </View>
36 </TouchableOpacity>
37 );
38 }
39 }
六、AlignItem
该属性也是比较常用的,用来定义子元素在交叉轴上的对齐方式。也就是说,子元素是横向排列的,那么该属性就约定纵轴方向上的对齐方式。AlignItem属性的属性值也没几个,也比较好理解,下方是AlignItem对应的熟悉值和使用方式:
属性值: type FlexAlignType = "flex-start" | "flex-end" | "center" | "stretch" | "baseline"; 用法示例: <View style={{ alignItem: 'flex-start' />
下方就是真的AlignItem实现的一个Demo, 我们将根据下方的Demo来具体的看一下AlignItem所对应的每个属性值的作用,具体如下所示:
该示例完整代码如下:
1 // alignItem
2 import { FlexAlignType, Text, TouchableOpacity, View } from "react-native";
3 import { Component } from "react";
4 import React from "react";
5
6 type AlignItemTestCompontStateType = {
7 alignItemValue: FlexAlignType
8 }
9 export default class AlignItemTestComponent extends Component<null, AlignItemTestCompontStateType> {
10 constructor(props) {
11 super(props);
12 this.state = {
13 alignItemValue: 'flex-start'
14 };
15 }
16
17 clickView = (title: FlexAlignType) => () => {
18 this.setState({ alignItemValue: title});
19 };
20
21 customButton = (title: FlexAlignType) => {
22 return (
23 <TouchableOpacity onPress={this.clickView(title)}>
24 <View style = {{ width: 80, height: 30, backgroundColor: 'red', margin: 5, justifyContent:'center', alignItems:'center'}}>
25 <Text style={{color: '#fff', fontSize: 17}}>{title}</Text>
26 </View>
27 </TouchableOpacity>
28 );
29 };
30
31 operaView = () => {
32 return (
33 <View style={{ height: '100%', width: '30%', backgroundColor: '#e0e0e0', justifyContent: 'center', alignItems: 'center'
34 }}>
35 {this.customButton('flex-start')}
36 {this.customButton('center')}
37 {this.customButton('flex-end')}
38 {this.customButton('stretch')}
39 {this.customButton('baseline')}
40 </View>
41 );
42 };
43
44 item = (height: number, fontSize: number) => {
45 return (
46 <View style = {{ height: height, width: 50, backgroundColor: 'red' , margin: 10 }}>
47 <Text style={{fontSize: fontSize}}> {fontSize} </Text>
48 </View>
49 );
50 };
51
52 resultDisplayView = () => {
53 const {
54 alignItemValue
55 } = this.state;
56
57 let heights = [100, 150, 80];
58 if (alignItemValue === 'stretch') {
59 heights = [-1, -1, -1];
60 }
61 return (
62 <View style={{ height: '100%', width: '70%', alignItems: alignItemValue, backgroundColor: '#efefef', flexDirection: 'row', justifyContent: 'center' }}>
63 {this.item(heights[0], 10)}
64 {this.item(heights[1], 20)}
65 {this.item(heights[2], 30)}
66 </View>
67 );
68 };
69
70 render () {
71 return (
72 <View style={{ height: 200, backgroundColor: '#e5e5e5' , flexDirection: 'row'}}>
73 {this.operaView()}
74 {this.resultDisplayView()}
75 </View>
76 );
77 }
78 }
七、AlignSelf
最后我们来看一下这个AlignSelf属性,该属性是元素属性,主要设置在子元素上,用来控制单个子元素在父元素的交叉轴的位置。AlignSelf的作用方式与AlignItem差不多,只不过一个作用于父元素,一个是作用于子元素。下方是AlignSelf的属性值和用法示例:
属性值: type FlexAlignType = "flex-start" | "flex-end" | "center" | "stretch" | "baseline"; type AlignSelfType = "auto" | FlexAlignType; 用法示例: <View/> <View style={{ alignSelf: 'flex-start' /> </View>
最后我们仍然通过一个Demo来看一下AlignSelf的表现形式。在下方Demo中我们依次为右边中间的黑块设置的AlignSelf属性。每个属性的值的意思可参见AlignItem的属性值,只不过这些属性值是作用于子元素的。具体关于AlignSelf的内容就不做过多赘述了。
该部分Demo完整示例:
1 // alignSelf
2 import { FlexAlignType, Text, TouchableOpacity, View } from "react-native";
3 import { Component } from "react";
4 import React from "react";
5
6 type AlignSelfTestCompontStateType = {
7 alignItemValue: FlexAlignType
8 }
9 export default class AlignSelfTestComponent extends Component<null, AlignSelfTestCompontStateType> {
10 constructor(props) {
11 super(props);
12 this.state = {
13 alignItemValue: 'flex-start'
14 };
15 }
16
17 clickView = (title: FlexAlignType) => () => {
18 this.setState({ alignItemValue: title});
19 };
20
21 customButton = (title: FlexAlignType) => {
22 return (
23 <TouchableOpacity onPress={this.clickView(title)}>
24 <View style = {{ width: 80, height: 30, backgroundColor: 'black', margin: 5, justifyContent:'center', alignItems:'center'}}>
25 <Text style={{color: '#fff', fontSize: 17}}>{title}</Text>
26 </View>
27 </TouchableOpacity>
28 );
29 };
30
31 operaView = () => {
32 return (
33 <View style={{ height: '100%', width: '30%', backgroundColor: '#e0e0e0', justifyContent: 'center', alignItems: 'center'
34 }}>
35 {this.customButton('flex-start')}
36 {this.customButton('center')}
37 {this.customButton('flex-end')}
38 {this.customButton('stretch')}
39 </View>
40 );
41 };
42
43 resultDisplayView = () => {
44 const {
45 alignItemValue
46 } = this.state;
47
48 let height = 80;
49 if (alignItemValue === 'stretch') {
50 height = -1
51 }
52 return (
53 <View style={{ height: '100%', width: '70%', alignItems: 'flex-start', backgroundColor: '#efefef', flexDirection: 'row', justifyContent: 'center' }}>
54 <View style = {{ height: 150, width: 50, backgroundColor: 'black' , margin: 10}}/>
55 <View style = {{alignSelf: alignItemValue, height: height, width: 50, backgroundColor: 'black' , margin: 10}}/>
56 <View style = {{ height: 100, width: 50, backgroundColor: 'black' , margin: 10}}/>
57 </View>
58 );
59 };
60
61 render () {
62 return (
63 <View style={{ height: 180, backgroundColor: '#e5e5e5' , flexDirection: 'row'}}>
64 {this.operaView()}
65 {this.resultDisplayView()}
66 </View>
67 );
68 }
69 }
经过本篇博客的详细介绍想必对FlexBox有了更详细的了解,掌握了上述属性后,在RN中写布局应该就不是什么难事儿了。当然本篇博客值介绍了FlexBox布局比较核心的部分,想什么Margin、Padding等等这些属性比较简单,就不做过多赘述了。本篇博客所涉及的所有Demo会在github上给出,下方会给出相关链接。
下篇博客会集中根据具体示例来聊一下RN中常用的动画。