ConstraintLayout解析

一,概述

   ConstraintLayout 译为 约束布局,也有人把它称作 增强型的相对布局,由 2016 年 Google I/O 推出。虽然他出来一年多了,但是一直没有时间去研究去使用。事实上并不是没有时间,而是当时粗略了解了下,感觉这个布局主要是拖控件的,感觉拖控件很low.所以一直抗拒,心里排斥他。
   最近在性能优化里面看到说用ConstraintLayout替换以前我们使用的RelativeLayoutLinearLayout,减少布局层次,所以专门研究了一下。结果不试不知道,一试吓一跳。果然很好用,xml文件清晰了很多。(而且可以在xml里面自己手动编写属性,和以前使用RelativeLayout等一样,所以内心的抗拒完全消失了。)
  扯了这么多,该进入正片时间了。

二、使用

   为了使用ConstraintLayout,我们需要先在app/build.gradle文件中添加依赖,如下所示。

dependencies {
    implementation 'com.android.support.constraint:constraint-layout:1.1.2'
}

1、相对定位 (Relative positioning)

  作用:设置控件与另外一个控件的位置。
  属性如下:

  • 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

详细说明:

Relative Positioning Constraints.png

这些属性和RelativeLayout的layout_toRightOf属性的使用方法差不多。
例1
example1.png

如上图所示:BUTTON1和BUTTON2 2个控件,添加的约束如下

<Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button1"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"/>

    <Button
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button2"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toEndOf="@+id/button"
        app:layout_constraintTop_toTopOf="parent" />

