ConstraintLayout「最全总结」

现在大家几乎都使用过ConstraintLayout,但与经常使用的布局相比,可能会面临不熟悉CL的API或者是逐渐增加的新特性,还有可能因为对CL具体行为的理解模糊而导致误用,这篇文章的目的更多的是,全面总结ConstraintLayout的用法和实际行为特性,并不断更新

基于ConstraintLayout 1.1,当前2.0版本处于测试阶段
参考文章:ConstraintLayout入门指南

为什么要引入ConstraintLayout

  • 减少布局层级:常用的布局划分成了几个不同的类型,要组合不同的功能需要通过嵌套的形式实现,比如想要按比例布局就要使用layout_weight属性,想要使用layout_weight属性就要使用LinearLayout(以下简称LL)或者TableLayout,然后你在原先同级的每个布局外再嵌套一层布局以使用layout_weight。
  • 更多的布局需求:要实现固定宽高比、百分比布局,灵活控制大小等更高阶的布局需求,原先的各类布局方式都不能很好的支持,可能需要通过Java代码,在运行中二次实现。
  • 想要通过ConstraintLayout来 优化性能可能并不明显
  • 亦或者你只是想尝试下这款Andorid官方力推的新布局,看看它有什么新特性。

引入lib:

1
2
3
4
//确保SDK Tools中已经下载了ConstraintLayout的支持库:
dependencies {
implementation 'com.android.support.constraint:constraint-layout:1.1.2'
}

开始之前,我们应该意识到在布局文件中Layout主要职责是什么,控制widgets的位置和大小。下面将从这两个方向出发,看看ConstraintLayout是怎么做的。

1. 控制位置

layout中的view都有四个方向left(start), right(end), top, bottom,通过这些方向在两个view之间建立约束来实现基础的位置控制,相对于RelativeLayout,RL中所有的属性CL中都有与之对应的项目,稍加熟悉即可完成转换:

cl - rl

相对于父布局的相对布局属性,CL的规则是:将父布局当做一个id=”parent”的对象来对待:

cl parent - rl parent

1.1 Margins

设置普通的margin:
margin

1.2 visibility 对约束的影响

特殊的情况是:连接到visibility为GONE的view时的margin,在ConstraintLayout中,当widgets被设置View.GONE时进行了特殊处理。

  • 根据布局的传递,设置为GONE的view相当于大小被设置为0(实际上是被设置成了一个点)
  • 如果他们已经跟其他的组件产生了约束关系,约束关系还是会存在,但是所有的margin都会变成0
    Visibility Behavior
  • 也可以使用goneMargin来保持预期的边距 margin gone

这种特殊的处理,可以在你临时的把view设置为GONE时,不破坏布局结构。

1.3 居中和偏移

居中的设置参考之前于RelativeLayout的对应关系,CL增加了偏移属性,使用偏移可以更好的处理屏幕大小改变时的情景。
center
bias

布局中设置:

1
2
3
4
5
6
<android.support.constraint.ConstraintLayout ...>
<Button android:id="@+id/button" ...
app:layout_constraintHorizontal_bias="0.3"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent/>
</>

1.4 圆形的位置

circle
使用圆形约束,在创建圆形的菜单时非常有用
circle menu

1
2
3
4
5
<Button android:id="@+id/buttonA" ... />
<Button android:id="@+id/buttonB" ...
app:layout_constraintCircle="@+id/buttonA"
app:layout_constraintCircleRadius="100dp"
app:layout_constraintCircleAngle="45" />

2. 控制尺寸(大小)

2.1 给ConstraintLayout自己添加大小限制

当ConstraintLayout的大小设置为WRAP_CONTENT时,可以很方便的设置自己的最大宽高
max min

2.2 Widgets的尺寸(大小)约束

和我们在常用的布局中设置宽高一样,控制Widgets的宽度和高度一共有3种方式:

  • 固定的大小,如123dp
  • WRAP_CONTENT 自适应
  • 0dp,等于MATCH_CONSTRAINT,类似于match_parent,会利用所有可用的空间。注意,不建议使用match_parent

dimension-constraints

2.3 WRAP_CONTENT 强制约束

默认情况下,设置为WRAP_CONTENT的组件不会限制他们的大小,有时会导致超出约束条件的情况,如果需要强制他们满足约束条件的话,使用下面的强制约束

1
2
app:layout_constrainedWidth="true|false"
app:layout_constrainedHeight="true|false"

2.4 MATCH_CONSTRAINT 大小控制

默认情况下,MATCH_CONSTRAINT 会利用所有可用的空间,通过下面的设置,可以自定义具体的行为

  • layout_constraintWidth_min and layout_constraintHeight_min
  • layout_constraintWidth_max and layout_constraintHeight_max
  • layout_constraintWidth_percent and layout_constraintHeight_percent
  • 使用百分比大小时,对应方向的大小必须设置为 MATCH_CONSTRAINT

2.5 使用比例

使用比例有两种情况,而且必须至少有一条边被设置为0dp:

  • 如果只有一条边被设置为0dp,设置比例时,将参考另外一条已经确定尺寸的边
  • 如果两条边都被设置为0dp,那么会在满足比例的条件下,设置为最大尺寸
  • 同样的支持直接设置layout_constraintWidth_percentlayout_constraintHeight_percent来使用百分比控制相对于parent的比例
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    <Button
    android:layout_width="wrap_content"
    android:layout_height="0dp"
    app:layout_constraintDimensionRatio="1.6" />
    <Button
    android:layout_width="wrap_content"
    android:layout_height="0dp"
    app:layout_constraintDimensionRatio="1:1" />
    <Button
    android:layout_width="0dp"
    android:layout_height="0dp"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintDimensionRatio="H,16:9"
    app:layout_constraintTop_toTopOf="parent" />

2.6 Chains

Chains能自定义在同一轴线上view的位置和大小,首先我们来创建一条链,当widgets之间在同一个方向上相互连接(相互约束)时,链就形成了。

chain

2.6.1 链的head

水平方向chain最左边的控件和垂直方向chain最顶部的控件被成为head chain。通过对head chain添加chainStyle属性,可以设置该条chain在水平或垂直方向上的chainStyle

chain head

2.6.2 Chain Style

1
2
layout_constraintHorizontal_chainStyle
layout_constraintVertical_chainStyle

chainStyle属性一共有三种:spread、spread_inside、packed。再配合其他属性,最终可以组成五种chain style:
chainStyle

其他四种chain style的设置和效果都比较简单,重点介绍下Weighted Chain

  • 默认的spread模式,每个元素获得同样的空间,如果一个元素使用了0dp,将会占用剩下的所有可用空间
  • layout_constraintHorizontal_weightlayout_constraintVertical_weight可以用来控制同样设置为0dp的控件的比例
  • Weighted Chain中的控件也允许在chain方向上使用wrap_content自适应控件宽/高,且布局时优先满足设置为wrap_content的控件,相当于优先满足设置了指定宽高的控件
  • 当设置了margin时,margin生效的同时weight的比例不变,链上的控件实际占有布局会被压缩

3. 不可见的辅助工具

Guideline

barrier

Group

4. 用代码的方式操作

ps. 选择优化类型

Package Index:
android.support.constraint
android.support.constraint.helper
android.support.constraint.motion
android.support.constraint.utils