引言
去年在 Google I/O 大会上Google上发布了 ConstraintLayout,使得我们在构建复杂布局的同时能够让视图层级得到精简,而且可以通过布局工具拖拽轻松实现布局,不过这必须得Android Studio 2.2以后才有这个工具,但ConstraintLayout这个类本身是兼容到Android 2.3 的,最新版的Android Studio2.3.x版本已经默认把ConstraintLayout作为布局的根节点了,所以有必要总结下,认真学习了之后才发现真的很强大,我相信你真的看了这篇文章的话也会认为的。
一、ConstraintLayout概述
ConstraintLayout约束布局和其他布局容器一样,都是继承自ViewGroup的,所以他也拥有其他布局的一些公用属性,与其他布局不同的是他是通过约束规则来实现布局的,所以他还新增了一些他特有的属性(后面再详说),虽然是在Android Studio2.2之后才有这个工具,但是向下兼容到Android版本2.3,官网中对它的描述就一句话:它允许您以灵活的方式定位和设置小部件。主要体现在可以以拖拽的方式来定义约束规则从而实现复杂的布局,当然你也可以通过原始的方式自己设置对应的属性。
二、ConstraintLayout所支持的约束类型
ConstraintLayout 的核心基础就是创建约束。约束定义了布局内两个组件之间的关系,从而控制组件的布局位置。ConstraintLayout所支持的约束类型目前一共支持7中类型的约束:相对定位Relative positioning、外边距Margins、中心定位Centering positioning、可见性Visibility behavior、具体尺寸约束Dimension constraints、链式约束Chains、虚拟助手对象Virtual Helpers objects,值得注意的是目前还未支持在约束中具有循环依赖关系,设置布局有两种方式可以手拖拽也可以在XML文件中书写,本质都是一样的,手拖拽的方式只不过是自动生成对应的属性,约束通常是以app:layout_constraintXxxxx为开头的属性。
1、相对定位Relative positioning和中心定位Centering positioning
相对定位Relative positioning和中心定位Centering positioning是将给定的子View相对于另一个子View在水平和垂直轴上约束,其实和RelativeLayout差不多。简单的理解为把其中一个子View当成参照物,另一个参照它布局,对应的属性。
- 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
2、外边距Margins和可见性Visibility behavior
在常见的布局中如果一个子View可见状态为GONE时,设置外边距的时候就自动忽略掉了,而在ConstraintLayout中,除了共有的外边距属性之后还多了特有的 属性支持GONE状态的子View。
- layout_goneMarginStart
- layout_goneMarginEnd
- layout_goneMarginLeft
- layout_goneMarginTop
- layout_goneMarginRight
- layout_goneMarginBottom
3、具体尺寸比约束Dimension constraints
我们需要创建一些固定方向比的 View 组件,最常使用固定横纵比(即View 的宽度与高度的比例 w:h )的就是当 ImageView 用于展示一些固定横纵比的图片的时候,以前可以通过创建继承于 ImageView 的子类,并通过覆写 onMeasure() 来实现固定横纵比的布局。常用的 support library 中的 PercentLayout 也提供了一些机制来结局这类横纵比问题。而ConstraintLayout 也提供了机制来专门解决这个问题,选择想要控制横纵比的 View 然后通过属性视图中修改 ratio 值来动态适应
如上图,我们设置的 View 组件有着向父组件的 start 和 top 边缘的约束,它的 end 边缘则约束向一条参考线,而 bottom 边缘则没有被约束,这个 View 的 layout_width 和 layout_height 都被设置成 match_constraint,表示他们会根据所有的约束来设置宽高。在布局阶段这个组件的宽度就被计算好了,但是它的高度好像没有被确定。然后,因为设置了宽高横纵比,高度其实也被确定了,只是宽度的一个函数输出值(在以上例子中横纵比是 15:9 )这样设置的好处就是,当宽度变化的时候,高度自动跟着变化,如下图通过移动这个 View 组件 end 边缘约束向的参照线就可以看到效果。
设置横纵比的属性是 app:layout_constraintDimensionRatio ,而这个值有两个部分组成:方向和比例值。当宽高属性被设置成 match_constraint 实际上在 XML 源码中表现是被设置成 0dp,这就像 LinearLayout 的 weight 属性一样,会在 XML 中设置为 0dp ,而实际大小会根据父组件在布局 layout 过程中的大小来决定计算出来。
4、Bias用于控制控件在水平和垂直方向在屏幕上的偏移比例
- layout_constraintHorizontal_bias
- layout_constraintVertical_bias
当为目标子View设置好横纵向的约束时(app:layout_constraintLeft_toLeftOf=”parent”、app:layout_constraintRight_toRightOf=”parent”或者app:layout_constraintTop_toTopOf=”parent”、app:layout_constraintBottom_toBottomOf=”parent”),这个两个属性才会生效。实际操作过程中,你会发现对着设置好横纵向约束的Button进行拖动,布局中的layout_constraintHorizontal_bias和layout_constraintVertical_bias会一直发生相应的变化,如果你需要Button居中,那么直接将这两个属性的参数值设置为0.5即可。
5、layout_constraintDimentionRatio设置子View的宽和高按某个比例进行布局
值得注意的是这个属性layout_constraintDimentionRatio生效的条件必须满足:目标子View的layout_width和layout_height至少有一个设置为0dp,layout_constraintDimentionRatio默认参数比例是指宽:高;而变成高:宽可以设app:layout_constraintDimensionRatio=”H,2:1”,例如 app:layout_constraintDimensionRatio="H,3:1"
6、链式约束Chains
Chain 链是一种特殊的约束让多个 chain 链连接的 Views 能够平分剩余空间位置。在 Android 传统布局特性里面最相似的应该是 LinearLayout 中的权重比 weight ,但 Chains 链能做到的远远不止权重比 weight 的功能。链式约束主要是用于设置一组子View的布局,当某一个子View设置了
layout_constraintHorizontal_chainStyle 或者layout_constraintVertical_chainStyle 属性(水平垂直方向的链式约束)
其中Weighted Chain链的默认是在可用空间中平均分配元素。如果一个或多个元素使用MATCH_CONSTRAINT,它们将使用可用的空白空间(在它们之间平分)。属性layout_constraintHorizontal_weight和layout_constraintVertical_weight将控制如何使用MATCH_CONSTRAINT在元素之间分配空间。例如,在使用MATCH_CONSTRAINT的包含两个元素的链上,第一个元素使用权重为2,第二个元素的权重为1,第一个元素占用的空间将是第二个元素的两倍。例如要把A、B、C按钮水平排成一行,可以用链式约束
这样ButtonA、B、C就在水平方向形成了一条Chain,并且底部对齐。此时ButtonA新增app:layout_constraintHorizontal_chainStyle的属性设置,这个属性在一条Chain中只会出现在第一个控件中,这个控件是整条Chain的Head
6.1、创建约束链Chain 链
Chain 链是由多个 Views 组合的,所以要创建一个 Chain 链就需要先选择多个想要链接到一起的 Views ,然后再右键选择 ‘Center Horizontally’ 或者 ‘Center Vertically’ 来创建水平链或者垂直链。
如下图创建一个水平方向的约束链:
由图可知 Chain 链两边末端的两个 View 已经存在了相对于父组件的左边缘和右边缘的约束。 Chain 链的创建定义的是 Chain 链组件之间的间隙关系,并不影响原有的非成员间的约束
Chain 链组件之间的连接类似于链条图案,而边缘两端的 View 与 父组件之间的连接类似于弹窗图案。 最外面的连接图案代表了 Chain 链的链接模式(chain mode),链接模式决定了 Chain 链如何分配组件之间的剩余空间,你可以从 Chain 链每个组件下面的 “转换 Chain 模式” 按钮来切换 Chain 链模式。
,其中 Chain 模式在水平和垂直方向上都有三大类模式分别为:spread ,spread_inside 和 packed 。
6.2、 spread 模式(Chain 链的默认模式)——它将平分间隙让多个 Views 布局到剩余空间
6.3、Spread Inside Chain 链模式——它将会把两边最边缘的两个 View 到外向父组件边缘的距离去除,然后让剩余的 Views 在剩余的空间内平分间隙布局
6.4、Spread 系的权重
spread 和 spread inside Chain 链可以设置每个组件的 weight 权重,这跟 LinearLayout 的 weight 权重设置很像。当前版本(Android Studio 2.4 alpha 7)的视图编辑器不能直接操作设置这个权重,不过我们可以通过属性视图(properties 视图)来手动设置属性。
对特定的组件设置 spread 权重,首先得选择这个 View 组件,假设该 View 是在一个水平的 Chain 链中,那么需要在属性视图(properties 视图)中设置 android:layout_width="0dp" 然后修改 app:layout_constraintHorizontal_weight="1"
View 组件在 blueprint 蓝图视图模式中的改变,它的上边和下边缘都从直线变成了类似手风琴的线条,这符号就表示了 spread 或 spread inside Chain 链模式下的被设置了权重的组件。同时要注意的是,在 packed Chain 链模式下设置权重 weight 并没有作用。就是说并不像 spread 和 spread inside 模式中表现的占据尽可能的剩余空间,在 packed 模式下该组件就会被收缩成 0 大小。
XML 中设置 Chain 链, 在 XML 中设置 Chain 链模式只需要设置好双向互补的约束即可成链,例如以下例子, 在 textView 中设置了约束属性 app:layout_constraintEndToStartOf="@+id/textView2" ,而相对的 在 textView2 也设置了约束属性 app:layout_constraintStart_toEndOf="@+id/textView" ,本质上就是 创建两个约束条件,同一对锚点但是方向相反的约束条件,这就是 Chain 链的定义方式。
另外, textView 中的约束属性 app:layout_constraintHorizontal_chainStyle="spread" 就是指定了链模式 spread 你可以通过修改成 spread inside 或 packed 来切换链模式,而且这个约束属性必须在链头,即是链组件中的第一个组件。而设置链模式的 bias 可以通过设置约束属性 app:layout_constraintHorizontal_bias="0.75" 从 0.0 - 1.0 。最后,我们就可以通过设置属性 android:layout_width="0dp" 以及 app:layout_constraintHorizontal_weight="1" 来设置 Chain 链中组件的权重。
6.5、Packed Chain 链模式——它将所有 Views 打包到一起不分配多余的间隙(当然不包括通过 margin 设置多个 Views 之间的间隙),然后将整个组件组在可用的剩余位置居中
在 packed chain 链模式,打包在一起的 Views 组可以进一步通过控制修改 bias 值来控制打包组的位置,在例子中 bias 模式是 0.5 将 Views 组居中。
7、虚拟助手对象
在ConstraintLayout中有一类对象,在运行的时候不显示任何UI效果,只是作为参照物辅助布局,GuideLine就是其中之一。GuideLine分为水平引导线和垂直引导线。
并且支持设置在屏幕中所处的位置,可以使用layout_constraintGuide_begin和layout_constraintGuide_end设置具体dp值,也可以使用layout_constraintGuide_percent来设置比例。实际上 Guideline 类其实就是一个 View,而且它不会渲染任何东西因为它实现了一个 final 的 onDraw() 而且固定了它的可见性为 View.GONE ,这就决定了运行时不会显示任何东西,而在 View 的 layout 布局过程中它会占据一个位置,而其他组件可以通过它来布局对齐。所以实际上的 Guideline 只是一个极其轻量级没有任何显示但是可以用于约束布局对齐的 View 组件,当我们以创建从 view 的一个锚点到参照线的约束 constraint 对象来根据参照线来对齐这个 view时, 参照线移动时,受约束的 view 也会跟着参照线一起移动,最后,参照线 Guideline 拥有了一个属性 app:orientation="vertical" 来描述它是一个垂直的参照线(此处也可以设置为 horizontal)。它还有属性app:layout_constraintGuide_begin="16dp" 来描述它是一个对齐父组件的 start 边缘的 16dp 偏移量处。再次提醒的是,应该用 start 边缘而不是 left 边缘。当然切换向 end 类型的话,可以使用另一个属性 app:layout_constraintGuide_end="..." ,切换为百分比类型的参照线则是设置属性 app:layout_constraintGuide_percent="0.5" 值得取值范围为 0.0 到 1.0 ,描述的是百分比偏移量。
三、ConstraintLayout的图形界面操作
1、约束手柄类型
主要有三种手柄类型: 调整手柄、 约束手柄、 基线手柄
边角上的小正方形是缩放的控制点,通过拖拉这些点就可以对 View进行缩放。但是这个大多数情况并不是很适用,因为使用这种方式进行缩放后的组件将保持固定的尺寸,而我们往往更需要 View根据具体情况响应式大小。 每条边中间的锚点就是约束锚点,我们就是用这个锚点来创建约束的。其中 而锚点里面有蓝点表示这个锚点已经存在了一个约束,反之,空心锚点则表示没有约束。
2、建立约束
首先选择一个约束手柄,并按住鼠标拖动到另外一个控件的手柄原点上,当链接线变成绿色的时候松开鼠标即可创建一个约束。
添加基线约束
居中约束
参考 http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2016/0520/4287.html和 constraintlayout