注意:添加约束的时候必须水平和竖直都要添加约束。否则xml会报如下错误```
****The view is not constrained vertically: at runtime it will jump to the left unless you add a vertical constraint.****

2、边距 Margins

作用:设置target控件与source控件的边距。
属性如下:

  • android:layout_marginStart
  • android:layout_marginEnd
  • android:layout_marginLeft
  • android:layout_marginTop
  • android:layout_marginRight
  • android:layout_marginBottom

注意:边距只能设置精确的值(包括0)和尺寸引用。
当位置约束target控件View.GONE时,可用以下margin属性指示不同的边距值:

  • layout_goneMarginStart
  • layout_goneMarginEnd
  • layout_goneMarginLeft
  • layout_goneMarginTop
  • layout_goneMarginRight
  • layout_goneMarginBottom

3、居中设置 Centering positioning

ConstraintLayout的优势是如何处理不可能的约束。

example2.png

如上图所示,BUTTON1按钮竖直水平居中。
xml文件如下:

    <Button
        android:id="@+id/button"
        android:layout_width="91dp"
        android:layout_height="wrap_content"android:text="Button1"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

说明:以上示例,除非ConstraintLayoutBUTTON1的大小完全相同,否则两个约束不会同时满足。这种情况下,约束的作用类似于相反的力牵引控件并均分边距。而控件最终会在父容器中处于居中位置。

4、偏置 Bias

默认约束会促使控件处于居中位置。此时,可以使用Bias属性来调整位置,以达到两侧边距不同的效果。
属性如下:

  • layout_constraintHorizontal_bias
  • layout_constraintVertical_bias

    example3.png

    如上图所示设置左侧边距为30%代替默认的50%。
    代码如下:
  <Button
        android:id="@+id/button"
        android:layout_width="91dp"
        android:layout_height="wrap_content"
        android:text="Button1"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.3"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        />

layout_constraintVertical_bias用法一样。

5、Visibility

ConstraintLayout的View设置visibility=View.GONE时。
如果对其他控件有约束,则仍然会被注重,但是所有边距值都将看似为0。

example4.png

如上图所示:BUTTON1设置visibility=View.GONEBUTTON1隐藏了而且magin也变成了0

注意:上图使用的边距是由BUTTON2定义的到BUTTON1的距离。有些情况下这可能不是想要设置的边距(例:BUTTON1到父容器一边的边距32dp,BUTTON2到BUTTON1边距68dp,此时设置BUTTON1为GONE,那么BUTTON2到父容器边距变成了68dp)。出于以上原因,当一个作为参考的控件设置为GONE时,可以使用替代的边距值(详看Margins部分)。

6、尺寸约束

ConstraintLayout可以定义最小宽度和高度。

  • android:minWidth布局的最小宽度
  • android:minHeight布局的最小高度
    ContraintLayout尺寸设置为WRAP_CONTENT时,将使用最小尺寸。

说明:ContraintLayout没有MATCH_PARENT。只有0dp,wrap_content,xxdp。这三种形式。0dp的效果和RelativeLayout里面的MATCH_PARENT效果一致。

7、比例 Ratio

可以定义一个控件相对于另一个控件的尺寸比例。前提是,需要至少设置一个约束的尺寸为0dp(可以2个约束都为0dp),并通过layout_constraintDimentionRatio属性设置比例。

示例:设置Button的高度与宽度相同

        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="0dp"
        android:layout_marginTop="8dp"
        android:text="button1"
        app:layout_constraintDimensionRatio="H,1:1"
        app:layout_constraintTop_toTopOf="parent" />

说明:必须要加一个约束才能起作用。如果一个约束条件都没有,设置比例没有用。

比例值设置方式:

  • float浮点值:表示宽高间的比例
  • width:height方式表示比例

如果宽高都设置为0dp,也可以使用比例Ratio。这种情况下,系统会设置最大的那个尺寸来满足所有约束,并保持指定的宽高比。根据一个有尺寸的控件来约束一个指定约束面的控件。可以预先添加WH来分别约束宽高。例:一个尺寸被两个条件约束(如:宽度0dp并在父容器中居中),那么可以通过在比例Ratio前添加WH,并用,分隔来标识哪一面应该被约束:

<Button 
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintDimensionRatio="H,16:9"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"/>

宽度满屏 高度按照16:9区分

8、链 Chains

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

chain.png

例,如下就是个链。

  <Button
        android:id="@+id/button_a"
        android:layout_width="wrap_content"
        android:layout_height="40dp"
        android:background="#888888"
        android:text="A"
        app:layout_constraintHorizontal_chainStyle="spread"
        app:layout_constraintRight_toLeftOf="@+id/button_b"
        app:layout_constraintLeft_toLeftOf="parent"
         />

    <Button
        android:id="@+id/button_b"
        android:layout_width="wrap_content"
        android:layout_height="40dp"
        android:background="#FF4081"
        android:text="B"
        app:layout_constraintLeft_toRightOf="@+id/button_a"
        app:layout_constraintRight_toLeftOf="@+id/button_c"
      />

    <Button
        android:id="@+id/button_c"
        android:layout_width="wrap_content"
        android:layout_height="40dp"
        android:background="#303F9F"
        android:text="C"
        app:layout_constraintLeft_toRightOf="@+id/button_b"
        app:layout_constraintRight_toRightOf="parent"
        />

效果如下


chain1.png

链的类型如下:
chainType.png

Spread效果(需要控件宽度或者高度为固定值或者wrap_content)

chain_spread.png

Spread Inside效果(需要控件宽度或者高度为固定值或者wrap_content)

chain_spread_inside.png

    <Button
        android:id="@+id/button_a"
        android:layout_width="wrap_content"
        android:layout_height="40dp"
        android:background="#888888"
        android:text="A"
        app:layout_constraintHorizontal_chainStyle="spread_inside"
        app:layout_constraintRight_toLeftOf="@+id/button_b"
        app:layout_constraintLeft_toLeftOf="parent"
         />

    <Button
        android:id="@+id/button_b"
        android:layout_width="wrap_content"
        android:layout_height="40dp"
        android:background="#FF4081"
        android:text="B"
        app:layout_constraintLeft_toRightOf="@+id/button_a"
        app:layout_constraintRight_toLeftOf="@+id/button_c"
      />

    <Button
        android:id="@+id/button_c"
        android:layout_width="wrap_content"
        android:layout_height="40dp"
        android:background="#303F9F"
        android:text="C"
        app:layout_constraintLeft_toRightOf="@+id/button_b"
        app:layout_constraintRight_toRightOf="parent"
        />

Weighted Chain效果(需要控件宽度或者高度设置为0dp)

chain_weighted.png

        android:id="@+id/button_a"
        android:layout_width="0dp"
        android:layout_height="40dp"
        android:background="#888888"
        android:text="A"
        app:layout_constraintHorizontal_chainStyle="spread_inside"
        app:layout_constraintRight_toLeftOf="@+id/button_b"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintHorizontal_weight="2"
         />

    <Button
        android:id="@+id/button_b"
        android:layout_width="0dp"
        android:layout_height="40dp"
        android:background="#FF4081"
        android:text="B"
        app:layout_constraintLeft_toRightOf="@+id/button_a"
        app:layout_constraintRight_toLeftOf="@+id/button_c"
        app:layout_constraintHorizontal_weight="2"
      />

    <Button
        android:id="@+id/button_c"
        android:layout_width="0dp"
        android:layout_height="40dp"
        android:background="#303F9F"
        android:text="C"
        app:layout_constraintLeft_toRightOf="@+id/button_b"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintHorizontal_weight="1"
        />

Packed Chain效果(需要控件宽度或者高度为固定值或者wrap_content)

chain_packed.png

  <Button
        android:id="@+id/button_a"
        android:layout_width="wrap_content"
        android:layout_height="40dp"
        android:background="#888888"
        android:text="A"
        app:layout_constraintHorizontal_chainStyle="packed"
        app:layout_constraintRight_toLeftOf="@+id/button_b"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintHorizontal_weight="2"
         />

    <Button
        android:id="@+id/button_b"
        android:layout_width="wrap_content"
        android:layout_height="40dp"
        android:background="#FF4081"
        android:text="B"
        app:layout_constraintLeft_toRightOf="@+id/button_a"
        app:layout_constraintRight_toLeftOf="@+id/button_c"
        app:layout_constraintHorizontal_weight="2"
      />

    <Button
        android:id="@+id/button_c"
        android:layout_width="wrap_content"
        android:layout_height="40dp"
        android:background="#303F9F"
        android:text="C"
        app:layout_constraintLeft_toRightOf="@+id/button_b"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintHorizontal_weight="1"
        />

Packed Chain with Bias效果(需要控件宽度或者高度为固定值或者wrap_content)

Chain_Packed_bias.png

        android:id="@+id/button_a"
        android:layout_width="wrap_content"
        android:layout_height="40dp"
        android:background="#888888"
        android:text="A"
        app:layout_constraintHorizontal_chainStyle="packed"
        app:layout_constraintRight_toLeftOf="@+id/button_b"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintHorizontal_bias="0.7"
         />

    <Button
        android:id="@+id/button_b"
        android:layout_width="wrap_content"
        android:layout_height="40dp"
        android:background="#FF4081"
        android:text="B"
        app:layout_constraintLeft_toRightOf="@+id/button_a"
        app:layout_constraintRight_toLeftOf="@+id/button_c"
      />

    <Button
        android:id="@+id/button_c"
        android:layout_width="wrap_content"
        android:layout_height="40dp"
        android:background="#303F9F"
        android:text="C"
        app:layout_constraintLeft_toRightOf="@+id/button_b"
        app:layout_constraintRight_toRightOf="parent"
        />

9、Guideline

GuidelineLinearLayout 类似可以设置水平或垂直方向。
android:orientation="horizontal"
android:orientation="vertical"
水平方向高度0,垂直方向宽度0
Guideline 具有以下的三种定位方式:
layout_constraintGuide_begin 距离父容器起始位置的距离(左侧或顶部)
layout_constraintGuide_end 距离父容器结束位置的距离(右侧或底部)
layout_constraintGuide_percent 距离父容器宽度或高度的百分比
例如,设置一条垂直方向距离父控件40%的Guideline

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

10、MATCH_CONSTRAINT dimensions

在约束布局中宽高的维度 match_parent0dp 代替,默认生成的大小占所有的可用空间。那么有以下几个属性可以使用:
layout_constraintWidth_minlayout_constraintHeight_min //设置最小尺寸
layout_constraintWidth_maxlayout_constraintHeight_max //设置最大尺寸
layout_constraintWidth_percentlayout_constraintHeight_percent //设置相对于父类的百分比

11、Barrier

Barrier,障碍、屏障。Barrier是一个看不见的视图,其中包含您用来形成“Barrier”的观点。如果其中一个视图增长,则Barrier将其大小调整为所引用项目的最大高度或宽度。Barrier可以是垂直或水平的,并且可以创建到引用视图的顶部、底部、左侧或右侧。在约束布局中,可以使用属性constraint_referenced_ids属性来引用多个带约束的组件,从而将它们看作一个整体,Barrier 的介入可以完成很多其他布局不能完成的功能。
例如

Barrier.png

姓名,联系方式位于左边区域,输入框在右边区域,输入框随着左边区域的变化而移动。使用传统的布局方式实现嵌套过多,布局不够优雅。那么我们一起来看看约束布局是怎么去实现的:

 <TextView
        android:id="@+id/tv_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="姓名:"
        app:layout_constraintBottom_toBottomOf="@+id/et_name"
        app:layout_constraintTop_toTopOf="@+id/et_name"
        app:layout_constraintLeft_toLeftOf="parent"
        tools:layout_editor_absoluteX="0dp" />

    <TextView
        android:id="@+id/tv_contract"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:text="联系方式:"
        app:layout_constraintBottom_toBottomOf="@+id/et_contract"
        app:layout_constraintTop_toTopOf="@+id/et_contract"
        app:layout_constraintLeft_toLeftOf="parent"
        tools:layout_editor_absoluteX="0dp" />

    <EditText
        android:id="@+id/et_name"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:hint="请输入姓名"
        app:layout_constraintLeft_toLeftOf="@+id/barrier"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"/>

    <EditText
        android:id="@+id/et_contract"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:hint="请输入联系方式"
        app:layout_constraintLeft_toLeftOf="@+id/barrier"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/et_name"/>

    <android.support.constraint.Barrier
        android:id="@+id/barrier"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:barrierDirection="right"
        app:constraint_referenced_ids="tv_name,tv_contract"/>

12、Group

Group用于控制多个控件的可见性。
app:constraint_referenced_ids绑定控件。
例如把上一个例子的联系方式和对应的输入框隐藏,只需要这样做

        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:visibility="gone"
        app:constraint_referenced_ids="tv_contract,et_contract"/>

13、WRAP_CONTENT : enforcing constraints(强制约束)

控件的宽设置为 WRAP_CONTENT (包裹内容)时,如果实际宽度超过了约束的最大宽度,那么约束会失效(高同理),为了防止约束失效,增加了以下属性:
app:layout_constrainedWidth=”true|false” //默认false
app:layout_constrainedHeight=”true|false” //默认false

写在最后

注意事项

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

推荐阅读更多精彩内容