Android ConstraintLayout 使用详解

Android在遇到复杂布局的时候,避免不了嵌套布局,渲染起来也影响了应用的性能,而且在维护的时候会有些烦躁,至少我是这样,还有百分比等,这时我们的 ConstraintLayout 就是为了解决这些问题而来的,况且还兼容到API 9,更没有理由了。所以这篇用法就应运而生了,毕竟优秀的东西总是让人向往。(本文不讲解拖拽用法)

官方文档 https://developer.android.com/reference/android/support/constraint/ConstraintLayout

(一)基本属性
layout_constraintLeft_toRightOf
layout_constraintRight_toLeftOf
layout_constraintLeft_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

可以看到每个属性上有两个方向,可以看第一个就是 Left -> Right,表示当前添加到视图控件以什么相对位置进行摆放。通俗的讲就是你告诉我我以谁为基准来摆放我自己。

image.png
 <Button
    android:id="@+id/button17"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Button17"
    />
<Button
    android:id="@+id/button16"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Button16"
    app:layout_constraintLeft_toRightOf="@id/button17"
     />

以上的例子,换成刚才的说法就是,添加button16的时候,以我button16的左侧边去对应button17的右侧边。
可能有人会说那Start和End呢?相关Start和End的下面4个属性我觉得等同于左右。我查了一下说是有的语言是从右向左书写的。所以用Start和End能兼容两种形式。官方推荐使用Start和End。

layout_constraintBaseline_toBaselineOf相当于文字基准线对齐,什么是基准线呢?也就是相当于文字的下划线的位置的一条虚线。

以上的属性有一点特殊的是所有的值不仅可以设置为控件id,也就是根据id摆放,也可以对父容器进行摆放,也就是ConstraintLayout。这时候设置值为parent。

<Button
    android:id="@+id/buttonA"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Button-A"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintTop_toTopOf="parent" />
6B95D046-1223-42B5-848D-2B54AB5513FE.png

可以看到在所有的方向都指向parent,ButtonA全屏居中了。因为ButtonA的四个边相对了父容器的四个边摆放。如果四根弹簧当失去横向或者纵向的两根则变成了水平居中或者垂直居中。

再看外边距的几个属性:
android:layout_marginStart
android:layout_marginEnd
android:layout_marginLeft
android:layout_marginTop
android:layout_marginRight
android:layout_marginBottom

这些跟其他布局的外边距一样,但是如果没有约束关系,也就是链条连接,设置这个属性是无效的。原因可能是ConstraintLayout没有约束关系,就没必要用ConstraintLayout了吧,这可能是个冷知识。

继续看一个有特色的外边距:
layout_goneMarginStart
layout_goneMarginEnd
layout_goneMarginLeft
layout_goneMarginTop
layout_goneMarginRight
layout_goneMarginBottom

也就是说在约束目标控件为Gone的时候,要不要来点边距。例如我们在线性布局中,如果三个按钮按水平摆放。中间的设置为Gone后,两边的控件会贴在一起。完全隐藏了。没有设置边距平滑的机会。而这个外边距就是在目标约束组件设置为Gone的时候才会出现的边距。下面会举例子。

相关基础的ConstraintLayout用法已经介绍完了,简单的练习一下吧,写一个样例,先看一下效果图:


1745E0DD-7845-4BA7-8A47-7CB2A8734229.png

中间的是各种相对关系的例子,上面是layout_goneMarginLeft属性的例子,上面的例子是有两个Button,我隐藏了一个,本身是没有间距的,从图中可以看出来出现了间距。然后看一下布局的完整代码。

<?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/buttonA"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Button-A"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintTop_toTopOf="parent" />

<Button
    android:id="@+id/buttonB"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Button-B"
    android:layout_marginRight="20dp"
    app:layout_constraintRight_toLeftOf="@+id/buttonA"
    app:layout_constraintTop_toTopOf="@id/buttonA" />

<Button
    android:id="@+id/buttonC"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Button-C"
    android:layout_marginTop="20dp"
    app:layout_constraintTop_toBottomOf="@id/buttonA"
    app:layout_constraintLeft_toLeftOf="@id/buttonA"/>

<Button
    android:id="@+id/buttonD"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginLeft="20dp"
    android:text="Button-D"
    app:layout_constraintLeft_toRightOf="@id/buttonA"
    app:layout_constraintTop_toTopOf="@+id/buttonA"
    />

