CSS布局 + Flex布局

目录

一. CSS布局
二. Flex布局
 1. 容器(父视图)的属性
 2. item(子视图)的属性
三. 几个小练习


一. CSS布局


我们只会用到一点点儿CSS布局的属性,虽然少,但既然要用到,就得把它们总结在这里,搞明白。

在了解CSS布局的相关属性之前,我们先了解一下盒子模型——CSS会把每个组件都看作是一个盒子,每个盒子从内往外有三个部分:

  • content:盒子的内容,如文字、图片、子视图等
  • padding:内边距,即盒子的内容距离盒子内边框的距离
  • border:盒子的边框

1、宽和高

组件的宽度和高度决定了它在屏幕上显示的尺寸,一个组件的宽度和高度就是它外边框所包围矩形的宽度和高度,iOS里也是这样的。

  • 固定宽高(widthheight属性)

我们可以通过widthheight属性给一个组件设置固定的宽度,不过需要注意这两个属性只能接收数值,不能带单位,这是因为RN要自己去适配安卓和iOS的单位。

  • 自适应宽高(多个属性组合实现)

在RN里,我们想让一个组件自适应宽高,要分两步。首先我们要让该组件撑满它的父视图,即如果我们想自适应宽度,就得先让子视图的宽度撑满它父视图的宽度,如果我们想自适应高度,就得先让子视图撑满它父视图的高度,如果我们宽度和高度都想自适应,就得先让子视图把它父视图的宽度和高度都撑满。然后才能像我们iOS里使用Masonry那样,通过给组件添加上边距、左边距、下边距、右边距等约束来实现宽度和高度的自适应。

这里再对第一步做个解释,item的flex: 1属性可以让item在主轴上撑满它的容器,容器的alignItems: 'stretch'属性 + item在侧轴上不设置空间可以让item在侧轴上撑满它的容器。好像有点不明白是吧,我们再通过一个例子来说得明了点,RN里不是默认主轴为竖向、侧轴为横向嘛,我们就采取这个默认状态来举例子。在RN里,默认主轴为竖向、侧轴为横向的状态下,如果我们想让某个组件自适应高度,就得首先在它自身上设置flex: 1属性,来让它的高度撑满它的父视图;如果我们想让某个组件自适应宽度,就得首先在它父视图身上设置alignItems: 'stretch'属性(当然默认就是这个值),这还不够,你还得确保不给这个组件设置width属性或者width属性的值设为auto;如果我们想让一个组件自适应宽高,则两者都得做。当然,如果我们设置了主轴为横向、侧轴为竖向,其实也是一样的道理,自己捋一下就知道怎么做了。如果还是看不懂的话,没有关系,下面我们会详细谈到这几个属性,等你学了这几个属性,反过头来看这里就明白了。

2、外边距

  • marginTop:上边距,类似于Masonry里的top,接收一个数值
  • marginRight:右边距,类似于Masonry里的right,接收一个数值
  • marginBottom:下边距,类似于Masonry里的bottom,接收一个数值
  • marginLeft:左边距,类似于Masonry里的left,接收一个数值
  • margin:外边距,类似于Masonry里的edges,接收一个数值,同时设置上右下左边距


二. Flex布局


Flex是Flexible Box的缩写,可译为灵活的、有弹性的盒子。那Flex布局就是Flexible Box布局的缩写,就译为弹性盒子布局,用来对一个盒子进行灵活的布局。

在了解Flex布局之前,我们得先了解一下Flex布局里的两对儿重要的概念。

  • 容器和item

采用Flex布局的组件,被称为Flex容器(flex container),简称容器(container)。

这个组件上的子组件,被称为Flex项目(flex item),简称item(项目)。

我们可以把容器和item看做是父视图和子视图的关系,因此下文中的容器、父组件、父视图将是一样的概念,item、组件、子视图将是一样的概念,组件和视图将是一样的概念。

  • 主轴和侧轴

每个容器都有两根轴,一个是主轴,一个是侧轴

容器里众多的item自动沿主轴进行排列。

一个item在主轴方向上所占据的空间称为main size,一个item在侧轴方向上所占据的空间称为cross size

RN里主轴的默认方向为竖向,侧轴为横向。浏览器里主轴的默认方向为横向,侧轴为竖向,下面我们讲Flex布局的属性时会采用浏览器的状态,学会后对应到RN里,换个主轴方向就可以了。

