ConstraintLayout 是个功能超级强大的布局,很多复杂的布局之前需要很多层嵌套现在都可以使用一个 ConstraintLayout 来解决,很大程度上解决了布局嵌套层级过多问题,不仅如此,还提供了很多其他高级功能,对习惯用拖拽布局的同学也很友好。
其中主要包含了如下几个功能点:
- 相对定位(Relative positioning)
- 居中定位(Centering position)及偏移(bias)
- 角度定位(Circular position)
- 链表定位(Chains)
- 边距(Margins)
- 尺寸限制(Dimension constraints)
- 辅助工具:Guideline、Barrier、Group、Placeholder
- 优化器(Optimizer)
下面逐一介绍:
定位约束
相对定位(Relative positioning)
相对定位的概念类似 RelativeLayout,使用一个控件相对于另一个控件的位置来描述其在整个布局中的位置。
例如我们让按钮 B 在按钮 A 的左边,可以使用如下代码:
<Button android:id="@+id/buttonA" ... />
<Button android:id="@+id/buttonB" ...
app:layout_constraintLeft_toRightOf="@+id/buttonA" />
layout_constraintLeft_toRightOf 意为:当前按钮(B)的左边应该对其目标按钮(A)的右边。

用于控制相对定位的参数列表如下:
- layout_constraintLeft_toLeftOf
- layout_constraintLeft_toRightOf
- layout_constraintRight_toLeftOf
- layout_constraintRight_toRightOf
- layout_constraintTop_toTopOf
- layout_constraintTop_toBottomOf
- layout_constraintBottom_toTopOf
- layout_constraintBottom_toBottomOf
- layout_constraintBaseline_toBaselineOf
- layout_constraintStart_toEndOf
- layout_constraintStart_toStartOf
- layout_constraintEnd_toStartOf
- layout_constraintEnd_toEndOf
可以看到参数命名是有规则的,前缀都是 layout_constraint,后面跟着的是两个空间的方位,第一个方位意思是当前按钮的某个部位,Left 就代表当前按钮的左边,第二个方位是目标按钮的部位,Right 就是右边,所以根据参数名字我们就能猜到作用,layout_constraintLeft_toLeftOf 就是约束当前控件的左边对其目标控件的左边。
很多布局控件都有 start 及 end 相关的参数,使用起来好像跟 left 和 right 没什么区别,实际上,控件方向一般来说都是从左到右,但也有部分国家的习惯是从右到左,当控件方向是从左到右时,start 等于 left,end 等于 right;控件方向从右至左时,start 等于 right,end 等于 left。
控件的方位约束如下图所示:

其中 BaseLine 是文本的 BaseLine,例如我们如果想让两个 Button 的文本对其,但是两个 Btn 的文本大小又不同,我们可以这么设置:
<Button android:id="@+id/buttonA" ...
android:textSize="40sp"/>
<Button android:id="@+id/buttonB" ...
android:textSize="20sp"
app:layout_constraintBaseline_toBaselineOf="@+id/buttonA" />
这样在显示的时候两个文本大小不同的控件就会居中对其。
另外,我们相对定位不仅可以相对于别的控件 ID,还可以相对于父布局(parent),如果我们想让一个控件在布局的左上角,可以这么设置:
<Button android:id="@+id/buttonA" ...
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
还有一点需要注意,我们在 ConstraintLayout 中新添加一个 View 的时候可能会有个红色的警告:
this view is not constrained. it only has designtime positions so it will jump to (0 0) at runtime
这意味着你还没有给控件添加横向及竖向的约束,ConstraintLayout 默认是必须添加控件位置约束的,我们可以使用 layout_constraintLeft_toLeftOf 及 layout_constraintTop_toTopOf 约束控件的横向及竖向的位置。
居中定位(Centering position)及偏移(bias)
ConstraintLayout 中没有专门用于剧中定位的参数,但我们可以通过其他参数来实现,横向居中可以按照如下方式设置:
<Button android:id="@+id/buttonA" ...
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"/>
左边对其父布局,右边也对其父布局,也就意味着横向居中了。
竖向居中同理:
<Button android:id="@+id/buttonA" ...
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
有时候我们或许不仅仅需要设置居中,而是按照比例的靠左或靠右,ConstraintLayout 提供了两个用于设置偏移的参数:
- layout_constraintHorizontal_bias(水平偏移)
- layout_constraintVertical_bias(垂直偏移)
我们设置下面的按钮左侧有 30% 的偏移而不是默认的 50%:

使用代码来控制:
<Button android:id="@+id/button" ...
app:layout_constraintHorizontal_bias="0.3"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent/>
角度定位(Circular position)
角度定位类似相对定位,其思想是,通过对控件设置圆心、角度、半径来表达控件的位置,如果我们知道一个控件的位置,那么距离这个控件 100dp,角度为 150° 方向的位置肯定也是确定的了。

用于角度定位的参数有如下三个:
- layout_constraintCircle : 圆心的控件 ID
- layout_constraintCircleRadius : 半径,也就是该控件距离圆心控件的位置
- layout_constraintCircleAngle : 角度
<Button android:id="@+id/buttonA" ... />
<Button android:id="@+id/buttonB" ...
app:layout_constraintCircle="@+id/buttonA"
app:layout_constraintCircleRadius="100dp"
app:layout_constraintCircleAngle="45" />
链表定位(Chains)
链表定位将一组在同一条轴线上的控件描述为一组控件,并且对这些控件使用统一的约束。
我们不需要显示的将一组控件设置为链,只要其满足如下约束就可以成为一条链(下图是横向的,竖向的同理):

代码如下:
<TextView
android:id="@+id/tv_01" ...
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@id/tv_02" />
<TextView
android:id="@+id/tv_02" ...
app:layout_constraintBaseline_toBaselineOf="@id/tv_01"
app:layout_constraintLeft_toRightOf="@id/tv_01"
app:layout_constraintRight_toLeftOf="@id/tv_03" />
<TextView
android:id="@+id/tv_03" ...
app:layout_constraintBaseline_toBaselineOf="@id/tv_02"
app:layout_constraintLeft_toRightOf="@id/tv_02"
app:layout_constraintRight_toRightOf="parent" />
上面的三个控件互相约束依赖,形成一个链。
链表的第一个控件可称为链表的头(head):

我们可以在链表头中使用 layout_constraintHorizontal_chainStyle 或者 layout_constraintVertical_chainStyle 对改变链表的样式。
其支持如下四种风格:
- CHAIN_SPREAD(默认):展开元素,元素将均匀的分配到链中;
- CHAIN_SPREAD_INSIDE:类似 CHAIN_SPREAD,但两边的元素会贴着父布局;
- CHAIN_PACKED:元素会居中在一起,中间没有空隙;
- Weighted chain:权重链表,在 CHAIN_SPREAD 模式下,如果某些控件设置为 MATCH_CONSTRAINT,则它们将分割可用空间。

至于最后一种,当设置为 Packed 风格时,可以通过设置 bias 来更改控件的偏移位置。
边距(Margins)
ConstraintLayout 同样也支持 margin 设置边距,使用起来跟别的没什么区别,支持 margin 的属性如下:
- android:layout_marginStart
- android:layout_marginEnd
- android:layout_marginLeft
- android:layout_marginTop
- android:layout_marginRight
- android:layout_marginBottom
也就是几个常规的操作,就不细说了,没什么特别的。
但除此之外,还设置了另外几个 margin 参数,用于控制控件不可见后的 margin。
- layout_goneMarginStart
- layout_goneMarginEnd
- layout_goneMarginLeft
- layout_goneMarginTop
- layout_goneMarginRight
- layout_goneMarginBottom
这几个参数是指约束控件变为不可见时的 margin。

举个例子,我们把 tv_02 设置在 tv_01 的左边,并且设置了 layout_goneMarginLeft 为 20dp。
<TextView
android:id="@+id/tv_01" .../>
<TextView
android:id="@+id/tv_02" ...
app:layout_constraintLeft_toRightOf="@id/tv_01"
app:layout_goneMarginLeft="20dp" />
此时的控件布局如下:

看起来没什么区别,但当我们把 tv_01 设置为不可见时,也就是设置了 android:visibility="gone",tv_02 将距离左边距 20dp。

尺寸限制(Dimension constraints)
我们在对控件设置尺寸时可以设置固定的尺寸大小、wrap_content 以及 match_parent。
但官方不建议在 ConstraintLayout 中的子组件设置 match_parent,取而代之的是 设置为 0dp(MATCH_CONSTRAINT) 结合将左右/上下约束为父布局来实现。

例如下面代码:
<TextView
android:id="@+id/tv_01"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintLeft_toLeftOf="parent" />
还可以设置控件的宽高比,使用参数:layout_constraintDimensionRatio.
当宽高至少有一个设置为 0dp 时即可设置宽高比:
<Button android:layout_width="wrap_content"
android:layout_height="0dp"
app:layout_constraintDimensionRatio="1:1" />
这表示宽高比为 1:1.
该参数还支持在前缀添加 W 或者 H 来描述宽高限制。
- app:layout_constraintDimensionRatio="H,1:2":高:宽=1:2
- app:layout_constraintDimensionRatio="W,1:2":宽:高=1:2
辅助工具
Guideline
Guideline 是辅助线,用于辅助我们完成控件的摆放,但跟其他控件不同的是,Guideline 不会显示在页面上。
在使用时需要先指定 Guideline 的方向,使用 orientation 来设置:vertical 是垂直辅助线;horizontal 是水平辅助线。
Guideline 支持如下几个参数:
- layout_constraintGuide_begin:距离父布局左边(或顶部)的距离
- layout_constraintGuide_end:右边距离父布局(或底部)的距离
- layout_constraintGuide_percent:右边距离父布局(或底部)的百分比
其他组件可以根据辅助线来摆放位置。
<android.support.constraint.Guideline
android:id="@+id/guideline" ...
app:layout_constraintGuide_begin="100dp"
android:orientation="vertical"/>
<Button
android:id="@+id/button" ...
app:layout_constraintLeft_toLeftOf="@+id/guideline" />
Barrier
Barrier 用于将多个小组件的极端值为准则创建一个虚拟屏障,例如根据几个控件中最靠右的那个控件的右边创建一个屏障,OK,这个很难用语言准确的描述出来,直接看例子。
下面我们有两个按钮控件,长度不同,我们希望将 Barrier 设置到最长的那个控件的右边:

代码如下:
<android.support.constraint.Barrier
android:id="@+id/barrier" ...
app:barrierDirection="end"
app:constraint_referenced_ids="button1,button2" />
barrierDirection="end" 意思为将 Barrier 设置到这组控件的末尾处。
constraint_referenced_ids 中的 button1,button2 是这两个按钮的 id,按照上述设置即可。


这样,如果我们就可以把这个 Barrier 当做约束控件,其他控件可以根据他来摆放位置了。
Group
group 用来将几个控件归为一组,便于统一控制显示状态。
<android.support.constraint.Group
android:id="@+id/group"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="visible"
app:constraint_referenced_ids="button4,button9" />
constraint_referenced_ids 中引用的 id 将会被归为一组,对 group 的可见性设置将会影响到改组控件。
Placeholder
Placeholder 是占位符,在编写布局代码时可以预留几个 Placeholder 在里面,然后通过 setContent() 方法指定该占位符加载某个控件到指定位置。
优化器(Optimizer)
Constraint Layout 1.1 新增了这个优化选项,可以加快布局速度,并尝试减少一些不必要的约束,通过 app:layout_optimizationLevel 设置优化选项,共有如下几个可设置的选项:
- none : 不优化
- standard : 默认值,仅优化直接约束和 barrier 约束
- direct : 优化直接约束
- barrier : 优化 barrier 约束
- chain : 优化 chain 约束,实验性的
- dimensions : 通过尺寸计算来优化测量过程,实验性的,可能会有问题
可以结合使用:app:layout_optimizationLevel="direct|barrier|chain".
好了,上面就是这次文章的全部内容了,几乎囊括了所有使用方法,强烈建议复杂布局使用 ConstraintLayout 代替之前的多层次布局。
网友评论