<Button
    android:id="@+id/buttonE"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Button-E"
    android:layout_marginBottom="20dp"
    app:layout_constraintBottom_toTopOf="@id/buttonA"
    app:layout_constraintLeft_toLeftOf="@id/buttonA"
    />


<!--在buttonGone1的属性设置为android:visibility="gone" ,buttonGone2的外边距layout_goneMarginLeft生效-->
<Button
    android:id="@+id/buttonGone1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:visibility="gone"
    android:text="buttonGone-1"
    />

<Button
    android:id="@+id/buttonGone2"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:layout_goneMarginLeft="80dp"
    app:layout_constraintLeft_toRightOf="@id/buttonGone1"
    android:text="buttonGone-2"
    />

</android.support.constraint.ConstraintLayout>

(二)基本特性

1,偏移(Bias)
在上文第二张图全屏居中的例子中,你可能发现了布局中有蓝色的弹簧线,他是不是很像拉力呢,像是四条弹簧将按钮平衡的拉到中心的呢,事实上也是这样的。你对父布局的边做为相对目标,而button却很小,无法被拉伸到每个边界,所以button被拉到了居中的位置,在ConstraintLayout中也用这种方式进行全屏居中,水平或垂直居中,这种作用在布局的拉力,我们是可以控制拉力的大小的,也就是我们的主角偏移,先来看两个属性。

layout_constraintHorizontal_bias
layout_constraintVertical_bias

很容易的看出是水平偏移和垂直偏移,那就试试吧。


29758665-856D-444E-84C7-03759B280254.png
<Button
    android:id="@+id/buttonA"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Button-A"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintHorizontal_bias="0.3"
    app:layout_constraintVertical_bias="0.8"
    app:layout_constraintTop_toTopOf="parent" />

横向偏移了0.3,纵向偏移了0.8,相当于按百分比偏移了控件,这种力只有设置parent才会出现吗?当然不是,可以约束到任何边上,再看一个例子:


962BB2B3-8496-48E6-A19D-564B0E265223.png

从图中可以看到,底部按钮的左边约束到父布局,右侧约束给了偏移例子的按钮右侧,这样就会出现这种平衡力,那么按钮应该在中间来平衡这个关系才对。当然了,是我做了手脚,加了偏移,横向偏移0.8 看下代码:

<Button
    android:id="@+id/button1"
    android:layout_width="201dp"
    android:layout_height="wrap_content"
    android:text="偏移例子"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintLeft_toLeftOf="parent"
    />

<Button
    android:id="@+id/button2"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="底部按钮"
    android:layout_marginTop="112dp"
    app:layout_constraintRight_toRightOf="@+id/button1"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintTop_toTopOf="@+id/button1"
    app:layout_constraintHorizontal_bias="0.8" />

也就是说你在一个范围内想摆放这个按钮,就可以根据你的相对控件,写出这种平衡力,配合偏移来调整位置。

如果你想让一个控件的中心上下对齐一个控件的一条边呢,你可以约束这个组件的左侧边和右侧边同时约束你要居中的边,看一下例子:

A830A12B-0F61-4524-B8F7-D19CBB5DD266.png

图的上方是一个常规的以控件中心居中,跟以父布局没区别,下面是我们要的,以一条边居中,看一下代码:

<Button
    android:id="@+id/button1"
    android:layout_width="201dp"
    android:layout_height="wrap_content"
    android:text="偏移例子"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintLeft_toLeftOf="parent"
    />

<Button
    android:id="@+id/button2"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="底部按钮"
    android:layout_marginTop="112dp"
    app:layout_constraintRight_toRightOf="@+id/button1"
    app:layout_constraintLeft_toLeftOf="@+id/button1"
    app:layout_constraintTop_toTopOf="@+id/button1"
     />

<Button
    android:id="@+id/button3"
    android:layout_width="201dp"
    android:layout_height="wrap_content"
    android:text="偏移例子"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintLeft_toLeftOf="parent"
    />

<Button
    android:id="@+id/button4"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="底部按钮"
    android:layout_marginTop="112dp"
    app:layout_constraintRight_toRightOf="@+id/button3"
    app:layout_constraintLeft_toRightOf="@+id/button3"
    app:layout_constraintTop_toTopOf="@+id/button3"
    />

2,圆心定位(Circular positioning)

circle1.png
circle2.png

看一个三个属性
layout_constraintCircle : references another widget id
layout_constraintCircleRadius : the distance to the other widget center
layout_constraintCircleAngle : which angle the widget should be at (in degrees, from 0 to 360)