1. 容器(父视图)的属性

用在容器上的属性,最常用的有5个而且这些属性还都不是从来设置容器自己的布局,而是用来设置容器里item的布局。

  • flex-direction
  • justify-content
  • align-items
  • flex-wrap
  • align-content
1.1 flex-direction属性

flex-direction属性用来设置主轴的方向,即容器里item自动排列的方向。在浏览器里,主轴的方向默认为横向,所以flex-direction属性的默认值为row

它有4种取值。

  • row(默认值):横向
  • row-reverse:横向,逆向
  • column:竖向
  • column-reverse:竖向,逆向
1.2 justify-content属性

justify-content属性用来设置item在主轴上的对齐方式。

它有5种取值。

  • flex-start(默认值):左对齐
  • flex-end:右对齐
  • center: 居中(justify-content属性设置为这个值,可以勉强类似于Masonry里的centerX,毕竟centerX一般就用来做做居中效果)
  • space-between:两端对齐,item等间隔布局,但是第一个和最后一个item贴边。(这种对齐方式会把容器主轴上多余出来的space平均分配,作为每两个item之间的间隔(between),也就是说item是等间隔布局的,但是如果我们不特别设置,那么第一个和最后一个item都是紧贴容器边儿的。这种方式一般用在有多个item的情况下。
  • space-around:两端对齐,item两侧等间隔布局,第一个和最后一个不会贴边,但是会比item之间的间隔小一倍。(这种对齐方式会把容器主轴上多余出来的space平均分配,around到每个item的两侧,所以说第一个和最后一个item不会紧贴容器的边儿,但是item与容器边儿的间隔会比item之间的间隔小一倍。这种方式一般用在有多个item的情况下。
1.3 align-items

align-items属性用来设置item在侧轴上的对齐方式。

它有5种取值。

  • flex-start:上对齐
  • flex-end:下对齐
  • center:居中(align-items属性设置为这个值,可以勉强类似于Masonry里的centerY,毕竟centerY一般就用来做做居中效果)
  • baseline: item的第一行文字的基线对齐
  • stretch(默认值):如果item未设置height属性或height属性的值设为auto,那它的高度会撑满父视图的高度。我刚开始疑惑这明明是把一个item拉伸了,怎么把它归类为对齐方式呢?后来一想,item拉伸撑满父视图了,那可不就是一种对齐方式嘛,你说它上对齐也好、下对齐也好、居中也好,怎么都行。(这个属性很重要,在组件自适应宽高的时候会用到,要用心理解它:item父视图的alignItems属性设置为stretch值时,只要item在侧轴上不设置空间或空间值设为auto,那item就会在侧轴上撑满它的父视图。
1.4 flex-wrap属性

默认情况下,一个容器只有一条主轴,所有的item都会沿着这条主轴自动排列下去,但是如果item太多了,一个屏幕长度的主轴根本串儿不下这么多item,那后面的item就会溢出屏幕,而界面还不是ScrollView,不能滚动,怎么办呢?

flex-wrap属性用来设置换行,即当容器主轴上显示不下那么多的item时,我们可以让item换行来显示,并且一旦设置了换行,那么这个容器就不再是一条主轴了,而是会有多条平行的主轴。

它有3种取值。

  • nowrap(默认):不换行。
  • wrap:换行,第一行在上方,第二行折下去。
  • wrap-reverse:换行,第一行在下方,第二行折上去。
1.5 align-content属性

align-content属性用来设置多条主轴的对齐方式,如果项目只有一根轴线,该属性不起作用,align-content属性要基于flex-wrap属性设置了换行才有使用的意义。

它有6种取值。

  • flex-start:与侧轴的起点对齐
  • flex-end:与侧轴的终点对齐
  • center:与侧轴的中点对齐
  • space-between:与侧轴两端对齐,轴线等间隔布局
  • space-around:与侧轴两端对齐,轴线两侧等间隔布局
  • stretch(默认值):轴线撑满整个侧轴

2. item(子视图)的属性

用在item上的属性,最常用的有2个这些属性都是用来设置item自己的布局。

  • align-self
  • flex
2.1 align-self属性

在第1小节容器的属性,我们已经通过align-items属性统一设置了容器里item在侧轴上的对齐方式,但如果其中某个item不想遵循这个对齐方式怎么办呢?我们会常见到这种场景,10个里有8个item是一样的对齐方式,就有1、2个item特立独行,那我们就得用item自己的align-self属性来单独给它们设置对齐方式了。

align-self属性用来设置单个item在侧轴上的对齐方式,注意是给特立独行的单个item设置哦,而且是侧轴,它会覆盖它容器的align-items属性,当然如果不设置这个属性,默认值就为auto,即item会默认使用容器的align-items属性给它设置的对齐方式。

它有6种取值,除了auto,其他都与align-items属性完全一致。怎么说呢,item的align-self属性似乎是用来造反的,反抗它容器的align-items属性。

注意:这个属性是负责item在侧轴上的对齐方式的,所以它也可以自适应宽或高,只不过通常情况下,我们通过它父视图的align-items属性做了,毕竟它是用来造反的,如果真遇见某种情况必须得用它来做自适应宽高,那才用它。

2.2 flex属性

flex属性接收一个整数值,flex属性描述起来太麻烦了,可以自行去搜索加深理解,我这里只列举出它的用途及注意事项。

(1)flex属性有三个用途

  • 单个item撑满父视图主轴剩余空间:只有一个item时,我们会用flex: 1来让item撑满父视图主轴上的剩余空间。(注意是主轴哦,flex属性只能保证item撑满它父视图主轴上的剩余空间,侧轴它是不负责的,要想撑满侧轴,则是用容器的属性align-items属性+item不设置cross sizecross size设为auto。)
  • 单个item自适应宽或高:只有一个item时,我们会用flex: 1来让item首先撑满它父视图主轴上的剩余空间,这同时就使得item具备了可变宽高的能力,然后我们就可以结合使用item的外边距属性来让item自适应宽或高了。
  • 多个item按比例分配父视图主轴上的剩余空间:有多个item时,我们通过设置各个item的flex属性为不同的值,来让系统按比例分配各个item该占用多少父视图主轴上的剩余空间。

(2)使用flex属性需注意

  • flex属性的默认值为0,代表item不自适应宽或高,此时item就必须通过widthheight属性来指定宽高。
  • 在进行布局的时候,Flex布局会优先去布局那些通过widthheight属性设置固定宽高的item,这样就知道了主轴上的剩余空间,然后才会去按比例布局那些通过flex属性设置自适应宽高的item,通过flex属性设置的宽高会覆盖通过widthheight属性设置的宽高。


三. 几个小练习


iOS里怎么使用Masonry布局的,RN里就怎么使用Flex布局+CSS布局来做布局就行了,它们布局的思路是一样的,一个组件也同样至少需要四个约束。

当然,它们之间有一个极其细小的差别,那就是:上面提到的“RN里组件自适应宽高分两步”,而iOS里Masonry才不管你什么撑满不撑满呢。

当然,它们之间有一个极其巨大的差别,那就是:iOS里是没有主轴和侧轴之分的,你可以在一个父视图上横向、竖向同时放控件,并同时完成横向和竖向的布局。但RN里有主轴和侧轴之分,我们添加的组件只会沿着主轴排列,不可能同时出现横向和竖向排列,并且布局的时候也只能是沿着主轴布局,不可能同时完成两个方向的布局。

下面我们就通过几个小练习,来对比一下Masonry布局和Flex布局+CSS布局的思路,加深对RN里怎么布局组件的理解。

  • 练习1:宽高都是固定值。居中显示一个View
目标效果

Masonry:

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor whiteColor];
    
    UIView *blackView = [UIView new];
    blackView.backgroundColor = [UIColor blackColor];
    [self.view addSubview:blackView];
    [blackView mas_makeConstraints:^(MASConstraintMaker *make) {
        
        make.centerX.equalTo(self.view);// 约束1:横向居中
        make.centerY.equalTo(self.view);// 约束2:竖向居中
        make.width.mas_equalTo(300);// 约束3:宽度
        make.height.mas_equalTo(300);// 约束4:高度
    }];
}

Flex布局+CSS布局:

const styles = StyleSheet.create({
    containerStyle: {
        flex: 1,// 这个container类似于我们iOS里的ViewController.view,它们都添加在window上,但我们iOS里的会自动撑满window,RN里需要手动设置一下
        backgroundColor: 'white',

        justifyContent: 'center',// 约束1:子视图竖向居中
        alignItems: 'center',// 约束2:子视图横向居中
    },

    blackViewStyle: {
        backgroundColor: 'black',

        width: 300,// 约束3:宽度
        height: 300,// 约束4:高度
    },
});
  • 练习2:宽高都自适应。让一个view略小于其superView居中显示(边距为10)
目标效果

Masonry:

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor whiteColor];
    
    UIView *blackView = [UIView new];
    blackView.backgroundColor = [UIColor blackColor];
    [self.view addSubview:blackView];
    [blackView mas_makeConstraints:^(MASConstraintMaker *make) {
        
        make.centerX.equalTo(self.view);
        make.centerY.equalTo(self.view);
        make.width.mas_equalTo(300);
        make.height.mas_equalTo(300);
    }];
    
    UIView *redView = [UIView new];
    redView.backgroundColor = [UIColor redColor];
    [blackView addSubview:redView];
    [redView mas_makeConstraints:^(MASConstraintMaker *make) {
        
//        make.top.equalTo(blackView).offset(10);// 约束1:上边距
//        make.left.equalTo(blackView).offset(10);// 约束2:左边距
//        make.bottom.equalTo(blackView).offset(-10);// 约束3:下边距
//        make.right.equalTo(blackView).offset(-10);// 约束4:右边距
        
        // 逆时针:上左下右
        make.edges.mas_equalTo(UIEdgeInsetsMake(10, 10, 10, 10));// 约束1、2、3、4
    }];
}

