跟我一起学ConstraintLayout (1)

2,990 阅读15分钟

ConstraintLayout简介

一种应对复杂多层嵌套或复杂依赖关系的高效布局,解决相对布局中由于目标控件隐藏导致的布局混乱而被迫嵌套布局的问题,解决权重百分比布局时LinearLayout的不足,并且提供特殊布局方式,如圆形布局、特殊叠加摆放等。 学习本布局的目标是实现不嵌套的复杂布局。 本系列学习心得,先讲解基本用法,然后逐步进行进阶实战UI效果的实现,采坑之路的心得等。

低版本用Compile,关键字。高版本Android Studio如下

implementation 'com.android.support.constraint:constraint-layout:1.1.0'

如下图方法数统计图,所以对整个项目的包大小不构成威胁,可以放心食用,入口有点甜。

image.png

目录

  1. 基础属性介绍
  2. 相对摆放
  3. 水平居中以及权重居中
  4. Margin间距失效
  5. 目标依赖约束对象隐身术
  6. 圆形布局
  7. 宽高约束
  8. Guideline
  9. 链式布局
  10. 布局分组-控制可见性

基础属性介绍

大家应该熟悉相对布局RelativeLayout,有很多belowOf,rightOf类似的属性,用于做控件之间相对定位布局。所以开篇我们先讲这个基础属性,如何在ConstraintLayout布局内,进行相对定位。

属性名 简介
layout_constraintLeft_toLeftOf 当前控件A左侧依赖于目标约束对象B的左侧,简单点 就是A左靠近B左侧对齐
layout_constraintLeft_toRightOf 当前控件A左侧依赖于目标约束对象B的右侧,简单点 就是A左靠近B右侧对齐
layout_constraintRight_toLeftOf 当前A右侧靠近B左侧对齐,接下来的属性类似...
layout_constraintRight_toRightOf
layout_constraintTop_toTopOf
layout_constraintTop_toBottomOf
layout_constraintBottom_toTopOf
layout_constraintBottom_toBottomOf
layout_constraintBaseline_toBaselineOf 文本基准线对齐
layout_constraintStart_toEndOf 当前控件A起始边与目标约束对象B尾部对齐
layout_constraintStart_toStartOf 当前控件A起始边与目标约束对象B起始边对齐,下面类似
layout_constraintEnd_toStartOf
layout_constraintEnd_toEndOf

image.png

如上所述,常见的基本属性就是这些,我们先学习这些近似相对布局的属性。理解上述属性的宗旨是,把当前控件分为上下左右四个方向,在不同的情况下根据UI图,进行相对约束申明。

常用特性介绍

1. 相对摆放

下面2个控件Button1在父布局右上角,button2只申明在button1的下方。跟相对布局一样,Android坐标默认是中心点在左上角,所以不申明水平约束,会默认让Button2在左侧。

image.png

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


    <Button
        android:id="@+id/button1"
        android:layout_width="150dp"
        android:layout_height="50dp"
        android:text="我在右上角"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"/>

    <Button
        android:id="@+id/button2"
        android:layout_width="150dp"
        android:layout_height="50dp"
        android:text="我在他下面"
       app:layout_constraintTop_toBottomOf="@id/button1"/>


</android.support.constraint.ConstraintLayout>
2. 水平居中以及权重居中

下面开始进阶使用,如何水平居中。在下图中的ConstraintLayout下,Button控件就像是橡皮泥,多重约束控制时,会被拉伸。比如view的左侧要贴住父布局左侧,view的右侧又要靠近父布局右侧,这样view只好在中间开始被向两边拉伸。这个就跟你在媳妇和公婆之间纠结一样,两边都要你表态,哈哈。但是人是有私心的,又要偏袒一方,或者一方一直处于劣势,要维护下,所以又出现bias概念。如下图,虽然是水平居中,但是button2明显偏向某一方,0-1的权重值,越大就越靠近右侧。当然你想玩得转,设置0.5,两边不偏袒(我一条狗为啥说这个)。这里说的是水平居中实现方式,至于垂直居中和父布局居中类似,依样画葫芦,自行实践。红色的TextView展示如何平铺填满宽度,设置0dp(match_constraint )代表matchParent。

