Android FlexboxLayout 的使用

前言

FlexboxLayout 已经出生两年多了,也使用过了多次,但当再次使用的时候,总会忘记各属性的效果是怎样的,人老了果然是健忘,所以为了加深理解和以后查阅,特写此文。

官网地址:https://github.com/google/flexbox-layout

FlexboxLayout 是谷歌开源的一个布局控件,借鉴的是 CSS 的 Flexible Box 布局,跟 Android 的 LinearLayout 比较像,需要设置水平或垂直方向,但 FlexboxLayout 更强大,下面就让我们去一探究竟。

使用

导入 build.gradle

1
2
3
dependencies {
implementation 'com.google.android:flexbox:1.0.0'
}

先来个最原始的,所有属性都采用默认

布局

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.flexbox.FlexboxLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">

<TextView
android:layout_width="120dp"
android:layout_height="80dp"
android:layout_margin="1dp"
android:background="#FFCEE1"
android:text="1" />

<TextView
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_margin="1dp"
android:background="#FFCEE1"
android:text="2" />

<TextView
android:layout_width="80dp"
android:layout_height="120dp"
android:layout_margin="1dp"
android:background="#FFCEE1"
android:text="3" />

</com.google.android.flexbox.FlexboxLayout>

效果

这效果没啥可说的,下面开始对属性一一讲解

FlexboxLayout 支持的属性

flexDirection

决定主轴的方向,即 FlexboxLayout 子类的排列方向,有以下四种取值:

  • row(默认):水平方向,起点在左边
  • row_reverse:水平方向,起点在右边
  • column:垂直方向,起点在顶部
  • column:垂直方向,起点在底部

下面是效果,为了更方便的看效果,这里要先设置一个 alignItems 属性

1
app:alignItems="flex_start"

等会再解释这个属性,现在来看 flexDirection 属性各种取值的效果

flexWrap

控制 flex 容器是单行还是多行,并决定副轴(垂直主轴的方向)的起点位置,有以下三种取值:

  • nowrap(默认):单行(不换行)
  • wrap :正常换行
  • wrap_reverse:反方向换行

这里为了更方便的看效果,又得多设置一个属性 alignContent

1
app:alignContent="flex_start"

同样,等会再解释这个属性,现在我的布局这样的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.flexbox.FlexboxLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:alignContent="flex_start"
app:alignItems="flex_start">

<TextView
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_margin="1dp"
android:background="#FFCEE1"
android:text="1" />

<TextView
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_margin="1dp"
android:background="#FFCEE1"
android:text="2" />

<TextView
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_margin="1dp"
android:background="#FFCEE1"
android:text="3" />

<TextView
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_margin="1dp"
android:background="#FFCEE1"
android:text="4" />

<TextView
android:layout_width="80dp"
android:layout_height="100dp"
android:layout_margin="1dp"
android:background="#FFCEE1"
android:text="5" />

<TextView
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_margin="1dp"
android:background="#FFCEE1"
android:text="6" />


<TextView
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_margin="1dp"
android:background="#FFCEE1"
android:text="7" />

</com.google.android.flexbox.FlexboxLayout>

效果

justifyContent

控制主轴方向的对齐方式,有以下五种取值:

  • flex_start(默认):左对齐
  • flex_end:右对齐
  • center:居中
  • space_between:两端对齐,除两端外,多余空间平分中间间隔
  • space_around:两端对齐,多余空间平分到子控件两侧,相当于给子控件设置了左右 margin

效果(默认效果这里就不贴了)

alignItems

控制副轴方向的对齐方式,如果是多行的情况,效果会受到 alignContent 属性的影响, 有以下五种取值:

  • stretch(默认):如果固定高度或者 match_parent 时,子控件就会充满 FlexboxLayout 的高度,这也是为啥我们在看 flexDirection 属性效果时,需要设置此属性的原因。
  • flex_start:起点方向对齐,因为这个起点会受 flexDirection 属性影响,所以不能纯粹说顶部对齐
  • flex_end:终点方向对齐
  • center:居中
  • baseline:以每行第一个子控件的文字基线对齐

效果

alignContent

控制多行时,容器的对齐方式,单行时设置无效,和 alignItems 同时使用时,效果会相互影响,对于 alignItems 属性的部分取值,会起到覆盖作用,有以下六种取值:

  • stretch(默认):在 alignItems 设置为 stretch 时子控件充满单行高度;在 alignItems 设置为其他值时,子控件保留自身高度,但多行高度还是会充满 FlexboxLayout 的高度,这也是为啥我们在看 flexWrap 效果时需要设置此属性的原因
  • flex_start:起点方向对齐
  • flex_end:终点方向对齐
  • center:居中
  • space_between:两端(副轴)对齐,除两端外,多余空间平分中间间隔
  • space_around:两端(副轴)对齐,多余空间平分到单行上下两侧,相当于给单行设置了上下 margin

效果( alignItems 取默认值)

showDividerHorizontal

控制是否显示水平方向的分割线,值为(none | beginning | middle | end)一个或者多个

dividerDrawableHorizontal

设置水平方向的分割线

showDividerVertical

控制是否显示垂直方向的分割线,值为(none | beginning | middle | end)一个或者多个

dividerDrawableVertical

设置垂直方向的分割线

showDivider

控制是否显示分割线(包括水平和垂直方向),值为(none | beginning | middle | end)一个或者多个

dividerDrawable

设置分割线(包括水平和垂直方向),注意,如果跟 justifyContent="space_around" 或者 alignContent="space_between" 等属性值一起使用时,会看到意想不到的空间,所以如果不是想要的效果避免跟这些属性值同时使用

下面看个分割线的例子:

分割线 res/drawable/divider.xml

1
2
3
4
5
6
7
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<size
android:width="8dp"
android:height="12dp" />
<solid android:color="#44A444" />
</shape>

布局

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.flexbox.FlexboxLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:alignContent="flex_start"
app:alignItems="flex_start"
app:dividerDrawable="@drawable/divider"
app:flexWrap="wrap"
app:showDivider="beginning|middle">

<TextView
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_margin="1dp"
android:background="#FFCEE1"
android:text="1" />

<TextView
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_margin="1dp"
android:background="#FFCEE1"
android:text="2" />

<TextView
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_margin="1dp"
android:background="#FFCEE1"
android:text="3" />

<TextView
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_margin="1dp"
android:background="#FFCEE1"
android:text="4" />

<TextView
android:layout_width="80dp"
android:layout_height="100dp"
android:layout_margin="1dp"
android:background="#FFCEE1"
android:text="5" />

<TextView
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_margin="1dp"
android:background="#FFCEE1"
android:text="6" />

</com.google.android.flexbox.FlexboxLayout>

效果

FlexboxLayout 子控件支持的属性

layout_order(integer)

默认情况下子控件的排列顺序是按照布局中的顺序依次出现,或者代码中的子控件的添加顺序排列,但可以通过设置此属性控制排列顺序,取值从小到大依次排列,默认为 1

举例将第二个元素的此属性设置为 0

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.flexbox.FlexboxLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:alignContent="flex_start"
app:alignItems="flex_start">

<TextView
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_margin="1dp"
android:background="#FFCEE1"
android:text="1" />

<TextView
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_margin="1dp"
app:layout_order="0"
android:background="#FFCEE1"
android:text="2" />

<TextView
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_margin="1dp"
android:background="#FFCEE1"
android:text="3" />

</com.google.android.flexbox.FlexboxLayout>

效果

layout_flexGrow(float)

子控件的放大比例,决定如何分配剩余空间(如果有剩余空间的话),如果有多个子控件设置了此属性,则将剩余空间按比例分配给子控件,此属性类似 LinearLayoutlayout_weight 属性,如果没有设置,默认值为 0

layout_flexShrink(float)

子控件的缩小比例,当空间不足时,决定是否缩小子控件(仅在不换行的时候有效),如果设置为 0,则不对子控件进行缩小;如果设置为正数,则按比例缩小;如果不设置,默认值为 1

layout_alignSelf

给子控件设置在副轴方向上的对齐方式,和 alignItems 一样,区别在于 alignItems 作用于所有子控件,而此属性只控制单个子控件,有以下六种取值:

  • auto(默认),表示继承 alignItem 属性值
  • flex_start:起点方向对齐,
  • flex_end:终点方向对齐
  • center:居中
  • baseline:以文字基线对齐
  • stretch:充满

为了查看效果,FlexboxLayout 设置如下

1
2
3
app:alignContent="stretch"
app:alignItems="stretch"
app:flexWrap="wrap"

效果

layout_flexBasisPercent(fraction)

子控件的宽或者高在 FlexboxLayout 中的百分比,设置的是主轴方向的,比如主轴是水平方向,则控制子控件的宽度在容器内的百分比。设置此值后将覆盖 layout_width (或 layout_height ) 的值,此属性只有在 FlexboxLayout 的主轴方向的长度是固定(也就是 MeasureSpec mode 是 MeasureSpec.EXACTLY)时有效,默认值为 -1

layout_minWidth / layout_minHeight(dimension)

设置最小宽或者最小高,在 layout_flexShrink 作用时,子控件不会小于此最小值

layout_maxWidth / layout_maxHeight(dimension)

设置最大宽或者最大高,在 layout_flexGrow 作用时,子控件不会大于此最大值

layout_wrapBefore(boolean)

强制换行,flexWrap 属性设置为可换行时生效,默认为 false ,当设置为true ,则该子控件将成为新的一行的第一个元素

总结

果然亲自体验一遍印象比较深刻,总的说来这控件还是很强大的,特别是要实现热门标签这种需求简直再合适不过。google 还提供了 FlexboxLayoutManager 用于 RecyclerView 中,可以实现瀑布流的效果,在官网里头有demo,用起来很简单,基本上刚介绍的属性大部分都可以设置,区别在于增加了回收功能,官网 demo 地址:
https://github.com/google/flexbox-layout/tree/master/demo-cat-gallery

谨以此记录