FlexBox
React Native是采用FlexBox(弹性)布局,使用FlexBox规则来指定某个组件的子元素的布局,FlexBox提供了在不同尺寸设备上都能保持一致的布局方式,它是CSS3弹性框布局规范,因此熟悉前端的同学可能对此感到很亲切,不过React Native的FlexBox与web上的CSS也存在少许差异。
首先是默认值不同:flexDirection的默认值是column而不是row(这一点与手机客户端开发一致),而flex也只能指定一个数字值。
FlexBox类似android开发中的LinearLayout(线性布局),不过还是有许多不同之处,千万不能用LinearLayout的知识生搬硬套。
在学习FlexBox属性之前,让我们先了解一个概念:主轴和次轴
- 主轴即子元素线性排列的方向,如图水平轴为主轴,即子元素按水平方向排列。
- 次轴即与主轴垂直的轴,在图中为纵轴。
父视图属性(容器属性)
- flexDirection
- flexWrap
- justifyContent
- alignItems
flexDirection
flexDirection指定布局的主轴方向,定义了父视图中的子元素子元素是应该沿着水平轴(row)方向排列,还是沿着竖直轴(column)方向排列。如果未指定,默认值是竖直轴(column)方向。(这点与css中不同,但与android的LinearLayout是一致的,默认竖直方向排列)
flexDirection: row | row-reverse | column | column-reverse;
* row: 从左向右依次排列
* row-reverse: 从右向左依次排列
* column(default): 默认的排列方式,从上向下排列
* column-reverse: 从下向上排列
代码示例:
- import React,{Component} from 'react';
- import {
- Text,View,StyleSheet
- } from 'react-native';
-
- export default class FlexDirectionDemo extends Component {
- render() {
- return (
- <View style={Styles.container}> <Text style={Styles.subtitle}>flexDirectionDemo:row</Text> <View style={Styles.Box1}> <Text style={Styles.item}>1</Text> <Text style={Styles.item}>2</Text> <Text style={Styles.item}>3</Text> <Text style={Styles.item}>4</Text> </View> <Text style={Styles.subtitle}>flexDirectionDemo:row-reverse</Text> <View style={Styles.Box2}> <Text style={Styles.item}>1</Text> <Text style={Styles.item}>2</Text> <Text style={Styles.item}>3</Text> <Text style={Styles.item}>4</Text> </View> <Text style={Styles.subtitle}>flexDirectionDemo:column</Text> <View style={Styles.Box3}> <Text style={Styles.item}>1</Text> <Text style={Styles.item}>2</Text> <Text style={Styles.item}>3</Text> <Text style={Styles.item}>4</Text> </View> <Text style={Styles.subtitle}>flexDirectionDemo:column-reverse</Text> <View style={Styles.Box4}> <Text style={Styles.item}>1</Text> <Text style={Styles.item}>2</Text> <Text style={Styles.item}>3</Text> <Text style={Styles.item}>4</Text> </View> </View> ); } } const Styles = StyleSheet.create({ container: { backgroundColor: "#ffffff",flex: 1,},subtitle:{ backgroundColor: '#ffffff',alignItems: 'center' },Box1:{ height: 60,backgroundColor: '#333333',flexDirection:"row",Box2:{ height: 60,flexDirection:"row-reverse",Box3:{ height: 180,flexDirection:"column",Box4:{ height: 180,flexDirection:"column-reverse",item: { backgroundColor: "#f0f",width:30,margin: 4,height: 30,})
flexWrap
flexWrap定义了子元素在父视图内是否允许多行排列,默认为nowrap(不换行)
flexWrap: nowrap | wrap ;
- nowrap (默认):不换行,可能导致溢出。
- wrap 换行,子元素在一行排列不下时,就进行多行排列。
代码示例:
- import React,StyleSheet
- } from 'react-native';
-
- export default class FlexWrapDemo extends Component {
- render() {
- return (
- <View style={Styles.container}> <Text style={Styles.subtitle}>flexWrap:nowrap</Text> <View style={Styles.Box1}> <Text style={Styles.item}>1</Text> <Text style={Styles.item}>2</Text> <Text style={Styles.item}>3</Text> <Text style={Styles.item}>4</Text> </View> <Text style={Styles.subtitle}>flexWrap:wrap</Text> <View style={Styles.Box2}> <Text style={Styles.item}>1</Text> <Text style={Styles.item}>2</Text> <Text style={Styles.item}>3</Text> <Text style={Styles.item}>4</Text> </View> </View> ); } } const Styles = StyleSheet.create({ container: { backgroundColor: "#ffffff",Box1:{ height: 150,flexWrap:"nowrap",Box2:{ height: 150,flexWrap:"wrap",width:100,height: 50,})
justifyContent
justifyContent定义了子元素在主轴上的对齐方式。
justifyContent: flex-start | flex-end | center | space-between | space-
around;
- flex-start(默认值):左对齐
- flex-end:右对齐
- center: 居中
- space-between:两端对齐,子元素之间的间隔都相等。
- space-around:每个子元素两侧的间隔相等。所以,子元素之间的间隔比子元素与边框的间隔大一倍。
- import React,StyleSheet
- } from 'react-native';
-
- export default class JustifyContentDemo extends Component {
- render() {
- return (
- <View style={Styles.container}> <Text style={Styles.subtitle}>JustifyContent:flex-start</Text> <View style={Styles.Box1}> <Text style={Styles.item}>1</Text> <Text style={Styles.item}>2</Text> <Text style={Styles.item}>3</Text> </View> <Text style={Styles.subtitle}>JustifyContent:flex-end</Text> <View style={Styles.Box2}> <Text style={Styles.item}>1</Text> <Text style={Styles.item}>2</Text> <Text style={Styles.item}>3</Text> </View> <Text style={Styles.subtitle}>JustifyContent:center</Text> <View style={Styles.Box3}> <Text style={Styles.item}>1</Text> <Text style={Styles.item}>2</Text> <Text style={Styles.item}>3</Text> </View> <Text style={Styles.subtitle}>JustifyContent:space-between</Text> <View style={Styles.Box4}> <Text style={Styles.item}>1</Text> <Text style={Styles.item}>2</Text> <Text style={Styles.item}>3</Text> </View> <Text style={Styles.subtitle}>JustifyContent:space-around</Text> <View style={Styles.Box5}> <Text style={Styles.item}>1</Text> <Text style={Styles.item}>2</Text> <Text style={Styles.item}>3</Text> </View> </View> ); } } const Styles = StyleSheet.create({ container: { backgroundColor: "#0ff",Box1:{ height: 100,justifyContent:"flex-start",Box2:{ height: 100,justifyContent:"flex-end",Box3:{ height: 100,justifyContent:"center",Box4:{ height: 100,justifyContent:"space-between",Box5:{ height: 100,justifyContent:"space-around",width:50,})
alignItems
alignItems定义子元素在次轴(与主轴垂直的轴)上的对齐方式。
alignItems: flex-start | flex-end | center | stretch;
- flex-start:次轴的起点对齐。
- flex-end:次轴的终点对齐。
- center:次轴的中点对齐。
- stretch(默认值):子元素在次轴方向充满整个容器的高度或宽度。
注意:要使stretch选项生效的话,子元素在次轴方向上不能有固定的尺寸
- import React,StyleSheet
- } from 'react-native';
-
- export default class AlignItemsDemo extends Component {
- render() {
- return (
- <View style={Styles.container}> <Text style={Styles.subtitle}>alignItems</Text> <View style={Styles.Box}> <Text style={Styles.item}>1</Text> <Text style={Styles.item}>2</Text> <Text style={Styles.item}>3</Text> <Text style={Styles.item_flex_end}>4</Text> <Text style={Styles.item}>5</Text> </View> </View> ); } } const Styles = StyleSheet.create({ container: { backgroundColor: "#0ff",height: 300,Box:{ flex: 1,alignItems:"flex-end",//通过设置flex-start | flex-end | center | stretch;各种值可以看看效果 },flexGrow: 1,height: 100,item_flex_end: { backgroundColor: "#f0f",alignSelf: "flex-end",} })
子视图属性
- alignSelf
- flex
alignSelf
alignSelf属性以属性定义了flex容器内被选中子元素的对齐方式。允许该子元素有与其他子元素不一样的对齐方式,可覆盖alignItems 属性。
alignSelf: auto | flex-start | flex-end | center | stretch;
默认值为auto,表示继承父元素的alignItems属性,如果没有父元素,则等同于stretch。
该属性可能取5个值,除了auto,其他都与alignItems属性完全一致。
以下图为例,该图中父元素的alignItems属性为flex-start,所以子元素按照图alignItems
中的flex-start所示,而第三个子元素设置了其alignSelf属性为flex-end,因此第三个子元素的alignSelf属性覆盖父元素的alignItems属性变为与底部对齐。
代码示例:
- import React,StyleSheet
- } from 'react-native';
-
- export default class AlignSelfDemo extends Component {
- render() {
- return (
- <View style={AlignSelfDemoStyle.container}> <Text style={AlignSelfDemoStyle.subtitle}>alignSelf</Text> <View style={AlignSelfDemoStyle.Box}> <Text style={AlignSelfDemoStyle.item}>1</Text> <Text style={AlignSelfDemoStyle.item}>2</Text> <Text style={AlignSelfDemoStyle.item}>3</Text> <Text style={AlignSelfDemoStyle.item_flex_end}>4</Text> <Text style={AlignSelfDemoStyle.item}>5</Text> </View> </View> ); } } const AlignSelfDemoStyle = StyleSheet.create({ container: { backgroundColor: "#0ff",} })
flex
flex 属性定义了一个可伸缩元素的能力(flex的属性值大于0的时候才可伸缩)。
flex: number
如果父元素只有一个子元素设置 flex的值,通常flex:1, 指定该子元素扩展以充满所有剩余空间。
如果有多个子元素都设置了flex值,则根据flex的值的比值,瓜分所有剩余空间。
举个简单的例子:A、B、C三个元素,其flex的值分别为1,2,3。则其三个所占空间大小为1:2:3,如下图:
代码示例:
- import React,} from 'react-native';
-
- export default class FlexDemo extends Component {
- render() {
- return (
- <View style={ {flexDirection:'row',height:40,backgroundColor:"darkgray"}}> <View style={ {flex:1,backgroundColor:"red"}}> <Text style={ {fontSize:16}}>flex:1</Text> </View> <View style={ {flex:2,backgroundColor:"blue"}}> <Text style={ {fontSize:16}}>flex:2</Text> </View> <View style={ {flex:3,backgroundColor:"green"}}> <Text style={ {fontSize:16}}>flex:3</Text> </View> </View> ); } }
如果将三个子元素的flex都改为1,则三个子元素所占空间大小也是1:1:1
定位position
position 属性设置元素的定位方式,为将要定位的元素定义定位规则。
position : absolute | relative
绝对定位和相对定位。
- absolute:绝对定位,生成绝对定位的元素,元素的位置通过 “left”,“top”,“right” 以及 “bottom” 属性进行规定。
- relative:相对定位,生成相对定位的元素,相对于其正常位置进行定位。
如果你之前是做Android开发的,这里会感觉很不适应,这个absolute和relative与Android中的AbsoluteLayout和RelativeLayout大不相同,特别是relative,可以说是完全不同。
网上有很多人总结说“react native 相对定位不是相对于父容器,而是相对于兄弟节点”,我觉得说得很不准确。一个元素如果不设定position去定位的话,默认会形成文档流。每个元素会按顺序出现在文档流中,排到自己的位置上。而如果该元素设置了相对定位position:’relative’,则会相对于自己原本该在文档流的位置进行偏移,并且不影响其相邻的元素的位置摆放。因此总结下来,react native 相对定位不是相对于父容器,也不是相对于兄弟节点,而是相对于自己在文档流中正常位置进行定位。
绝对定位absolute则是相对于父容器进行绝对定位,脱离了文档流。
举个例子,有三个view,普通排列,由于rn是默认flexBox布局,所以会是从上到下一个挨着一个排列:
- import React,ScrollView,StyleSheet
- } from 'react-native';
-
- export default class PositionDemo extends Component {
- render() {
- return (
- <ScrollView> <View style={{flex:1}}> <Text>FlexBox布局</Text> <View style={styles.container}> <View style={styles.Box1}/> <View style={[styles.Box2]}/> <View style={[styles.Box3]}/> </View> <Text>第二个元素position=relative,top:20,left20</Text> <View style={styles.container}> <View style={styles.Box1}/> <View style={[styles.Box2,{position:'relative',top:20,left:20}]}></View> <View style={styles.Box3}/> </View> <Text>第二个元素position=absolute,{position:'absolute',left:20}]}></View> <View style={styles.Box3}/> </View> </View> </ScrollView> ); } } const styles = StyleSheet.create({ container: { height: 180,backgroundColor: '#CCCCCC',marginBottom: 10,Box1: { width: 50,backgroundColor: '#FF0000' },Box2: { width: 50,backgroundColor: '#00FF00' },Box3: { width: 50,backgroundColor: '#0000FF' } });
如上图所示,第二组中,我将第二元素设置为相对定位
positon:relative,left:20
,可以看到第二个元素偏离原来的位置,但其仍然没有脱离文档流,只是相对于它原本的位置进行偏移。在第三组中,我将第二个元素设置为绝对定位
positon:absolute,left:20
,可以看到第二个元素脱离了文档流,相对于父容器进行偏移,剩下第一个元素和第三个元素,还规规矩矩的排在那里。
好奇的朋友可以在第一组中为第二个元素设置一下top:20,left:20
,
- <View style={[styles.Box2,{top:20,left:20}]}/>
看看会出现什么样的结果呢?
如上图,它与第二组的情况一样,所以我们可以看出元素默认的position是relative。即我们如果不设值position的话,默认按相对定位来处理。
其他属性
- margin 外边距
- marginBottom 底部外边距
- marginLeft 左外边距
- marginRight 右外边距
- marginTop 上外边距
- marginVertical 上下外边距,如果marginTop与marginBottom一样的话,可以直接用这个值代替
- marginHorizontal 左右外边距
- borderColor 整体边框颜色
- borderRadius 整体边框的圆角
- borderWidth 整体边框的宽
- borderStyle 边框样式 dotted solid double dashed等
- borderBottomColor 底边框颜色
- borderBottomWidth 底边框宽度
- borderLeftColor 左边框颜色
- borderLeftWidth 左边框宽度
- borderRightColor 右边框颜色
- borderRightWidth 右边框宽度
- borderTopColor 上边框颜色
- borderTopWidth 上边框宽度
- borderBottomLeftRadius 左下角圆角边框
- borderBottomRightRadius 右下角圆角边框
- borderTopLeftRadius 上边框左圆角
- borderTopRightRadius 上边框右圆角
- padding 内边距
- paddingBottom 底部内边距
- paddingTop 顶部内边距
- paddingLeft 左内边距
- paddingRight 右内边距
- paddingHorizontal 左右内边距
- paddingVertical 上下内边距
- height 元素高度,包含padding与border
- width 元素宽度,包含padding与border
Demo
本文的示例代码在:
https://github.com/mronion0603/ReactNativeExercise/tree/master/src/02_flex