<?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=".MainActivity">

    <Button
        android:id="@+id/button1"
        android:layout_width="150dp"
        android:layout_height="50dp"
        android:text="水平居中"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"/>

    <Button
        android:id="@+id/button2"
        android:layout_width="150dp"
        android:layout_height="50dp"
        android:layout_marginTop="60dp"
        android:text="水平居中后偏向某一侧"
        app:layout_constraintHorizontal_bias="0.6"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"/>

    <TextView
        android:id="@+id/button3"
        android:layout_width="0dp"
        android:layout_height="50dp"
        android:layout_marginTop="10dp"
        android:background="@color/colorAccent"
        android:gravity="center"
        android:text="水平填满"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@id/button2"/>

</android.support.constraint.ConstraintLayout>

image.png

3. Margin间距失效

在这里要注意一点,ConstraintLayout布局中,我想让Button2布局不遮挡演示的Button1,直接设置 android:layout_marginTop="60dp"是不行的。如下图,没有变更位置,控件依然遮挡Button1.

      <Button
        android:id="@+id/button2"
        android:layout_width="150dp"
        android:layout_height="50dp"
        android:layout_marginTop="60dp"
        android:text="水平居中后偏向某一侧"
        app:layout_constraintHorizontal_bias="0.6"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        />

image.png

通过上面实践发现,没有申明某一边的约束目标对象(比如这里是parent),设置对应的边的margin不起作用。比如我button1是有左侧约束对象的,此时设置300dp左间距,生效!

    <Button
        android:id="@+id/button1"
        android:layout_width="150dp"
        android:layout_height="50dp"
        android:layout_marginLeft="300dp"
        android:text="水平居中"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"/>

image.png

4. 目标依赖约束对象隐身术

在UI实现中,有很多控件是在某一些场景下才会显示,但是布局肯定又得把它写进去。然后显示时,它又要占据当前常规情况下的控件的位置,需要调整这个常规控件向左或者向右移动位置。这种目标隐身术,平常通过对这2个控件进行LinearLayout嵌套等方式去实现,很不好办。下面介绍隐身术的应对办法,goneMargin大法。

layout_goneMarginStart
layout_goneMarginEnd
layout_goneMarginLeft
layout_goneMarginTop
layout_goneMarginRight
layout_goneMarginBottom

我在布局里,要求button1在最上面,button2和button3要相对button1在它下面,当button2显示时,button3要在button2下面。此时就需要这样布局了。

<?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"
                                             android:layout_width="match_parent"
                                             android:layout_height="match_parent">

    <Button
        android:id="@+id/button1"
        android:layout_width="150dp"
        android:layout_height="50dp"
        android:text="你们跟着我"
        app:layout_constraintLeft_toLeftOf="parent"
        />

    <Button
        android:id="@+id/button2"
        android:layout_width="150dp"
        android:layout_height="50dp"
        android:layout_marginTop="100dp"
        android:text="我是2.我在他下面"
        android:visibility="visible"
        app:layout_constraintTop_toBottomOf="@id/button1"/>

    <Button
        android:id="@+id/button3"
        android:layout_width="150dp"
        android:layout_height="50dp"
        android:text="我是3.我在他下面"
        app:layout_constraintTop_toBottomOf="@id/button2"
        />
</android.support.constraint.ConstraintLayout>

image.png

接下来我再设置 android:visibility="gone",隐藏button2,神奇的一幕发生了。此时button2消失后,button2的marginTop100dp也没了,button3仍然保持button2之前对button1的依赖约束。

image.png

那么如果我还想要维持之前的100dp怎么办,设置button3的marginTop为100dp,不行,会变成下面图的样子。

这个时候就请出goneMargin了,如下图。

<?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"
                                             android:layout_width="match_parent"
                                             android:layout_height="match_parent">

    <Button
        android:id="@+id/button1"
        android:layout_width="150dp"
        android:layout_height="50dp"
        android:text="你们跟着我"
        app:layout_constraintLeft_toLeftOf="parent"
        />

    <Button
        android:id="@+id/button2"
        android:layout_width="150dp"
        android:layout_height="50dp"
        android:layout_marginTop="100dp"
        android:text="我是2.我在他下面"
        android:visibility="visible"
        app:layout_constraintTop_toBottomOf="@id/button1"/>

    <Button
        android:id="@+id/button3"
        android:layout_width="150dp"
        android:layout_height="50dp"
        app:layout_goneMarginTop="100dp"
        android:text="我是3.我在他下面"
        app:layout_constraintTop_toBottomOf="@id/button2"
        />
</android.support.constraint.ConstraintLayout>

image.png
设置button2消失后,如下图
image.png

OK,一切如我们想要的效果一样,完美。那么如果我不需要在button2消失后,保持这么多间距呢, app:layout_goneMarginTop改下即可,实际业务场景经常有这种需要,上方的布局消失后,与顶部间距有不一样的间距,此时goneMargin就发挥大作用了。不用自己代码里搞事情去实现动态区分布局。

5. 圆形布局

实现星星环绕效果的布局,一看这个效果,开发头都大了,自定义layout group?不不不,我们有新的真爱来救我们了。圆形布局大法帮你实现各种吊炸天的设计效果图。另外也可实现头像右上角角标效果。

layout_constraintCircle :依赖哪个控件进行布局
layout_constraintCircleRadius :到依赖对象中心的距离
layout_constraintCircleAngle :当前要摆放的控件应处于哪个角度(度数,从0到360)

image.png

废话不多说,show you fuck code。

<?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"
                                             android:layout_width="match_parent"
                                             android:layout_height="match_parent">


    <Button
        android:id="@+id/button1"
        android:layout_width="80dp"
        android:layout_height="80dp"
        android:text="垂直居中"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintTop_toTopOf="parent"/>

    <Button
        android:id="@+id/button2"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:text="A"
        app:layout_constraintCircle="@+id/button1"
        app:layout_constraintCircleAngle="45"
        app:layout_constraintCircleRadius="70dp"/>

    <Button
        android:id="@+id/button3"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:text="B"
        app:layout_constraintCircle="@+id/button1"
        app:layout_constraintCircleAngle="90"
        app:layout_constraintCircleRadius="70dp"/>

    <Button
        android:id="@+id/button4"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:text="C"
        app:layout_constraintCircle="@+id/button1"
        app:layout_constraintCircleAngle="135"
        app:layout_constraintCircleRadius="70dp"/>
</android.support.constraint.ConstraintLayout>

image.png

上图我们实现了A在45°方向摆放,并且靠近依赖约束的对象的中心点70dp,B和C类似,跟A间隔45°摆放,一种星星环绕效果实现好了。想想以前怎么实现,是不是泪奔。。。组织上来拯救我们了,给google递茶,递辣条!

6. 宽高约束

很多时候我们需要设置最大宽度和高度,或者最小宽度或高度。那么在ConstraintLayout里,我们将接触多种限制手段。

属性 简介
layout_constraintWidth_min和layout_constraintHeight_min 设置宽高的最小值
layout_constraintWidth_max和layout_constraintHeight_max 设置宽高的最大值
layout_constraintWidth_percent和layout_constraintHeight_percent 设置宽高的比例值,需要设置宽高layout_width、layout_height属性为MATCH_CONSTRAINT(0dp)

除了上述属性外,1.1版本开始新增加WRAP_CONTENT时的强制约束特性,下图中,由于WRAP_CONTENT时,宽高约束失效,第一个控件虽然申明了最大宽度,但是并没有起作用。但是第二个控件 app:layout_constrainedWidth="true"设置后,宽度被限制在200dp。

//属性
app:layout_constrainedWidth=”true|false”
app:layout_constrainedHeight=”true|false”

<?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"
                                             android:layout_width="match_parent"
                                             android:layout_height="match_parent">

    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="100dp"
        android:text="我的宽度被限制了,但是不起作用我的宽度被限制了,但是不起作用我的宽度被限制了,但是不起作用"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintWidth_max="200dp"/>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="100dp"
        android:maxWidth="100dp"
        android:text="宽度限制起作用了,宽度限制起作用了宽度限制起作用了宽度限制起作用了"
        app:layout_constrainedWidth="true"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@id/button1"
        app:layout_constraintWidth_max="200dp"/>


</android.support.constraint.ConstraintLayout>

image.png

此外,可以设置宽高的比例,比如16:9.下图文本显示“111”控件是16:9的效果,"H,16:9"代表约束高度,且保持宽高比16:9;根据上面的学习,宽度0dp时,设置了左边和右边的依赖约束对象为parent,所以宽度是已知的充满屏幕;利用已知的宽计算出 高度=宽度*16/9。 对于这一块,我也是有很多疑问,还没完全理解,等理解后,再对ratio进行详细解释。下面这一句是别人贴出来的。

In this case the system sets the largest dimensions the satisfies all constraints and maintains the aspect ratio specified.

<?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"
                                             android:layout_width="match_parent"
                                             android:layout_height="match_parent">

    <TextView
        android:id="@+id/tv1"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:background="@color/colorAccent"
        android:text="1111111"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintDimensionRatio="H,16:9"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        />

    <TextView
        android:id="@+id/tv2"
        android:layout_width="100dp"
        android:layout_height="0dp"
        android:background="#cccccc"
        android:gravity="center"
        android:text="22222"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintDimensionRatio="W,1:4"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        />

    <TextView
        android:layout_width="0dp"
        android:layout_height="100dp"
        android:background="@color/colorPrimary"
        android:text="4444444"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintDimensionRatio="W,1:4"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        />


    <TextView
        android:layout_width="0dp"
        android:layout_height="30dp"
        android:background="@color/colorPrimary"
        android:text="55555"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintDimensionRatio="H,1:4"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        />

    <TextView
        android:layout_width="100dp"
        android:layout_height="0dp"
        android:background="@color/colorPrimary"
        android:text="333333"
        app:layout_constraintHeight_percent="0.3"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        />

</android.support.constraint.ConstraintLayout>

image.png

7. Guideline

有些时候UI效果把界面化为几个区域,A区域会根据B区域的宽度而动态调整位置。或者有时候,某些控件要作为一个整体在同一垂直方向都居中(这些控件在同一个X坐标),但是以前的相对布局单纯设置垂直居中就会被叠加。比如下图效果,1和2控件都要在垂直居中,使用Guideline可以愉快的解决这些问题。其中的 app:layout_constraintGuide_percent="0.5"表示在垂直方向50%的位置。

<?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"
                                             android:layout_width="match_parent"
                                             android:layout_height="match_parent">


    <android.support.constraint.Guideline
        android:id="@+id/guideline"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintGuide_percent="0.5"

        />

    <TextView
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:background="@color/colorAccent"
        android:text="1111"
        app:layout_constraintBottom_toTopOf="@id/guideline"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"/>

    <TextView
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:background="@color/colorPrimary"
        android:text="2222"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@id/guideline"/>

</android.support.constraint.ConstraintLayout>
8. 链式布局

当AB控件相互之间建立依赖约束时,比如A申明右边与B的左边约束,B同样申明,形成一个回路,就产生了链AB。比如官网的下图

image.png
image.png
其中第一个申明的控件作为链头,所有链的属性在链头申明,比如chainstyle。根据官网的图,chainstyle有下面几种,默认是spread。
image.png

重点讲下权重链,类似于linearlayout的weight使用,但是比它要更强大。设置0dp(MATCH_CONSTRAINT)的链内控件将瓜分剩余可用控件,根据权重值,分割不同的大小。如下图,Button1和button2设置了0dp,因为button1设置了做间距80dp,瓜分剩余空间是在这个区域右侧。由于未设置具体权重,所以2者均分。而button3、button4、button5不一样,button5设置了50dp宽度,但是未设置权重,而butto3和button4分别设置了1、2的权重值,所以button4是button3的2倍大小。

image.png

<?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"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >

    <Button
        android:id="@+id/button1"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginLeft="80dp"
        android:text="Button1"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toLeftOf="@+id/button2"/>

    <Button
        android:id="@+id/button2"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="Button2"
        app:layout_constraintLeft_toRightOf="@+id/button1"
        app:layout_constraintRight_toRightOf="parent"/>


    <Button
        android:id="@+id/button3"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="Button3"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintHorizontal_weight="1"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toLeftOf="@+id/button4"/>

    <Button
        android:id="@+id/button4"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="Button4"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintHorizontal_weight="2"
        app:layout_constraintLeft_toRightOf="@+id/button3"
        app:layout_constraintRight_toLeftOf="@+id/button5"
        />

    <Button
        android:id="@+id/button5"
        android:layout_width="50dp"
        android:layout_height="wrap_content"
        android:text="Button5"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toRightOf="@+id/button4"
        app:layout_constraintRight_toRightOf="parent"/>

</android.support.constraint.ConstraintLayout>


9. 布局分组-控制可见性

利用constraint.Group对布局里的控件进行分组,app:constraint_referenced_ids里申明同一组的控件。 看源码其实group类继承ConstraintHelper,最终继承的view。我们通过referenced_ids属性设置的id数组最终被它存放,最终updatePreLayout方法遍历设置可见或不可见等。比如下图,我button1所在的group设置了invisible,button2保持了与button1的约束。而左下角放置的2个button都因为group设置了gone,导致隐藏了。值得注意的是,一旦被设置到某个分组group控件里,子控件本身的visibility属性就不起作用了,group设置的隐藏和可见才是最终的效果,默认group是显示。

image.png

<?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"
                                             android:layout_width="match_parent"
                                             android:layout_height="match_parent">

    <android.support.constraint.Group
        android:id="@+id/group"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:visibility="invisible"
        app:constraint_referenced_ids="button1"/>

    <android.support.constraint.Group
        android:id="@+id/group2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:visibility="gone"
        app:constraint_referenced_ids="button3,button4"/>

    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="80dp"
        android:text="Button1"
        android:visibility="gone"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toLeftOf="@+id/button2"/>

    <Button
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button2"
        app:layout_constraintLeft_toRightOf="@+id/button1"
        app:layout_constraintRight_toRightOf="parent"/>


    <Button
        android:id="@+id/button3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button3"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        />


    <Button
        android:id="@+id/button4"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button4"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toRightOf="@id/button3"
        />
</android.support.constraint.ConstraintLayout>

而看源码group,像space控件一样draw()为空,没有绘制动作,性能上没有什么负担。在上面介绍的Guideline也是同样如此,

    public void onDraw(Canvas canvas) {
    }

    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        if (this.mUseViewMeasure) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        } else {
            this.setMeasuredDimension(0, 0);
        }

    }
10. Barrier

如果A控件要同时在B和C的右侧,而B和C又长度不一定,此时相对布局无法确定A到底写谁右侧好,而只能嵌套一个层,然后A在B和C的父布局右侧,这样很不好,所以Barrier界线就是拿来做这种特殊布局的。

<?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"
                                             android:layout_width="match_parent"
                                             android:layout_height="match_parent">


    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="80dp"
        android:text="Button1-111111111111"
        app:layout_constraintLeft_toLeftOf="parent"
        />

    <Button
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="80dp"
        android:text="Button2"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/button1"/>

    <android.support.constraint.Barrier
        android:id="@+id/barrier"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:barrierDirection="end"
        app:constraint_referenced_ids="button1,button2"/>

    <Button
        android:id="@+id/button3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:text="Button3"
        app:layout_constraintLeft_toRightOf="@id/barrier"
        app:layout_constraintTop_toTopOf="parent"/>
</android.support.constraint.ConstraintLayout>

image.png

