无比强大的ViewGroup——ConstraintLayout

ConstraintLayout是一种可以灵活的控制view大小和权重的view容器。

支持Android Api 9以上

目前它有以下的约束方式:

  1. Relative positioning相对位置约束
  2. Margins间距约束
  3. Centering positioning 中心约束
  4. Circular positioning 圆形约束
  5. Dimension constraints 尺寸约束
  6. Chains 链式约束
  7. Virtual Helpers objects 虚拟帮助对象
  8. Optimizer 优化

Relative positioning

相对位置约束是指布局中view相对于其它view的位置来设置自己的位置。分为纵向和横向两种:

  • 横向: left, right, start, end

    一般情况下,View开始部分就是左边,但是有的语言目前为止还是按照从右往左的顺序来书写的,例如阿拉伯语,在Android 4.2系统之后,Google在Android中引入了RTL布局,更好了支持了由右到左文字布局的显示,为了更好的兼容RTL布局,google推荐使用MarginStart和MarginEnd来替代MarginLeft和MarginRight,这样应用可以在正常的屏幕和由右到左显示文字的屏幕上都保持一致的用户体验。

  • 纵向:top, bottom, text baseline

下面举个示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<include
layout="@layout/view_title_bar"
android:id="@+id/view_title"/>
<TextView
android:id="@+id/tv_one"
android:text="ViewA"
······
app:layout_constraintTop_toBottomOf="@id/view_title"
······
/>
<TextView
android:text="ViewB"
······
app:layout_constraintTop_toBottomOf="@id/tv_one"
······
/>
</android.support.constraint.ConstraintLayout>

得到的效果如下图:

ConstraintLayout

除此之外,它还有一下的几种相对约束条件:

  • 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

Margins

同其它ViewGroup一样,它也有Margins约束条件,不同的是当参照控件可见性为GONE的时候又多了一些其它的属性。举个例子,当A控件 约束 在B控件的左边,B控件GONE了,此时A会额外拥有一个margin的能力,来“补充”B消失的导致的“位移”。

ViewB GONE 之前

ViewB GONE 之后

下面是代码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<TextView
······
android:id="@+id/tv_B"
android:layout_width="100dp"
android:text="ViewB"
app:layout_constraintLeft_toRightOf="parent"
android:visibility="visible"
/>
<TextView
······
android:id="@+id/tv_D"
android:text="ViewD"
app:layout_goneMarginLeft="120dp"
android:layout_marginLeft="20dp"
app:layout_constraintLeft_toRightOf="@id/tv_B"
/>

下面罗列一下它的约束条件:

  • android:layout_marginStart
  • android:layout_marginEnd
  • android:layout_marginLeft
  • android:layout_marginTop
  • android:layout_marginRight
  • android:layout_marginBottom
  • layout_goneMarginStart
  • layout_goneMarginEnd
  • layout_goneMarginLeft
  • layout_goneMarginTop
  • layout_goneMarginRight
  • layout_goneMarginBottom

Centering positioning and bias

我们如何使控件居中呢?

这里我们先来看看如何让控件在容器中水平居中。

1
2
3
4
5
6
<TextView
android:id="@+id/tv_A"
······
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
······ />

是就是这么奇妙,这样控件就在容器中水平居中了,垂直居中同理。那么如果是两个控件中间且居中呢?

直接上代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<TextView
android:id="@+id/tv_B"
android:text="ViewB"
······/>
<TextView
android:id="@+id/tv_C"
android:text="ViewC"
······
app:layout_constraintRight_toLeftOf="parent"
/>
<TextView
android:id="@+id/tv_D"
android:text="ViewD"
······
app:layout_constraintLeft_toLeftOf="@id/tv_B"
app:layout_constraintRiht_toRightOf="@id/tv_C"
/>

只要使得ViewD分别于ViewB和ViewC产生依赖,只要就居中了。

控件就某一方向不居中的情况

比如使得ViewA距左边30%而不是50%

1
2
3
4
5
6
7
<TextView
android:id="@+id/tv_A"
······
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintHorizontal_bias="0.3"
······ />

当屏幕的尺寸变化时,这种方式能很好的兼容。

Circular positioning (Added in 1.1)

在1.1版本中添加了,圆形约束条件。下面是其相关的属性:

  • layout_constraintCircle : 参照widget的id
  • layout_constraintCircleRadius : 中心点距离参照widget中心的距离
  • layout_constraintCircleAngle : 角度,取值范围[0,360]

ConstraintLayout

那么来看下下图中的效果是如何实现的。

ConstraintLayout

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" />

Visibility behavior

可见性约束,GONE通常来讲是不会展示在页面上的,但是在ConstraintLayout中:

  1. 相当与将控件的大小缩小成为一个点,它的宽和高约等于0
  2. 它与其它控件的依赖关系是依然存在的,但是自身的margin是几乎等于0的

Dimensions constraints

尺寸约束,同其它ViewGroup一样对控件的宽和高给出最大值和最小值。对应的属性值如下:

  • android:minWidth
  • android:minHeight
  • android:maxWidth
  • android:maxHeight

上面的属性,在控件申明为wrap_content有效

Widgets dimension constraints

控件可以通过android:layout_widthandroid:layout_height设置自己的大小,这两个属性的值三种情况:

  1. 使用具体确定的数值,或者是资源xml中定义的具体尺寸.
  2. 使用WRAP_CONTENT,会自行计算大小
  3. 使用0dp, 其效果等同于MATCH_CONSTRAINT