Flex布局+CSS布局:

const styles = StyleSheet.create({
    containerStyle: {
        flex: 1,
        backgroundColor: 'white',

        justifyContent: 'center',
        alignItems: 'center',
    },

    blackViewStyle: {
        backgroundColor: 'black',

        width: 300,
        height: 300,
    },

    redViewStyle: {
        backgroundColor: 'red',

        flex: 1,// 这行代码用来使redView撑满父视图的高度,以便redView能自适应高度
        // 那宽度额?因为父视图的alignItems属性默认为'stretch',所以只要这里不设置redView的宽度,它的宽度就会默认撑满父视图,那宽度也能自适应了

        // marginTop: 10,// 约束1:上边距
        // marginRight: 10,// 约束2:右边距
        // marginBottom: 10,// 约束3:下边距
        // marginLeft: 10,// 约束4:左边距

        // 顺时针:上右下左,这个属性只接收一个数值,同时设置4个方向
        margin: 10,
    },
});

注意:这里用到了flex属性的第二个用途。

  • 练习3:宽度自适应,高度固定值。让两个view垂直居中且等宽且等间隔排列(间隔为10,自动计算其宽度)
目标效果

Masonry:

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor whiteColor];
    
    UIView *blackView = [UIView new];
    blackView.backgroundColor = [UIColor blackColor];
    [self.view addSubview:blackView];
    [blackView mas_makeConstraints:^(MASConstraintMaker *make) {
        
        make.centerX.equalTo(self.view);
        make.centerY.equalTo(self.view);
        make.width.mas_equalTo(300);
        make.height.mas_equalTo(300);
    }];
    
    UIView *orangeView1 = [UIView new];
    orangeView1.backgroundColor = [UIColor orangeColor];
    [blackView addSubview:orangeView1];
    
    UIView *orangeView2 = [UIView new];
    orangeView2.backgroundColor = [UIColor orangeColor];
    [blackView addSubview:orangeView2];
    
    [orangeView1 mas_makeConstraints:^(MASConstraintMaker *make) {
        
        make.centerY.equalTo(blackView);// 约束1:竖向居中
        make.left.equalTo(blackView).offset(10);// 约束2:左边距
        make.right.equalTo(orangeView2.mas_left).offset(-10);// 约束3:右边距,距离后面一个兄弟视图的左边框
        make.height.mas_equalTo(150);// 约束4:高度
    }];
    
    [orangeView2 mas_makeConstraints:^(MASConstraintMaker *make) {
        
        make.centerY.equalTo(blackView);// 约束1:竖向居中
        make.right.equalTo(blackView).offset(-10);// 约束2:右边距,距离父视图的右边框
        make.height.mas_equalTo(150);// 约束3:高度
        make.width.equalTo(orangeView1);// 约束4:和orangeView1等宽,这是非常重要的一个约束,这样系统就知道怎么给orangeView1和orangeView2分配空间了
    }];
}

