Android 进阶——ConstraintLayout详解攻略以及使用替代你的常规布局

引言

去年在 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 边缘约束向的参照线就可以看到效果。


这里写图片描述
<android.support.constraint.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="com.stylingandroid.scratch.MainActivity">
  <View
    android:id="@+id/imageView"
    android:layout_width="0dp"
    android:layout_height="0dp"
    android:layout_marginStart="16dp"
    android:layout_marginTop="16dp"
    app:layout_constraintDimensionRatio="h,15:9"
    app:layout_constraintEnd_toStartOf="@+id/guideline"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent" />
  <android.support.constraint.Guideline
    android:id="@+id/guideline"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    app:layout_constraintGuide_percent="0.39" />
</android.support.constraint.ConstraintLayout>

设置横纵比的属性是 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_constraintHorizo​​ntal_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 链中组件的权重。

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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="com.stylingandroid.scratch.MainActivity">
  <TextView
    android:id="@+id/textView"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginStart="16dp"
    android:layout_marginTop="16dp"
    app:layout_constraintEnd_toStartOf="@+id/textView2"
    app:layout_constraintHorizontal_chainStyle="spread"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    tools:text="TextView" />
  <TextView
    android:id="@+id/textView2"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginTop="16dp"
    app:layout_constraintEnd_toStartOf="@+id/textView3"
    app:layout_constraintStart_toEndOf="@+id/textView"
    app:layout_constraintTop_toTopOf="parent"
    tools:layout_editor_absoluteX="141dp"
    tools:text="TextView" />
  <TextView
    android:id="@+id/textView3"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginEnd="16dp"
    android:layout_marginTop="16dp"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toEndOf="@+id/textView2"
    app:layout_constraintTop_toTopOf="parent"
    tools:text="TextView" />
</android.support.constraint.ConstraintLayout>

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.htmlconstraintlayout

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容