网传constraintLayout(约束布局)的布局,性能和功能性相当优越,虽然RelativeLayout用的飞起来,也得与时俱进。
本文主要分为以下几个部分:
catalog.png
1. 背景介绍
在面试的时候经常会被问到有没有做过布局优化,都通过什么方式优化布局界面的?优化布局界面其中一种方式就是通过 ConstraintLayout 降低布局层级,从而避免过度测量和绘制。本篇文章重点讲解 ConstraintLayout 的用法,关于 ConstraintLayout 性能方面的优势,可以参考这篇文章:解析 ConstraintLayout 的性能优势。
通过可视化拖拽的方式编写 ConstraintLayout 布局界面,个人是不太推崇的,虽然它确实很方便,但是即使通过可视化拖拽的方式编写布局之后,还是需要看懂 xml 文件中关于 ConstraintLayout 的属性,才可以更灵活的修改布局界面,所以本文主要讲解通过 xml 属性的方式编写 ConstraintLayout 布局界面,关于可视化拖拽的方式编写 ConstraintLayout 布局界面,可以参考郭霖写的这篇博客: Android 新特性介绍,ConstraintLayout 完全解析
好了,上面介绍了 ConstraintLayout 的两个优点,下面开始进入本文的正题,通过 xml 布局属性的方式编写 ConstraintLayout 布局。使用之前,需要在build.gradle文件中添加对 ConstraintLayout 的依赖:
dependencies { ... implementation'com.android.support.constraint:constraint-layout:1.0.2'...}
2. 详细使用
本节主要分为 7 点介绍 ConstraintLayout 的详细使用,那就开始吧~
2.1 相对位置
ConstraintLayout 和 RelativeLayout 是非常类似的布局控件,它们之间最大的相似之处在于都可以编写一个控件相对于其他控件或父控件的相对位置,比如下面这个样式的布局既可以通过
RelativeLayout 实现,也可以通过 ConstraintLayout 实现
relativepositioning.png
通过 ConstraintLayout 实现的代码如下所示:
通过上面的代码,可以看到几个陌生的 xml 属性,它们都是 ConstraintLayout 的 xml 属性,比如:
通过属性的名字就可以猜测出它们大概的意思,比如:
app:layout_constraintLeft_toLeftOf是指该控件的左边缘和某个控件的左边缘对齐
app:layout_constraintRight_toRightOf是指该控件的右边缘和某个控件的右边缘对齐
app:layout_constraintTop_toBottomOf是指该控件的上边缘和某个控件的下边缘对齐
依次类推,同样含义的属性还有:
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_constraintStart_toEndOf
layout_constraintStart_toStartOf
layout_constraintEnd_toStartOf
layout_constraintEnd_toEndOf
layout_constraintBaseline_toBaselineOf
上述属性的属性值可以是某个控件的 id,也可以是parent,比如:
app:layout_constraintLeft_toRightOf="@+id/tv_a"
app:layout_constraintRight_toRightOf="parent"
对于相对位置,官方给出的示意图如下所示:
relativepositioning1.png
2.2 边距(Margins)
假如现在有如下这样一个界面:TextViewA 上边距离 Toolbar 30dp,在屏幕最左边,TextViewB 上边距离 Toolbar 30dp,左边距离 TextView A 也是 30dp,该如何编写布局文件呢?
margin.png
该布局 xml 关键代码如下所示:
在 ConstraintLayout 布局中,下面几个 Margin 相关的属性依然是有效的
android:layout_marginStart
android:layout_marginEnd
android:layout_marginLeft
android:layout_marginTop
android:layout_marginRight
android:layout_marginBottom
注意:在 ConstraintLayout 中,上述属性值只可以是正数或者0,不可以是负数。
假如现在有这样的一个需求,当 TextViewA 显示的时候,TextViewB 距离左边是 20dp;当 TextViewA 可见性为Gone的时候,TextViewB 距离左边是 30dp,使用 ConstraintLayout 可以很容易的实现这样的需求。
在 ConstraintLayout 中如下这些边距属性就是当依据的控件变为 Gone 的时候就会生效
layout_goneMarginStart
layout_goneMarginEnd
layout_goneMarginLeft
layout_goneMarginTop
layout_goneMarginRight
layout_goneMarginBottom
2.3 居中显示
假如说有如下图所示的一个界面,TextViewA 在布局的正中间,如果用 RelativeLayout 和 LinearLayout 实现是很方便的,但是用 ConstraintLayout 怎么实现呢?
centerposition.png
其实也很简单,代码如下:
在 ConstraintLayout 中并不像 LinearLayout 和 RelativeLayout 是通过android:gravity="center"、android:layout_gravity="center"、android:layout_centerVertical="true"和android:layout_centerHorizontal="true"设置居中位置的,而是通过app:layout_constraintBottom_toBottomOf、app:layout_constraintLeft_toLeftOf、app:layout_constraintRight_toRightOf和app:layout_constraintTop_toBottomOf设置该控件上下左右分别依附于指定的控件即可。
在 ConstraintLayout 中有一个非常重要的概念 ---- constraint(约束):当控件有自己的大小时,例如:wrap_content和具体的数值时,我们为控件添加的都是 constraint (约束),这个约束有点像橡皮筋一样会有一个拉力拉着控件,但是并不会改变控件的大小。
例如上例,在居中显示中,TextViewA 宽度为 100dp,左边有app:layout_constraintLeft_toLeftOf="parent"约束,右边有app:layout_constraintRight_toRightOf="parent"约束,左边和右边分别有一个同样大小的力拉着 TextViewA 控件,所以 TextViewA 会水平居中,竖直方向也是一样的。
如果想让上述例子中的 TextViewA 水平方向撑满整个父控件,即如下图所示,那该怎么做呢?
centerposition1.png
首先我们想到的是使用android:layout_width="match_parent"实现,这样设置之后确实也会实现这样的效果,但是查看 ConstraintLayout 的官方文档发现,在 ConstraintLayout 中match_parent已经被match_constraint所取代,所以使用android:layout_width="0dp"更为合适(在 xml 中并没有match_constraint这个属性值,0dp 即代码match_constraint值)。
Bias属性
可以在上下和左右分别有约束的时候加上偏移率,属性如下所示:
app:layout_constraintHorizontal_bias="0.1"
app:layout_constraintVertical_bias="0.8"
layout_constraintHorizontal_bias取值范围是[0.0 ~ 1.0],从左向右
layout_constraintVertical_bias取值范围是[0.0 ~ 1.0],从上到下
centerposition2.png
2.4 可见性对布局的影响
在 2.1 节中提到过几个特殊的属性:
layout_goneMarginStart
layout_goneMarginEnd
layout_goneMarginLeft
layout_goneMarginTop
layout_goneMarginRight
layout_goneMarginBottom
这些属性是配合着layout_marginStart等属性使用的,当控件的约束所依附的 target 控件的 visibility 不为 GONE 的时候,layout_marginStart等属性起作用,当 target 控件的 visibility 为 GONE 的时候,layout_goneMarginStart属性起作用
2.5 尺寸约束
ConstraintLayout 的最小尺寸
当 ConstraintLayout 的宽度或者高度为wrap_content时,可以通过如下属性为ConstraintLayout设置最小尺寸或最大尺寸
android:minWidth:最小宽度
android:minHeight:最小高度
android:maxWidth:最大宽度
android:maxHeight:最大高度
控件的尺寸约束
在ConstraintLayout中,设置控件的大小总共有三种方式,分别是:
一个具体的属性值,比如:123dp或者一个 Dimension 引用
使用wrap_content,根据自身大小决定
0dp即match_constraint
控件按宽高比设置大小
在ConstraintLayout中,其中的控件可以按照宽高比设置其大小,前提是控件的宽或者高的尺寸至少有一个是0dp,然后通过layout_constraintDimensionRatio设置控件的宽高比,显示出来的控件的宽和高即是设置的比例的大小,如下所示:
在上面 xml 代码中,Button 的宽度是一个特定值:wrap_content,高度是一个可变值:0dp,宽:高 = 1 :1,则高度也就是和宽度相同的值
通过layout_constraintDimensionRatio设置的参数可以是:
一个浮点数,代表宽/高的比例
也可以是上述例子中的形式:宽:高
当控件宽和高的值都是0dp时,也可以通过layout_constraintDimensionRatio设置宽和高的比例,比如:
在上面 xml 代码中,Button 的宽是可变值:0dp,高也是可变值:0dp,高:宽 = 16:9,但是 Button 有上下两个约束:高顶到父控件的上边缘,底顶到父控件的下边缘,这样 Button 的高度就固定了,再通过比例,即可得到宽 = 高 * 9/16.
2.5 链(Chains)
在 ConstraintLayout 中有一个非常重要的概念:链(Chains)
那什么才是链呢?在下图所示的界面中即存在一个链:
chains.png
上图中 TextViewA、TextViewB 和 TextViewC 形成了一个链,上图对应的 xml 代码是:
从代码中可以看到形成链的三个控件有以下特点:
TextViewA 和 TextViewB 通过app:layout_constraintRight_toLeftOf="@+id/tv_b"和app:layout_constraintLeft_toRightOf="@+id/tv_a"相互依赖
TextViewB 和 TextViewC 通过app:layout_constraintRight_toLeftOf="@+id/tv_c"和app:layout_constraintLeft_toRightOf="@+id/tv_b"相互依赖
这样便形成了一个链,在此链中最左边的控件称为链头。同样的也可以通过上下相互依赖形成上下的链,最上面的控件称为链头。
在链头中通过layout_constraintHorizontal_chainStyle或layout_constraintVertical_chainStyle可以设置链的样式。
链的样式总共有三种:
CHAIN_SPREAD:默认样式
CHAIN_SPREAD_INSIDE
CHAIN_PACKED
用一张官方的图解释上面几个属性的含义,如下图所示:
chainsStyle.png
这里需要强调下比重链(Weighted chain)样式:在CHAIN_SPREAD样式的链中,如果其中的控件的宽度或高度为 0dp,即可以通过layout_constraintHorizontal_weight或layout_constraintVertical_weight设置该控件在水平或者竖直方向的比例,类似于 LinearLayout 中的 weight 属性的作用
2.6 Guideline
在 ConstraintLayout 中有一个特殊的辅助类:android.support.constraint.Guideline,主要用于辅助布局,可以把它看做是一个辅助线,但是不会绘制到界面上,有水平的和垂直的。
Guideline有以下几个属性:
android:orientation:vertical或horizontal,表示此 Guideline 是水平的还是垂直的
app:layout_constraintGuide_begin:表示 Guideline 的距离左边或上边的距离,根据方向决定是距离哪边的
app:layout_constraintGuide_begin:表示 Guideline 的距离右边或下边的距离,根据方向决定是距离哪边的
app:layout_constraintGuide_percent:表示 Guideline 距离左边或上边的百分比,根据方向决定是距离哪边的
用一张效果图展示 Guideline 的作用,一个 TextView 在中线的左边 32dp 位置处,另一个 TextView 在中线右边 32dp 处
guideline.png
上图代码如下:
好啦,至此关于 ConstraintLayout 的使用就基本全部介绍完毕,是不是觉得很好用呢?那就赶快在项目中使用起来的!文中涉及的代码都在这儿:ConstraintLayoutPractic。
参考资料:
ConstraintLayout 完全解析 快来优化你的布局吧--Hongyang
Android ConstraintLayout 约束布局--打鱼还是晒网 —— stone
ConstraintLayout约束布局使用教程难点理解--yueding
链接://www.greatytc.com/p/6e5fa646ddf5