Flex布局+CSS布局:

const styles = StyleSheet.create({
    containerStyle: {
        flex: 1,
        backgroundColor: 'white',

        justifyContent: 'center',
        alignItems: 'center',
    },

    blackViewStyle: {
        backgroundColor: 'black',

        width: 300,
        height: 300,

        flexDirection: 'row',// 先让这两个橙色视图横向排列

        alignItems: 'center',// 约束1:竖向居中
    },

    orangeView1Style: {
        backgroundColor: 'orange',

        flex: 1,// 功能1:让视图可以通过外边距自适应宽高。功能2:和orangeView2设置为一样的数值,让系统知道怎么给orangeView1和orangeView2分配空间,达到等宽的效果。
        marginLeft: 10,// 约束2:左边距
        marginRight: 10,// 约束3:右边距,距离后面一个兄弟视图的左边框
        height: 150,// 约束4:高度
    },

    orangeView2Style: {
        backgroundColor: 'orange',

        marginRight: 10,// 约束2:右边距,距离父视图的右边框
        height: 150,// 约束3:高度
        flex: 1,// 功能1:让视图可以通过外边距自适应宽高。功能2:和orangeView1设置为一样的数值,让系统知道怎么给orangeView1和orangeView2分配空间,达到等宽的效果。
    },
});

注意:这里用到了flex属性的第三个用途。

  • 练习4:宽度自适应,高度固定值。红色视图的宽度自适应(左右边距为10),高度给个固定值150
预期效果

Masonry:

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor whiteColor];
    
    UIView *blackView = [UIView new];
    blackView.backgroundColor = [UIColor blackColor];
    [self.view addSubview:blackView];
    [blackView mas_makeConstraints:^(MASConstraintMaker *make) {
        
        make.centerX.equalTo(self.view);
        make.centerY.equalTo(self.view);
        make.width.mas_equalTo(300);
        make.height.mas_equalTo(300);
    }];
    
    
    UIView *redView = [UIView new];
    redView.backgroundColor = [UIColor redColor];
    [blackView addSubview:redView];
    [redView mas_makeConstraints:^(MASConstraintMaker *make) {
        
        make.centerY.equalTo(blackView);// 约束1:竖向居中
        make.left.equalTo(blackView).offset(10);// 约束2:左边距
        make.right.equalTo(blackView).offset(-10);// 约束3:右边距
        make.height.mas_equalTo(150);// 约束4:高度
    }];
}

Flex布局+CSS布局:

const styles = StyleSheet.create({
    containerStyle: {
        flex: 1,
        backgroundColor: 'white',

        justifyContent: 'center',
        alignItems: 'center',
    },

    blackViewStyle: {
        backgroundColor: 'black',

        width: 360,
        height: 300,

        justifyContent: 'center',// 约束1:竖向居中
    },

    redViewStyle: {
        backgroundColor: 'red',

        // flex: 1,// 注意这里千万不能写这句话哦,因为这个时候主轴是竖向的,它是用来做高度自适应的,然而我们现在不需要做高度自适应,所以不能写,一旦写了就会覆盖掉height属性,达不到固定值的效果
        // 同时,因为父视图的alignItems属性默认为'stretch',所以只要这里不设置redView的宽度,它的宽度就会默认撑满父视图,那宽度也能自适应了

        marginLeft: 10,// 约束2:左边距
        marginRight: 10,// 约束3:右边距
        height: 150,// 约束4:高度
    },
});

总结:

  • 可以看到,我们iOS里用Masonry布局无非也就用到widthheighttopleftbottomrightcenterXcenterY这几个属性就完成了大多数的布局。

  • 与此相对应,RN里用Flex布局+CSS布局无非也就用到widthheightmarginTopmarginLeftmarginBottommarginRightjustify-contentalign-itemsflex这几个属性就完成了大多数的布局。

  • 当然了,和iOS里一样,还有很多其它的布局属性这里都没有列出来,我们遇到的时候去一查就ok了,以上这几个属性就可以应对绝大多数的布局了。


参考博客:

阮一峰的《Flex布局:语法篇》
阮一峰的《Flex布局:实例篇》


你可能感兴趣的:(CSS布局 + Flex布局)