下面是在具体实践中的一种UI效果事例,仅供参考练习。整个布局只有一层,避免了嵌套过审的问题。“张三”和下面的转圈图片以及“我放学了”文本提示都是要求作为一个整体垂直居中,如果常规的布局,需要把这三个放到一个容器里一起进行居中,必然存在嵌套,但是约束布局就不需要这样。下面转圈图片消失时,我放学文本提示要自动依赖显示在左侧方形图片右侧,与张三左对齐,传统的布局也需要对我放学了文本和转圈图片进行嵌套,这样加上上面的嵌套,导致多了2层嵌套。

image.png

<?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="67dp"
    >

    <ImageView
        android:id="@+id/sdv_chat_dialog_header"
        android:layout_width="44dp"
        android:layout_height="44dp"
        android:layout_marginLeft="16dp"
        android:background="@null"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        android:src="@drawable/ic_launcher_background"
        app:layout_constraintTop_toTopOf="parent"
        />

    <TextView
        android:id="@+id/rrv_remind_unread"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="34dp"
        android:gravity="center"
        android:includeFontPadding="false"
        android:textColor="#ff4444"
        app:layout_constraintLeft_toLeftOf="@id/sdv_chat_dialog_header"
        app:layout_constraintTop_toTopOf="@id/sdv_chat_dialog_header"
        tools:text="9"/>

    <android.support.constraint.Barrier
        android:id="@+id/barrier"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:barrierDirection="end"
        app:constraint_referenced_ids="sdv_chat_dialog_header"/>

    <android.support.constraint.Guideline
        android:id="@+id/guideline_horizontal_center"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintGuide_percent="0.5"
        />

    <TextView
        android:id="@+id/tv_chat_dialog_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="11dp"
        android:includeFontPadding="false"
        android:singleLine="true"
        android:textColor="#000000"
        android:textSize="17sp"
        app:layout_constraintBottom_toTopOf="@id/guideline_horizontal_center"
        app:layout_constraintLeft_toRightOf="@id/barrier"
        tools:text="张三"/>

    <TextView
        android:id="@+id/tv_chat_dialog_update_time"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginRight="16dp"
        android:includeFontPadding="false"
        android:textSize="12sp"
        app:layout_constraintBaseline_toBaselineOf="@id/tv_chat_dialog_name"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:text="18:05"/>


    <ImageView
        android:id="@+id/iv_chat_watch_sync"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="11dp"
        android:layout_marginRight="5dp"
        android:layout_marginTop="2dp"
        android:background="@mipmap/ic_synchronization"
        android:visibility="visible"
        app:layout_constraintLeft_toRightOf="@id/barrier"
        app:layout_constraintTop_toBottomOf="@id/guideline_horizontal_center"
        />

    <TextView
        android:id="@+id/tv_chat_dialog_last_msg_content"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="2dp"
        android:includeFontPadding="false"
        android:singleLine="true"
        android:textColor="#000000"
        android:textSize="13sp"
        app:layout_constraintLeft_toRightOf="@id/iv_chat_watch_sync"
        app:layout_constraintRight_toLeftOf="@id/tv_chat_dialog_update_time"
        app:layout_constraintTop_toBottomOf="@id/guideline_horizontal_center"
        app:layout_goneMarginLeft="11dp"
        tools:text="我放学了,我放学了,我放学了,我放学了,我放学了,我放学了,我放学了,我放学了,我放学了,我放学了,我放学了8888888"/>


    <View
        android:id="@+id/view_divider"
        android:layout_width="match_parent"
        android:layout_height="1px"
        android:layout_marginLeft="72dp"
        android:background="#cccccc"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"/>

</android.support.constraint.ConstraintLayout>

结尾

约束布局也不是一定要完全替代传统布局,合适的才是最好的。充分利用各种布局的特性满足UI效果,才是最好的编码方式。 到这里,一些常见的使用已经具备了,剩下的就是实战磨合API,还有一些进阶配合使用下一次再讲。 比如ConstraintSet,在代码里动态改变约束,做一些布局动画。代码就不用贴出来,都在上面。大家可以在拷贝后,不断修改以便熟悉API。