<Button android:id="@+id/buttonA" ... />
  <Button android:id="@+id/buttonB" ...
  app:layout_constraintCircle="@+id/buttonA"
  app:layout_constraintCircleRadius="100dp"
  app:layout_constraintCircleAngle="45" />

我还没想到有哪些场景能用到。暂时上一个官方的例子在这里吧。表示我来过。

3,可见性行为(Visibility behavior)
翻译一下文档。

ConstraintLayout对控件的特定处理标记为View.GONE。

像其他的布局一样,设置为GONE的控件不会显示,也不是布局本身的一部分(即如果标记为GONE,它们的实际尺寸将不会更改)

但就布局计算而言,GONE控件仍然是其中的一部分,具有重要的区别:

1,对于布局传递,它们的尺寸将被视为零(基本上,它们将被解析为一个点)
2,如果他们对其他控件有约束,他们仍然会生效,但任何边距都会等于零

visibility-behavior (2).png

您可以暂时将窗口控件标记为GONE,而不会破坏布局,这在进行简单的布局动画时尤其有用。

注意:使用的边距将是B在连接到A时定义的边距。在某些情况下,这可能不是您想要的剩余边距(例如A在其容器侧面有100dp的边距,B只有16dp到A,A标记为已消失,B对容器的边距为16dp)。因此,您可以指定在连接到标记为已消失的窗口小部件时要使用的备用边距值(请参阅上面有关已删除边距属性的部分)。

4,尺寸约束(Dimensions constraints)

首先区分一下在ConstraintLayout 中android:layout_width和android:layout_height有什么区别

image.png
<Button
    android:id="@+id/btn1"
    android:layout_width="200dp"
    android:layout_height="wrap_content"
    android:text="200dp"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent" />

<Button
    android:id="@+id/btn2"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="wrap_content"
    app:layout_constraintTop_toBottomOf="@+id/btn1"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent" />


<Button
    android:id="@+id/btn3"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:text="0dp"
    app:layout_constraintTop_toBottomOf="@id/btn2"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent" />

可以看到主要的区别就是match_parent对应ConstraintLayout的0dp了。官方不推荐在ConstraintLayout中使用match_parent,添加相对父布局位置的约束配合0dp的width和height也能实现效果。


image.png

第一种则是设置固定大小,第二种则是0dp,第三种是0dp加上margin。

继续看一下 约束尺寸的四个属性:
android:minWidth
android:minHeight
android:maxWidth
android:maxHeight

很明显是设置宽高的最大值和最小值,当其尺寸设置为WRAP_CONTENT时,ConstraintLayout将使用这些最小和最大尺寸。

 <Button
    android:id="@+id/btn1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="有一个很长的故事~~~~~~~~~~~~"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    android:maxWidth="40dp"
    android:minWidth="3dp" />

可以看到文字应该是超过了40dp所能容纳的大小,我设置了最大只能40dp。效果图:

image.png

5,WRAP_CONTENT : 强制约束( enforcing constraints )

官方译文:
如果宽高设置为WRAP_CONTENT,则在1.1之前的版本中,它们将被视为文字宽高 - 这意味着约束不会限制生成的宽高。虽然通常这足够(并且更快),但在某些情况下,您可能希望使用WRAP_CONTENT,但仍然强制执行约束以限制生成的宽高。在这种情况下,您可以添加一个相应的属性:

app:layout_constrainedWidth=”true|false”
app:layout_constrainedHeight=”true|false”

如果设置为WRAP_CONTENT,就是如果文字的宽高改变了,约束就失效了,比如你的text里面文字太多,直接就把约束搞没了,为什么会有这样的机制呢,因为有的时候我们的需求更关心于显示的文字,看需求而定要不要这个属性,这是我的理解。

说个例子。

image.png
<Button
    android:id="@+id/btn1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="有一个的故事"
    />
<Button
    android:id="@+id/btn2"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:layout_constraintLeft_toRightOf="@id/btn1"
    app:layout_constraintRight_toRightOf="parent"
    android:text="有一个小和尚"
    />

很常规的一个相对关系,右侧按钮左边相对左侧按钮的右边,右侧按钮右边相对夫布局,如果按刚才的说法,文字增长,会使布局约束失效。


image.png
<Button
    android:id="@+id/btn1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="有一个的故事"
    />
<Button
    android:id="@+id/btn2"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:layout_constraintLeft_toRightOf="@id/btn1"
    app:layout_constraintRight_toRightOf="parent"
    android:text="有一个小和尚,有一个小和尚有一个小和尚有一个小和尚有一个小和尚有一个小和尚有一个小和尚"
    />