需要注意的是: MATCH_PARENT在ConstraintLayout不推荐使用。可以将控件的left/righttop/bottom约束于parent。

WRAP_CONTENT(1.1)

在ConstraintLayout中,仍然可以对WRAP_CONTENT的最大尺寸进行约束:

  • app:layout_constrainedWidth=”true|false”

app:layout_constrainedHeight=”true|false”
当其中一个被设置为true时,控件的最大宽高任然可以被约束链约束,需要注意的是,这样做会使布局变慢一些。

MATCH_CONSTRAINT(1.1)

当宽高被设为MATCH_CONSTRAINT,这个控件将尝试占据布局上所有可用的地方,但同时会被这些属性所限制:

  • layout_constraintWidth_min/layout_constraintHeight_min:最小宽高

layout_constraintWidth_max/layout_constraintHeight_max:最大宽高

layout_constraintWidth_percent/layout_constraintHeight_percent:宽高相对于父容器的百分比。

注意:这些属性同时可以设置为wrap;
当使用百分比尺寸的时候,应当设置宽高为MATCH_CONSTRAINT;
父容器需要设置app:layout_constraintWidth_default=”percent”或app:layout_constraintHeight_default=”percent”(在1.1-beta2以后不再必须设置)

设置比例

constraintLayout支持子控件设置其宽高比,要使该特性生效至少需要将宽高中的一个设置为0dp(MATCH_CONSTRAINT)

1
2
3
4
5
<Button
······
android:layout_width="wrap_content"
android:layout_height="0dp"
app:layout_constraintDimensionRatio="1:1" />

宽和高之比可以用下面两种方式:

  1. 一个float数值,其等于宽/高的值
  2. 使用比例,例如:“2:1”

注意:当宽高均被设为0dp时,父容器将尝试在满足所有约束条件及比例的同时,占据最大的宽高;
如果只想对某个方向设置比例,则可以在属性前面加上W或H,与比例以,隔开:

1
2
3
4
5
<Button android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintDimensionRatio="H,16:9"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent"/>

Chains

链使我们能够对一组在水平或竖直方向互相关联的控件的属性进行统一管理。成为链的条件是:一组控件他们通过一个双向的约束关系链接起来

ConstraintLayout.jpg

注意:链的属性是由一条链的头结点控制的。而头结点的定义是一条链中位于最左端的控件。

ConstraintLayout

链的Margins

ConstraintLayout支持对链添加Margin,如果这条链的控件是分散分布的,将会从已分配给链的空间中减去设置的链边距。

链的Style

链支持设置他们的Style,只需在头结点指定layout_constraintHorizontal_chainStyle或layout_constraintVertical_chainStyle。共支持五种类型:

  • CHAIN_SPREAD(默认值):链中的元素将分散分布;

  • Weighted chain:在CHAIN_SPREAD模式中,如果某些组件被设置成MATCH_CONSTRAINT,他们将会占据所有空余空间并分散分布;

  • CHAIN_SPREAD_INSIDE:类似于CHAIN_SPREAD,但链的两端不会分散;

  • CHAIN_PACKED:链中的元素将会紧密相连在一起,偏移(bias)的设定将会影响他们在容器中所处的位置

  • Weighted chains:当链处在默认的模式(CHAIN_SPREAD)且其中一个或多个元素被设置为MATCH_CONSTRAINT时,他们将平均占据剩下的空余空间;如果其中的结点同时设置了

  • layout_constraintHorizontal_weight或layout_constraintVertical_weight属性,那么他们将根据所设置的比重来分配剩下的空间。

ConstraintLayout

Virtual Helper objects

虚拟辅助类部件它们最终不会在界面上呈现出来,但可以帮助我们更好更精细地控制布局。目前。所支持的这类部件包括:

1. Guideline

Guideline可以放在竖直方向或水平方向,水平Guideline的高为0,宽度与父容器一致;竖直Guideline同理。
Guideline 具有三类特殊的属性:

  • layout_constraintGuide_begin:设置Guideline 距离父容器起始位置的距离(left或top);

  • layout_constraintGuide_end:设置Guideline 距离父容器尾部的距离(right或bottom);

  • layout_constraintGuide_percent:设置Guideline 相对于父容器宽度/高度的百分比位置。

2. Barrier(1.1)

Barrier 使用多个控件作为参考,在这些控件中,选取在特定方向最边缘的的控件创建一条Guideline。
constraint_referenced_ids用来设置要参考的控件id,多个控件id间以逗号的形式隔开。

3. Group(1.1)

Group 用于控制所引用的一组控件的可见性(Visibility),constraint_referenced_ids用来设置要参考的控件id,多个控件id间以逗号的形式隔开。

1
2
3
4
5
6
<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" />

注意:多个Group 部件可以引用相同的控件,这时Group 在xml中的定义顺序将决定这个控件最终的可见性。

Optimizer(1.1)

在1.1 版本中放出了ConstraintLayout的优化模式。可以通过设置app:layout_optimizationLevel的优化等级,有以下值:

  • none : 无优化
  • standard : 默认优化. 只优化direct和barrier
  • direct :
  • barrier :
  • chain : 优化链式
  • dimensions : 优化尺寸测量 (experimental), 减少测量度量

以上优化的属性默认是关闭的,可以同时开启多个如:app:layout_optimizationLevel="direct|barrier|chain"