ConstraintLayout属性详解和Chain的使用

来源:互联网 发布:网络直播 经济效益 编辑:程序博客网 时间:2024/06/17 03:18

今日科技快讯

近日有网友爆料:在百度地图搜索"深圳市儿童医院",搜索结果却将莆田系的远东妇儿医院的地址链接到了下面,而且放到第二位,导致许多家长被误导。对此百度地图官微称:百度地图的标注是完全免费的,不存在商业售卖标注。本次事件为我们的疏忽,返回了错误的结果,已经第一时间修正了。

作者简介

早上好!新的一周开始啦,很高兴又跟大家见面了!

本篇是老司机 张旭童 的第九篇投稿了!细致地分析了ConstraintLayout,同时对我之前有关ConstraintLayout文章中没详细说的chains部分进行了补充,希望对大家有所帮助!

张旭童 的博客地址:

http://blog.csdn.net/zxt0601

概述

说实话这篇文章写的算是比较晚了,距离ConstraintLayout出现至今已经有一年了。

且自AS2.3起创建新的Activity,默认的layout根布局就是ConstraintLayout。所以再不学习就真的晚了。我也是正式开始学习的道路,先说一下我的学习过程:

  • 先阅读了:

ConstraintLayout官方文档

https://developer.android.com/reference/android/support/constraint/Guideline.html

Guideline官方文档 

https://developer.android.com/reference/android/support/constraint/Guideline.html

  • 实践每个属性并记下笔记(翻译)

  • 学习了郭神 关于ConstraintLayout可视化操作(点击可查看)的博客,发现博客中对Chain的概念没有提及

  • 查询关于Chain以及一些疑点的资料

  • 整理成文(当然中间也遇到了许许多多的问题)

使用前的准备

引入也有坑,无力吐槽。先放上截止至20170524,最新版本1.0.1

compile 'com.android.support.constraint:constraint-layout:1.0.1'

坑是啥?因为我使用的是最新的 release版 AndroidStudio2.3.2,新建 Activity 后,自动帮我引入的是 1.0.8-alpha版本

开始我就这么愉快的学习了,可是当我学习到 Chain 相关姿势时,特码的,他居然报错。说找不到属性:

ok,那我百度,显然搜不到的,ok,那我再 google,特么的居然也搜不到。

震惊,于是机智的我去看源码,发现我使用的1.0.8-alpha版本的源码里根本没有Chain相关属性的支持,所以我就觉得一定是引入的版本有问题,于是我用 google 搜索 ”ConstraintLayout last version”,发现诶~官方有说最新版链接如下:

http://tools.android.com/recent/constraintlayout102isnowavailable

按照这个链接提示,最新版是 1.0.2,嗯哼,当我换成 1.0.2 后,发现无法 download….

不知道是网络问题还是什么问题,提示我无法下载,具体的错误记不清了。反正就是无法获取到这个版本。

特么的机智的我又直接去AndroidStudio的Library Dependency里去搜索,发现居然搜不到“ConstraintLayout “的库。再次懵逼。

后来我进行最后的一次尝试,因为我看google官方上1.0.2版本的上一个版本是1.0.1.于是我修改版本号,sync gradle,居然成功了。

总结踩坑历程:

1. 最新 Release版 AndroidStudio模板自带的是 1.0.8alpha版 ConstraintLayout

2. 使用 Chain 相关属性报错

3. 发现该版本源码没有 Chain 相关属性

4. 官网说的最新版 1.0.2 我无法下载

5. AndroidStudio 自带的 Library Dependency 搜不到 ConstraintLayout

6. 修改版本号为 1.0.1 下载

对此,我只能说“惊不惊喜! 意不意外!” 

ConstraintLayout是什么

先概况一下,它是一个为了解决布局嵌套和模仿前端 flexible 布局的一个新布局

从字面上理解,ConstraintLayout 是约束布局。在我理解,这是一个 RelativeLayout 的升级版。

而当初推出 RelativeLayout 的目的是为了在减少多层嵌套布局,推出 ConstraintLayout 也是同样的目的,尽可能的使布局 宽而短,而不是 窄而长。而 ConstraintLayout 更加强大,很多需要多层嵌套的布局,使用 ConstraintLayout 只需要一层即可解决。

它的 Chain 几种 style 方式,和前端的 flexbox 布局风格一致,官方文档中也说了它是 flexible 方式布局控件的东西。

A ConstraintLayout is a ViewGroup which allows you to position and size widgets in a flexible way.

而且搭配可视化的操作,使得布局也变得更轻松。