可以看到右侧按钮的文本增多,导致约束失效,然后我们试试强制约束的属性app:layout_constrainedHeight=”true。

image.png
 <Button
    android:id="@+id/btn1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="有一个的故事"
    />
<Button
    android:id="@+id/btn2"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:layout_constraintLeft_toRightOf="@id/btn1"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constrainedWidth="true"
    android:text="有一个小和尚,有一个小和尚有一个小和尚有一个小和尚有一个小和尚有一个小和尚有一个小和尚"
    />

我们就可以根据这个属性来约束强制保证我们的布局。

6,MATCH_CONSTRAINT (0dp)

看几个属性:

layout_constraintWidth_min and layout_constraintHeight_min :
layout_constraintWidth_max and layout_constraintHeight_max :
layout_constraintWidth_percent and layout_constraintHeight_percent :

上面两个和WRAP_CONTENT的最大最小值宽高类似,写一个一样的例子试试。

image.png
   <Button
    android:id="@+id/btn1"
    android:layout_width="0dp"
    android:layout_height="0dp"
    android:text="有一个很长的故事~~~~~~~~~~~~"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintWidth_max="40dp"
    app:layout_constraintWidth_min="3dp" />

如果不写相对关系的话是出不来的,因为它是0dp。是的,他需要相对关系的支撑。像这个宽高都为0的,四个方向都要写相对关系的。总之存在于这个布局中就要存在相对关系的。

需要注意的是不仅仅可以设置多少dp,还可以设置app:layout_constraintWidth_min="wrap",这歌wrap代表的是wrap_content。

还剩下最后一个属性layout_constraintWidth_percent and layout_constraintHeight_percent :

设置这个属性需要几个前提:

1, 宽高应设置为MATCH_CONSTRAINT(0dp)
2,默认值应设置为app app:layout_constraintWidth_default =“percent”或app:layout_constraintHeight_default =“percent”
(注意:这在1.1-beta1和1.1-beta2中是必需的,但是如果定义了percent属性,则在以下版本中不需要)
3,然后将layout_constraintWidth_percent或layout_constraintHeight_percent属性设置为0到1之间的值来定义百分值。

总结一句话,通俗的讲就是设置自己的尺寸是父容器的多少百分值,需要按上述步骤来写。

来看一下例子:

image.png
<Button
    android:id="@+id/btn1"
    android:layout_width="0dp"
    app:layout_constraintWidth_default="percent"
    app:layout_constraintWidth_percent="0.8"
    android:layout_height="wrap_content"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    android:text="有一个的故事"
    />

占用了父控件的80%,没什么好说的。

7,比率 (Ratio)

先看一下属性:
app:layout_constraintDimensionRatio

这个比率的意思是当前自身控件的横纵比,也就是宽和高的比可以通过一个比值来控制。前提是至少需要宽或者高为0dp,以这种形式设置width:height;

还有一种形式,先看一下文档是如何描述的。

如果两个尺寸都设置为MATCH_CONSTRAINT(0dp),也可以使用比率。在这种情况下,系统设置满足所有约束的最大尺寸并保持指定的纵横比。要根据另一个特定边的尺寸约束一个特定边,可以预先附加W或H,分别约束宽度或高度。例如,如果一个宽高受两个目标约束(例如,宽度为0dp并且以父对象为中心)你可以通过在比率前添加字母W(用于约束宽度)或H(用于约束高度)来指示哪一边应该被约束,用逗号分隔:

也就是说宽铺面屏幕,按16来算,高度根据16的9份来展示。

看一下例子:

image.png
<Button
    android:id="@+id/btn1"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    app:layout_constraintDimensionRatio="1:1"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintBottom_toBottomOf="parent"
    android:text="有一个的故事"
    />

<Button
    android:id="@+id/btn2"
    android:layout_width="0dp"
    android:layout_height="0dp"
    app:layout_constraintDimensionRatio="h,16:9"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    android:text="banner"
    />

7,链 (Chains)

image.png

链在单个轴(水平或垂直)上提供类似行的行为。另一个轴可以独立约束

链由链的第一个元素(链的“头部”)上设置的属性控制:

image.png

首先,我们之前讲到的都是单向的相对关系,如果互相建立相对关系,就是链,链分水平链和垂直链,无论是水平和垂直第一个控件就叫做链头,水平就是最左是链头,垂直就是最上是链头。如果给链的设置magin,则整个链都会响应这个边距,然后用剩余空间去调整这个链。

看你一下例子:

image.png
<Button
    android:id="@+id/btn1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toLeftOf="@id/btn2"
    android:layout_marginLeft="50dp"
    android:text="1"/>

<Button
    android:id="@+id/btn2"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:layout_constraintLeft_toRightOf="@+id/btn1"
    app:layout_constraintRight_toLeftOf="@+id/btn3"
    android:text="1"/>
<Button
    android:id="@+id/btn3"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:layout_constraintLeft_toRightOf="@+id/btn2"
    app:layout_constraintRight_toRightOf="parent"
    android:text="1"/>

链的几种风格 Chain Style

image.png

在链的第一个元素上设置属性layout_constraintHorizo​​ntal_chainStyle或layout_constraintVertical_chainStyle时,链的行为将根据指定的样式更改(默认为CHAIN_SPREAD)

看几个属性值:

CHAIN_SPREAD - 元素将展开(默认样式)
Weighted chain - 在CHAIN_SPREAD模式下,如果控件设置为MATCH_CONSTRAINT(0dp),它们将拆分可用空间
CHAIN_SPREAD_INSIDE - 与默认类似,但是链的端点将不会展开
CHAIN_PACKED - 链的元素将被打包在一起。然后,子项的水平或垂直偏差属性将影响打包元素的定位

通俗的讲就三种模式

1,spread

2,spread_inside

3, packed

其他的都是一种扩展用法。

手写一下上面的例子更好的理解:

image.png
 <Button
    android:id="@+id/btn1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toLeftOf="@id/btn2"
    app:layout_constraintHorizontal_chainStyle="spread"
    android:text="spread"/>

<Button
    android:id="@+id/btn2"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:layout_constraintLeft_toRightOf="@+id/btn1"
    app:layout_constraintRight_toLeftOf="@+id/btn3"
    android:text="spread"/>
<Button
    android:id="@+id/btn3"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:layout_constraintLeft_toRightOf="@+id/btn2"
    app:layout_constraintRight_toRightOf="parent"
    android:text="spread"/>


<Button
    android:id="@+id/btn4"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="spread_inside"
    app:layout_constraintHorizontal_chainStyle="spread_inside"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toLeftOf="@+id/btn5"
    app:layout_constraintTop_toBottomOf="@+id/btn1" />

<Button
    android:id="@+id/btn5"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:layout_constraintLeft_toRightOf="@+id/btn4"
    app:layout_constraintRight_toLeftOf="@+id/btn6"
    app:layout_constraintTop_toBottomOf="@+id/btn2"
    android:text="spread_inside"/>
<Button
    android:id="@+id/btn6"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:layout_constraintTop_toBottomOf="@+id/btn3"
    app:layout_constraintLeft_toRightOf="@+id/btn5"
    app:layout_constraintRight_toRightOf="parent"
    android:text="spread_inside"/>

<Button
    android:id="@+id/btn7"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:text="weight-1份"
    app:layout_constraintHorizontal_chainStyle="spread"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toLeftOf="@+id/btn8"
    app:layout_constraintHorizontal_weight="1"
    app:layout_constraintTop_toBottomOf="@+id/btn4" />

<Button
    android:id="@+id/btn8"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    app:layout_constraintLeft_toRightOf="@+id/btn7"
    app:layout_constraintHorizontal_weight="2"
    app:layout_constraintRight_toLeftOf="@+id/btn9"
    app:layout_constraintTop_toBottomOf="@+id/btn5"
    android:text="weight-2份"/>
<Button
    android:id="@+id/btn9"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:text="weight-1份"
    app:layout_constraintTop_toBottomOf="@+id/btn6"
    app:layout_constraintHorizontal_weight="1"
    app:layout_constraintLeft_toRightOf="@+id/btn8"
    app:layout_constraintRight_toRightOf="parent"
    />

<Button
    android:id="@+id/btn11"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="packed"
    app:layout_constraintHorizontal_chainStyle="packed"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toLeftOf="@+id/btn22"
    app:layout_constraintTop_toBottomOf="@+id/btn7" />

<Button
    android:id="@+id/btn22"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:layout_constraintLeft_toRightOf="@+id/btn11"
    app:layout_constraintRight_toLeftOf="@+id/btn33"
    app:layout_constraintTop_toBottomOf="@+id/btn8"
    android:text="packed"/>
<Button
    android:id="@+id/btn33"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:layout_constraintTop_toBottomOf="@+id/btn9"
    app:layout_constraintLeft_toRightOf="@+id/btn22"
    app:layout_constraintRight_toRightOf="parent"
    android:text="packed"/>

<Button
    android:id="@+id/btn44"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="packed-bias0.3"
    app:layout_constraintHorizontal_chainStyle="packed"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toLeftOf="@+id/btn55"
    app:layout_constraintTop_toBottomOf="@+id/btn11"
    app:layout_constraintHorizontal_bias="0.3"/>

<Button
    android:id="@+id/btn55"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:layout_constraintLeft_toRightOf="@+id/btn44"
    app:layout_constraintRight_toLeftOf="@+id/btn66"
    app:layout_constraintTop_toBottomOf="@+id/btn22"
    android:text="packed-bias0.3"/>
<Button
    android:id="@+id/btn66"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:layout_constraintTop_toBottomOf="@+id/btn33"
    app:layout_constraintLeft_toRightOf="@+id/btn55"
    app:layout_constraintRight_toRightOf="parent"
    android:text="packed-bias0.3"/>

然后我们就能在横向纵向的链中使用这个模式了,注意的是仅仅在链头设置就好了。

在看一段官方译文:

在水平链上,如果一个元素定义了10dp的右边距而下一个元素定义了5dp的左边距,则这两个元素之间产生的边距为15dp。

在计算链用于定位项目的剩余空间时,会同时考虑项目及其边距。剩余空间不包含边距。

8,约束线 (Guideline)

暂且就叫约束线吧,约束线就是一个虚线,让我们可以随意的去定义一条线来辅助布局,当然它在布局显示的时候是不可见的,可以水平可以垂直,通android:orientation=""来设置,值为vertical&horizontal。

看一下属性:
app:layout_constraintGuide_percent:

app:layout_constraintGuide_start:

app:layout_constraintGuide_end:

image.png
 <android.support.constraint.Guideline
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:id="@+id/guideline"
    app:layout_constraintGuide_percent="0.3"
    android:orientation="vertical"/>

<Button
    android:text="Button"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:id="@+id/button"
    app:layout_constraintLeft_toLeftOf="@+id/guideline"
    android:layout_marginTop="16dp"
    app:layout_constraintTop_toTopOf="parent" />

我设置了一个百分比的属性做为例子,start和end类似,设置具体的大小,start就是从左侧开始,end就是从右侧开始。

再看一个稍微线多一点的官方例子,


image.png
<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:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">

<Button
    android:id="@+id/button4"
    android:layout_width="wrap_content"
    android:layout_height="0dp"
    android:text="@string/button"
    android:layout_marginStart="8dp"
    app:layout_constraintLeft_toLeftOf="@+id/guideline"
    android:layout_marginLeft="8dp"
    app:layout_constraintBaseline_toBaselineOf="@+id/button5"
    app:layout_constraintRight_toLeftOf="@+id/guideline3"
    android:layout_marginRight="8dp"
    app:layout_constraintHorizontal_bias="0.9" />

<Button
    android:id="@+id/button5"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/button"
    app:layout_constraintRight_toLeftOf="@+id/guideline2"
    android:layout_marginRight="8dp"
    android:layout_marginEnd="8dp"
    app:layout_constraintLeft_toLeftOf="@+id/guideline3"
    android:layout_marginLeft="8dp"
    app:layout_constraintHorizontal_bias="0.5"
    app:layout_constraintTop_toTopOf="@+id/guideline4"
    android:layout_marginTop="16dp" />

<Button
    android:id="@+id/button6"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/button"
    app:layout_constraintRight_toLeftOf="@+id/guideline2"
    tools:layout_constraintTop_creator="1"
    tools:layout_constraintRight_creator="1"
    android:layout_marginEnd="5dp"
    android:layout_marginTop="4dp"
    app:layout_constraintTop_toTopOf="@+id/guideline5"
    android:layout_marginRight="8dp"
    android:layout_marginLeft="8dp"
    app:layout_constraintLeft_toLeftOf="@+id/guideline3"
    app:layout_constraintHorizontal_bias="0.5" />

<TextView
    android:id="@+id/textView3"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:text="@string/guidelines"
    android:textSize="30sp"
    tools:layout_constraintTop_creator="1"
    tools:layout_constraintRight_creator="1"
    app:layout_constraintRight_toRightOf="parent"
    android:layout_marginTop="16dp"
    tools:layout_constraintLeft_creator="1"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    android:layout_marginLeft="16dp"
    app:layout_constraintHorizontal_bias="0.0"
    android:layout_marginRight="16dp" />

<Button
    android:id="@+id/button7"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/button"
    tools:layout_constraintTop_creator="1"
    android:layout_marginStart="8dp"
    app:layout_constraintTop_toBottomOf="@+id/button6"
    tools:layout_constraintLeft_creator="1"
    app:layout_constraintLeft_toLeftOf="@+id/guideline"
    android:layout_marginTop="0dp" />

<Button
    android:id="@+id/button8"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/button"
    tools:layout_constraintTop_creator="1"
    android:layout_marginStart="10dp"
    android:layout_marginTop="8dp"
    tools:layout_constraintLeft_creator="1"
    app:layout_constraintLeft_toLeftOf="@+id/guideline"
    app:layout_constraintTop_toTopOf="@+id/guideline6" />

<android.support.constraint.Guideline
    android:layout_width="0dp"
    android:layout_height="0dp"
    android:id="@+id/guideline"
    android:orientation="vertical"
    app:layout_constraintGuide_percent="0.15" />

<android.support.constraint.Guideline
    android:layout_width="0dp"
    android:layout_height="0dp"
    android:id="@+id/guideline2"
    android:orientation="vertical"
    app:layout_constraintGuide_percent="0.85" />

<android.support.constraint.Guideline
    android:layout_width="0dp"
    android:layout_height="0dp"
    android:id="@+id/guideline3"
    android:orientation="vertical"
    app:layout_constraintGuide_percent="0.5" />

<android.support.constraint.Guideline
    android:layout_width="0dp"
    android:layout_height="0dp"
    android:id="@+id/guideline4"
    android:orientation="horizontal"
    app:layout_constraintGuide_percent="0.15" />

<android.support.constraint.Guideline
    android:layout_width="0dp"
    android:layout_height="0dp"
    android:id="@+id/guideline5"
    android:orientation="horizontal"
    app:layout_constraintGuide_percent="0.50097847" />

<android.support.constraint.Guideline
    android:layout_width="0dp"
    android:layout_height="0dp"
    android:id="@+id/guideline6"
    android:orientation="horizontal"
    app:layout_constraintGuide_percent="0.85" />

</android.support.constraint.ConstraintLayout>

这个属性tools:layout_constraintTop_creator="1",可以忽略。

9,屏障 (Barrier)
这个不是LOL里的屏障,只是直译过来的大家要区分开,Barrier类似 Guideline都是虚拟存在的辅助开发的视图。把多个控件看为整体确定一条约束线。Barrier之外的控件跟随Barrier的内容改变而自适应。

先看一下属性:

app:barrierDirection="start" 确定Barrier方向可选值有left, right, top, bottom, start, end
app:constraint_referenced_ids="button1,button2" 确定Barrier的控件集合。

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

上图方向为Start,在组件集合1,2的开始的地方竖起了屏障,如果是end呢?


image.png

在最宽的控件上竖起了屏障,在很多类似的场景下需要嵌套布局,这就是Barrier存在的含义吧。

如果控件尺寸发生变化,屏障将根据其方向自动移动以获得极限的控件


image.png

这种情况我并没有试出来。

10,动画 (ConstraintSet)
让布局平滑的切换。直接上一个官方的例子吧:

Screenshot_1532008110.png

Screenshot_1532008113.png

上面两张图片平滑的动画切换,

constraintset_example_big.xml

<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:id="@+id/activity_constraintset_example"
android:layout_width="match_parent"
android:layout_height="match_parent" >

<ImageView
    android:id="@+id/imageView"
    android:layout_width="0dp"
    android:layout_height="0dp"
    android:layout_marginEnd="24dp"
    android:layout_marginStart="24dp"
    android:layout_marginTop="24dp"
    android:onClick="toggleMode"
    android:scaleType="centerCrop"
    android:src="@drawable/lake"
    app:layout_constraintDimensionRatio="h,16:9"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    tools:layout_constraintLeft_creator="1"
    tools:layout_constraintRight_creator="1"
    android:contentDescription="@string/lake_tahoe_image" />

<TextView
    android:id="@+id/textView9"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/lake_tahoe_title"
    android:textSize="30sp"
    app:layout_constraintLeft_toLeftOf="@+id/imageView"
    android:layout_marginTop="8dp"
    app:layout_constraintTop_toBottomOf="@+id/imageView" />

<TextView
    android:id="@+id/textView11"
    android:layout_width="0dp"
    android:layout_height="0dp"
    android:text="@string/lake_discription"
    app:layout_constraintLeft_toLeftOf="@+id/textView9"
    android:layout_marginTop="8dp"
    app:layout_constraintTop_toBottomOf="@+id/textView9"
    app:layout_constraintRight_toRightOf="@+id/imageView"
    app:layout_constraintBottom_toBottomOf="parent"
    android:layout_marginBottom="16dp"
    tools:layout_constraintTop_creator="1"
    tools:layout_constraintBottom_creator="1"
    app:layout_constraintHorizontal_bias="0.0"
    app:layout_constraintVertical_bias="0.0" />

</android.support.constraint.ConstraintLayout>

constraintset_example_main.xml

<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:id="@+id/activity_constraintset_example"
android:layout_width="match_parent"
android:layout_height="match_parent" >

<ImageView
    android:id="@+id/imageView"
    android:layout_width="0dp"
    android:layout_height="0dp"
    android:layout_marginStart="8dp"
    android:onClick="toggleMode"
    android:scaleType="centerCrop"
    android:src="@drawable/lake"
    app:layout_constraintBottom_toBottomOf="@+id/textView9"
    app:layout_constraintDimensionRatio="w,16:9"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintTop_toTopOf="@+id/textView9"
    tools:layout_constraintBottom_creator="1"
    tools:layout_constraintTop_creator="1"
    android:contentDescription="@string/lake_tahoe_image"
    app:layout_constraintVertical_bias="0.0" />

<TextView
    android:id="@+id/textView9"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginStart="8dp"
    android:layout_marginTop="16dp"
    android:text="@string/lake_tahoe_title"
    android:textSize="30sp"
    app:layout_constraintLeft_toRightOf="@+id/imageView"
    app:layout_constraintTop_toTopOf="parent" />

<TextView
    android:id="@+id/textView11"
    android:layout_width="0dp"
    android:layout_height="0dp"
    android:layout_marginBottom="8dp"
    android:layout_marginEnd="8dp"
    android:layout_marginStart="8dp"
    android:layout_marginTop="24dp"
    android:text="@string/lake_discription"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintTop_toBottomOf="@+id/imageView"
    tools:layout_constraintBottom_creator="1"
    tools:layout_constraintLeft_creator="1"
    tools:layout_constraintRight_creator="1"
    tools:layout_constraintTop_creator="1" />

</android.support.constraint.ConstraintLayout>

java

public class ConstraintSetExampleActivity extends AppCompatActivity {

private static final String SHOW_BIG_IMAGE = "showBigImage";

/**
 * Whether to show an enlarged image
 */
private boolean mShowBigImage = false;
/**
 * The ConstraintLayout that any changes are applied to.
 */
private ConstraintLayout mRootLayout;
/**
 * The ConstraintSet to use for the normal initial state
 */
private ConstraintSet mConstraintSetNormal = new ConstraintSet();
/**
 * ConstraintSet to be applied on the normal ConstraintLayout to make the Image bigger.
 */
private ConstraintSet mConstraintSetBig = new ConstraintSet();

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.constraintset_example_main);

    mRootLayout = (ConstraintLayout) findViewById(R.id.activity_constraintset_example);
    // Note that this can also be achieved by calling
    // `mConstraintSetNormal.load(this, R.layout.constraintset_example_main);`
    // Since we already have an inflated ConstraintLayout in `mRootLayout`, clone() is
    // faster and considered the best practice.
    mConstraintSetNormal.clone(mRootLayout);
    // Load the constraints from the layout where ImageView is enlarged.
    mConstraintSetBig.load(this, R.layout.constraintset_example_big);

    if (savedInstanceState != null) {
        boolean previous = savedInstanceState.getBoolean(SHOW_BIG_IMAGE);
        if (previous != mShowBigImage) {
            mShowBigImage = previous;
            applyConfig();
        }
    }
}

@Override
public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putBoolean(SHOW_BIG_IMAGE, mShowBigImage);
}

// Method called when the ImageView within R.layout.constraintset_example_main
// is clicked.
public void toggleMode(View v) {
    TransitionManager.beginDelayedTransition(mRootLayout);
    mShowBigImage = !mShowBigImage;
    applyConfig();
}

private void applyConfig() {
    if (mShowBigImage) {
        mConstraintSetBig.applyTo(mRootLayout);
    } else {
        mConstraintSetNormal.applyTo(mRootLayout);
    }
}
}

11,运动布局 (motionlayout)

ConstraintLayout子类,很炫酷,可以让布局动起来,有兴趣的自行了解吧。

谷歌官方Demo(包括ConstraintLayout和MotionLayout)
https://github.com/googlesamples/android-ConstraintLayoutExamples

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

推荐阅读更多精彩内容