一. 简介

首先,和其他的布局一样,ConstraintLayout 和其它的 布局 一样是继承于 ViewGroup。它的出现主要是为了解决布局嵌套过多的问题,以灵活的方式定位和调整 View。学过 iOS开发 的小伙伴肯定都知道在xib或者storyboard里面的拖拽布局或者说约束布局,和我们这里的很像,但其实还是有很大的区别的。

ConstraintLayout 和其他的布局一样有以下这些的属性:

① View在左上右下四个方向和其他View之间的距离,值是 dp

1
2
3
4
android:layout_marginStart (android:layout_marginLeft) 
android:layout_marginTop
android:layout_marginEnd (android:layout_marginRight)
android:layout_marginBottom

② View内部元素到View左上右下边框之间的距离,值是 dp

1
2
3
4
android:paddingStart (android:paddingLeft)
android:paddingTop
android:paddingEnd (android:paddingRight)
android:paddingBottom

③ 容器 内部的对齐方式 以及 容器相对于父容器的对齐方式

1
2
android:gravity         //设置布局管理器内组件的对齐方式
android:layout_gravity //设置布局本身相对于父视图的位置

二. ConstraintLayout

1.系统默认的布局样式

Android Studio2.3之后,创建一个layout文件,默认使用布局如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

从上面可以发现 四个属性 用来表示 View 之间的关系(此处parent可以换成其他想要与之关联的View的控件 id):

1
2
3
4
5
6
7
8
9
10
11
//View左边对齐parent左边
layout_constraintLeft_toLeftOf="parent"

//View上边对齐parent上边
layout_constraintTop_toTopOf="parent"

//View右边对齐parent右边
layout_constraintRight_toRightOf="parent"

//View下边对齐parent底部
layout_constraintBottom_toBottomOf="parent"

从上面可以发现 四个属性 用来表示 View 之间的关系(此处parent可以换成其他想要与之关联的View的控件 id):

1
2
3
4
5
6
7
8
9
10
11
//View左边对齐parent右边
layout_constraintLeft_toRightOf="parent"

//View上边对齐parent下边
layout_constraintTop_toBottomOf="parent"

//View右边对齐parent左边
layout_constraintRight_toLeftOf="parent"

//View下边对齐parent上边
layout_constraintBottom_toTopOf="parent"

模板中声明了一个TextView,且处于屏幕中间。如何做到的呢?

TextView后面设置四个属性顾名思义都指定了TextViewParent(父布局)的关系,约束布局如果不指定水平和竖直方向的百分比,默认是50%,所以会居中。

2. 设置 bisa

上面说到了系统默认约束布局的百分比是50%,那么百分比是怎么设置的呢?百分比设置后,系统是怎么计算的呢?

1
2
3
4
5
//横向的百分比
layout_constraintHorizontal_bias="0.5"

//纵向的百分比
layout_constraintVertical_bias="0.5"

我们来用小例子证实一下上面的说法:

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
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="400dp"
android:layout_height="400dp"
android:background="#cc6699"
tools:context=".MainActivity">

<View
android:layout_width="120dp"
android:layout_height="120dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"

android:background="#ff0000"
/>

<TextView
android:layout_width="100dp"
android:layout_height="100dp"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"

android:background="#334455"

app:layout_constraintHorizontal_bias="0.4"
app:layout_constraintVertical_bias="0.5"
/>

</androidx.constraintlayout.widget.ConstraintLayout>

根据我们的计算:0.4 = L / ( L + R ) = L / (400 - 100),可以得到 L = 120dp。这个值和我们用来放在TextView左边测试的View的宽度一致,证明了我们的说法。

3. 设置 precent

ConstraintLayout 子布局的宽或高设置为0dp时,可以对宽或高设置百分比,百分比的取值范围是 0.0~1.0,相对的是父容器的宽高大小。

1
2
3
4
5
//设置横方向占比来确定宽度
layout_constraintWidth_percent=""

设置竖方向占比来确定高度
layout_constraintHeight_percent=""

简单小例子:

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
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="400dp"
android:layout_height="400dp"
android:background="#cc6699"
tools:context=".MainActivity">

<TextView
android:layout_width="0dp"
android:layout_height="0dp"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"

android:background="#334455"

app:layout_constraintWidth_percent="0.1"
app:layout_constraintHeight_percent="0.9"
/>

</androidx.constraintlayout.widget.ConstraintLayout>

你会发现这个百分比只能改变宽高,不会改变相对位置,所以可以配和 bisa 使用。

4. 设置 ratio

layout_width 或者 layout_height 设置为0dp时,还可以通过 layout_constraintDimensionRatio设置宽高比例。该比例默认表示 width:height的值。

使用 layout_constraintDimensionRatio 设置宽度和高度的比值来灵活设置View的尺寸。如果想要表示高度:宽度则可以配置属性类似 h,1:9 的含义是 h:w=9:1 也可设置 w,9:1 是一样的。

简单小例子:

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
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="400dp"
android:layout_height="400dp"
android:background="#cc6699"
tools:context=".MainActivity">

<TextView
android:layout_width="0dp"
android:layout_height="0dp"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"

android:background="#334455"

app:layout_constraintWidth_percent="0.1"
app:layout_constraintDimensionRatio="h,1:9"
/>

</androidx.constraintlayout.widget.ConstraintLayout>

5. 设置 强制约束

首选请看下面的效果:

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"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="400dp"
android:layout_height="400dp"
android:background="#cc6699"
tools:context=".MainActivity">

<Button
android:id="@+id/btn1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:text="站位"
/>

<Button
android:id="@+id/btn2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintLeft_toRightOf="@id/btn1"
app:layout_constraintTop_toTopOf="@id/btn1"
app:layout_constraintRight_toRightOf="parent"
android:layout_marginTop="40dp"
android:text="测试测试测试测试测试测试测试测试测试测试测试测试"
/>

</androidx.constraintlayout.widget.ConstraintLayout>

怎么回事,不是应该右边不会超出父布局的么,我已经设置了layout_constraintRight_toRightOf="parent",这个就是设置了适应内容属性后出现的问题,此时需要强制使用约束宽度的属性(app:layout_constrainedWidth="true") ,你会发现效果正常了。

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
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="400dp"
android:layout_height="400dp"
android:background="#cc6699"
tools:context=".MainActivity">

<Button
android:id="@+id/btn1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:text="站位"
/>

<Button
android:id="@+id/btn2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintLeft_toRightOf="@id/btn1"
app:layout_constraintTop_toTopOf="@id/btn1"
app:layout_constraintRight_toRightOf="parent"
android:layout_marginTop="40dp"
android:text="测试测试测试测试测试测试测试测试测试测试测试测试"
app:layout_constrainedWidth="true"
/>

</androidx.constraintlayout.widget.ConstraintLayout>

6. 设置 控件链条

通过 layout_constraintHorizontal_chainStylelayout_constraintVertical_chainStyle 设置链式控件的样式。这个属性有点像 LinearLayout 中的 weight 属性平分布局。使用此属性,通常是权重分配不满足需求,但是又需要居中或者分配View的空间

属性效果官方示例图:

使用此属性之前,需要把你即将连成链条的View彼此之间建立关联关系,水平方向则是控件彼此左右关联,竖直方向则是上下关联,每相邻两个View之间必须紧紧关联id。即是:将一个方向上的控件形成锁链(相互依赖),默认属性是 spread

① Spread Chain

View之间平分距离,包括左右两边也要平分。

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
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="400dp"
android:layout_height="400dp"
android:background="#cc6699"
tools:context=".MainActivity">

<Button
app:layout_constraintHorizontal_chainStyle="spread"

android:id="@+id/btn1"
android:layout_width="100dp"
android:layout_height="wrap_content"

app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@id/btn2"

app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:text="左边"
/>

<Button
android:id="@+id/btn2"
android:layout_width="100dp"
android:layout_height="wrap_content"

app:layout_constraintLeft_toRightOf="@id/btn1"
app:layout_constraintRight_toLeftOf="@id/btn3"

app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:text="中间"
/>

<Button
android:id="@+id/btn3"
android:layout_width="100dp"
android:layout_height="wrap_content"

app:layout_constraintLeft_toRightOf="@id/btn2"
app:layout_constraintRight_toRightOf="parent"

app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:text="右边"
/>

</androidx.constraintlayout.widget.ConstraintLayout>

② Spread Inside Chain

View之间平分距离,左右两边不要平分。

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
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="400dp"
android:layout_height="400dp"
android:background="#cc6699"
tools:context=".MainActivity">

<Button
app:layout_constraintHorizontal_chainStyle="spread_inside"

android:id="@+id/btn1"
android:layout_width="100dp"
android:layout_height="wrap_content"

app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@id/btn2"

app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:text="左边"
/>

<Button
android:id="@+id/btn2"
android:layout_width="100dp"
android:layout_height="wrap_content"

app:layout_constraintLeft_toRightOf="@id/btn1"
app:layout_constraintRight_toLeftOf="@id/btn3"

app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:text="中间"
/>

<Button
android:id="@+id/btn3"
android:layout_width="100dp"
android:layout_height="wrap_content"

app:layout_constraintLeft_toRightOf="@id/btn2"
app:layout_constraintRight_toRightOf="parent"

app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:text="右边"
/>

</androidx.constraintlayout.widget.ConstraintLayout>

③ Packed Chain

View之间彼此靠近,且整体居中。

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
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="400dp"
android:layout_height="400dp"
android:background="#cc6699"
tools:context=".MainActivity">

<Button
app:layout_constraintHorizontal_chainStyle="packed"

android:id="@+id/btn1"
android:layout_width="100dp"
android:layout_height="wrap_content"

app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@id/btn2"

app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:text="左边"
/>

<Button
android:id="@+id/btn2"
android:layout_width="100dp"
android:layout_height="wrap_content"

app:layout_constraintLeft_toRightOf="@id/btn1"
app:layout_constraintRight_toLeftOf="@id/btn3"

app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:text="中间"
/>

<Button
android:id="@+id/btn3"
android:layout_width="100dp"
android:layout_height="wrap_content"

app:layout_constraintLeft_toRightOf="@id/btn2"
app:layout_constraintRight_toRightOf="parent"

app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:text="右边"
/>

</androidx.constraintlayout.widget.ConstraintLayout>

④ Packed Chain with Bisa

View之间彼此靠近,且整体位于设置的比例处。

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
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="400dp"
android:layout_height="400dp"
android:background="#cc6699"
tools:context=".MainActivity">

<Button
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintHorizontal_bias="0.9"

android:id="@+id/btn1"
android:layout_width="100dp"
android:layout_height="wrap_content"

app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@id/btn2"

app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:text="左边"
/>

<Button
android:id="@+id/btn2"
android:layout_width="100dp"
android:layout_height="wrap_content"

app:layout_constraintLeft_toRightOf="@id/btn1"
app:layout_constraintRight_toLeftOf="@id/btn3"

app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:text="中间"
/>

<Button
android:id="@+id/btn3"
android:layout_width="100dp"
android:layout_height="wrap_content"

app:layout_constraintLeft_toRightOf="@id/btn2"
app:layout_constraintRight_toRightOf="parent"

app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:text="右边"
/>

</androidx.constraintlayout.widget.ConstraintLayout>

参考文章

布局大杀器—ConstraintLayout