Google 官方推荐所有操作都在 ”Design” 区域搞定,即通过可视化拖拖拽拽生成布局大致的样子,然后针对具体属性、约束 精细修改。

甚至可以这么说,你完全不需要知道 ConstraintLayout 的具体属性值分别是什么,只通过拖拽和鼠标点击就可以实现一些布局。

那么本文的意义何在呢?

我觉得首先是要知其然知其所以然,那些拖拽点击生成的代码属性到底是什么意思?通过本文可以了解。

另外 虽然大部分操作可以在“Design”区域完成,但是保不齐的需要你切换至“Text”区域,写上一两行属性代码,了解 这些属性 总是有益无害的。

而且,有一些属性是无法简单通过拖拽点击完成的,例如 Margins when connected to a GONE widget。

刚才提到 RelativeLayout,其实 RelativeLayout 也是通过约束来布局 子View 的呀。以前 RelativeLayout 的约束有两种:

1. 子控件和子控件之间的约束(如 android:layout_below="@id/title"

2. 子控件和父控件的约束(如 android:layout_alignParentTop="true")

现在 ConstraintLayout 也是类似的,只不过除了以上两种约束,还多了一种

3. 子控件和 Guideline 的约束

其实关于和 Guideline 的约束,也可以理解成 约束1,因为 Guideline 其实就是一个在屏幕上不显示的 View 罢了。稍后讲到 Guideline 会带大家看看它巨简单的源码。

下面开始正文,开始属性的讲解.

相对定位(Relative positioning)

这一节的属性和相对布局的很像,值得注意的是参数取值是 ID(@id/button1)代表 约束1、3, 或者字符串"parent" 代表 约束2

  • 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

属性都形如 layout_constraintXXX_toYYYOf,这里我的理解,constraintXXX 里的 XXX 代表是这个子控件自身的哪条边(Left、Right、Top、Bottom、Baseline), 
而 toYYYOf 里的 YYY 代表的是和约束控件的 哪条边 发生约束 (取值同样是 Left、Right、Top、Bottom、Baseline)。

当 XXX 和 YYY 相反时,表示控件自身的 XXX 在约束控件的 YYY 的一侧,例如 app:layout_constraintLeft_toRightOf="@id/button1" ,表示的是控件自身的左侧在 button1 的右侧。

当 XXX 和 YYY 相同时,表示控件自身的 XXX 和约束控件的 YYY 的一侧 对齐,例如:app:layout_constraintBottom_toBottomOf="parent",表示控件自身底端和父控件底端对齐。代码为:

Margins

margin 和以往的使用一致,注意 margin不能为负值 即可。在上图中也顺便展示了 margin 的使用。

当约束的widget为GONE时的Margins

Margins when connected to a GONE widget

举例,当 A控件 约束在 B控件 的左边,B控件 GONE了,此时 A 会额外拥有一个 margin 的能力,来“补充” B 消失的导致的“位移”。这就是本节的属性。

这一节的属性开始我并没有理解,后来是通过写了一些Demo实验才明白。奈何官方文档惜字如金,只有一句话,并没有 Demo 展示:

When a position constraint target’s visibility is View.GONE, you can also indicates a different margin value to be used using the following attributes:

先看属性:

  • layout_goneMarginStart

  • layout_goneMarginEnd

  • layout_goneMarginLeft

  • layout_goneMarginTop

  • layout_goneMarginRight

  • layout_goneMarginBottom

再看Demo:

当给 button4 隐藏GONE掉以后: 

会发现 Button5 纹丝不动,并没有受到 Button4 消失的影响。

这里我们再仔细看看 button4 的 android:layout_width="100dp",而 button5 的 android:layout_marginRight="10dp",app:layout_goneMarginRight="110dp" 
110 = 100 +10 , 这是一道小学计算题。

什么意思?几个注意事项:

  • app:layout_goneMarginRight 要配合 android:layout_marginRight 一起使用。

  • 如果只设置了 app:layout_goneMarginRight 没有设置 android:layout_marginRight,则无效。(alpha版本的bug,1.0.1版本已经修复)

  • 在约束的布局 gone 时,控件自身的 marginXXX 会被 goneMarginXXX 替换掉,以本文 Demo 为例,原本 button4 宽度是 100,button5 的 marginRight 是 10, 加起来是 110,如果想让 button4 隐藏之后,button5 仍然纹丝不动,则需要设置 goneMarginRight为10+100 = 110.

居中定位和倾向(Centering positioning and bias)

  • 居中定位

约束布局一个有用的地方是它如何处理“不可能”的约束。比如你定义如下:

按照我们第一小节讲的属性值,这个定义的意思是,Button 的左边和父控件的左边对齐,Button 的右边和父控件的右边对齐。

可是控件是 wrap_content 的,它如果不铺满父控件要如何能满足这两个约束呢?实际效果如下: 

控件会居中显示,因为这两个约束作用 类似于 水平方向上,有相反的力去拉控件,最终控件会居中显示。

  • 倾向(Bias)

搭配bias,能使约束偏向某一边,默认是0.5,有以下属性:

  • layout_constraintHorizontal_bias (0最左边 1最右边)

  • layout_constraintVertical_bias (0最上边 1 最底边)

比如上个Demo,我加入 app:layout_constraintHorizontal_bias="0.9" ,则会在水平方向上向右偏移至90%

对可见性的处理(Visibility behavior)

这一节是对前一节 goneMargin 的补充。重点是 Gone 隐藏掉的控件,会被解析成一个点,并忽略 margin

ConstraintLayout 能为 View.Gone 的 View 特殊处理。

通常,GONE 的控件不会被显示,并且不是布局本身的一部分(即如果标记为 GONE,则其实际尺寸并不会更改)。但是在布局计算方面,GONE 的 View 仍然是其中的一个重要区别:

对于布局传递,它们的维度将被视为零(基本上它们将被解析为一个点)。如果他们对其他小部件有约束力,那么他们仍然会受到尊重,但任何 margin 都将等于零

拿上个Demo改一下,为 A 加上一个 android:layout_marginRight="10dp",为了使 A 隐藏后,B 仍能纹丝不动,则 B 的 app:layout_goneMarginRight="120dp"。B goneMarginRight120 = A宽度100 + A marginRight10 +B marginRight10

尺寸约束(Dimensions constraints)

ConstraintLayout的最小尺寸 (Minimum dimensions on ConstraintLayout)

可以为 ConstraintLayout 自身定义最小的尺寸,他会在 ConstraintLayout 为 WRAP_CONTENT 时起作用。

  • android:minWidth

  • android:minHeight

控件尺寸约束(Widgets dimension constraints)

控件的宽高有三种方式为其设置:

  • 确定尺寸

  • WRAP_CONTENT

  • 0dp,就等于 MATCH_CONSTRAINT

有些人可能有疑问,为什么不用 MATCH_PARENT 了。官方文档如是说:

MATCH_PARENT is not supported for widgets contained in a ConstraintLayout, though similar behavior can be defined by using MATCH_CONSTRAINT with the corresponding left/right or top/bottom constraints being set to “parent”.

意思是 MATCH_PARENT 不再被支持了,通过 MATCH_CONSTRAINT 替代。我们写个 Demo 看一下三种方式设置的效果吧:

有些人是不是要说,你特么逗我,不是说好的 0dp 等于 MATCH_CONSTRAINT,应该是撑满屏幕的呀!OK ,把刀放下。让我们仔细看这个 MATCH_CONSTRAINT 属性。它 match 的是约束。

而这里第三个按钮的约束是第二个按钮,所以它的宽度设为 MATCH_CONSTRAINT 时,是和它的约束按钮,即第二个按钮一样宽。

注意,此时,竖直方向上没有约束,所以不能使用 MATCH_CONSTRAINT 属性.

我们仅仅将第三个按钮的属性修改为

app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"

则它宽度会撑满屏幕:

我们再修改 Demo,分别为后两个按钮加上 margin:

最后,记住一句话约束要和 0dp 的 方向一致。否则无效

比例(Ratio)

只有一个方向约束

可以以比例去定义 View 的宽高。为了做到这一点,需要将至少一个约束维度设置为 0dp(即MATCH_CONSTRAINT)并将属性 layout_constraintDimentionRatio 设置为给定的比例。例如:

比例值有两种取值:

  • 浮点值,表示宽度和高度之间的比率 (2,0.5)

  • "width:height" 形式的比例 (5:1,1:5)

当约束多于一个(宽高都被约束了)

如果两个维度均设置为 MATCH_CONSTRAINT(0dp),也可以使用比例。 在这种情况下,系统会使用满足所有约束条件和比率的最大尺寸。

如果需要根据一个维度的尺寸去约束另一个维度的尺寸。则可以在比率值的前面添加 W 或者 H 来分别约束宽度或者高度。

例如,如果一个尺寸被两个目标约束(比如宽度为0,在父容器中居中),可以使用 W 或H 来指定哪个维度被约束。

这里用“H”表示以高度为约束,高度的最大尺寸就是父控件的高度,“2:1”表示高:宽 = 2 : 1. 则宽度为高度的一半:

链条(Chains)

链条在同一个轴上(水平或者垂直)提供一个类似群组的统一表现。另一个轴可以单独控制。

创建链条(Creating a chain)

如果一组小部件通过双向连接(见图,显示最小的链,带有两个小部件),则将其视为链条。

链条头(Chain heads)

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

头是水平链最左边的 View,或垂直链最顶端的 View。

链的margin(Margins in chains)

如果在连接上指定了边距,则将被考虑在内。例如:

通过 app:layout_constraintRight_toLeftOf="@+id/buttonB" 和 app:layout_constraintLeft_toRightOf="@+id/buttonA" 就建立了链条,(我中有你,你中有我)。

然后它们两个成了一个整体,所以链条左边设置 app:layout_constraintLeft_toLeftOf="parent" 使得和父控件左对齐,右边设置 app:layout_constraintRight_toRightOf="parent"使得和父控件右对齐,这样整个链条就居中了,最后对左控件设置了 margin,相当于整个链条左边有了 margin。效果:

链条样式(Chain Style)

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

看图这里就很像 js 里的 flexible 有木有。因为 ConstraintLayout 就是模仿 flexible 做的。

取值如下:

  • spread - 元素将被展开(默认样式)

  • 加权链 - 在 spread 模式下,如果某些小部件设置为 MATCH_CONSTRAINT,则它们将拆分可用空间

  • spread_inside - 类似,但链的端点将不会扩展

  • packed - 链的元素将被打包在一起。 孩子的水平或垂直偏差属性将影响包装元素的定位

拿加权链举个例子:

加权链(Weighted chains)

和LinearLayout的weight类似。

链的默认行为是在可用空间中平均分配元素。 如果一个或多个元素使用 MATCH_CONSTRAINT,它们将使用剩余的空白空间(在它们之间相等)。 属性 layout_constraintHorizontal_weight 和 layout_constraintVertical_weight 将决定这些都设置了 MATCH_CONSTRAINT 的 View 如何分配空间。

例如,在包含使用 MATCH_CONSTRAINT 的两个元素的链上,第一个元素使用权重为 2,第二个元素的权重为 1,第一个元素占用的空间将是第二个元素的两倍

最后关于链条,再给大家看一个关于 margin 的 demo:

一图胜千言,可以看到虽然他们的 weight 相等,但是 margin 是被计算在约束里的,所以左边的按钮宽度比右边的小。

Guideline

Guideline 只能用于 ConstraintLayout 中,是一个工具类,不会被显示,仅仅用于辅助布局。它可以是 horizontal 或者 vertical 的。(例如:android:orientation="vertical")

  • vertical 的 Guideline 宽度为零,高度为 ConstraintLayout 的高度

  • horizontal 的 Guideline 高度为零,宽度为 ConstraintLayout 的高度

定位 Guideline 有三种方式:

  • 指定距离左侧或顶部的固定距离(layout_constraintGuide_begin)

  • 指定距离右侧或底部的固定距离(layout_constraintGuide_end)

  • 指定在父控件中的宽度或高度的百分比(layout_constraintGuide_percent)

一个栗子一看便知:

Guideline源码:

public static final int GONE = 0x00000008;

源码就这么点,这货的源码和 ViewStub 有点像啊,可以看出

  • 它默认是 GONE 的。8 就是 View.GONE 的值。

  • 它的 public void setVisibility(int visibility) 方法被空实现了,所以用户也没办法改变它的可见度。

  • 推导出它一定是 GONE 的。在屏幕上不可见

  • this.setMeasuredDimension(0, 0) 和 public void draw(Canvas canvas) 的空实现,表明这是一个超轻量的 View,不可见,没有宽高,也不绘制任何东西。仅仅作为我们的锚点使用。

总结

这篇文章我写了整整一天,每个例子我都边写边跑了一遍,也看了几篇别人的文章,有些人简单的翻译了官方文档,但是对文档中一些没有举例, 不那么好理解的地方也没有说明,于是便有了此文。

关于可视化操作,建议直接看我写的

动态图解&实例 ConstraintLayout Chain

http://blog.csdn.net/zxt0601/article/details/72736802

文中代码地址在我的Demo合集中:

https://github.com/mcxtzhang/Demos/tree/master/constraintlayoutdemo/src/main

更多

每天学习累了,看些搞笑的段子放松一下吧。关注最具娱乐精神的公众号,每天都有好心情。

如果你有好的技术文章想和大家分享,欢迎向我的公众号投稿,投稿具体细节请在公众号主页点击“投稿”菜单查看。

欢迎长按下图 -> 识别图中二维码或者扫一扫关注我的公众号: