[原]手把手教你ConstraintLayout
开篇
这是我写的第一篇技术博客。其实有点不知道从何说起,所以索性就从Android布局中的 ConstraintLayout(约束布局) 说起吧。
ConstraintLayout 从一出生就吸引万千目光。在2016年Google I/O大会上,它被隆重的介绍出来。它的问世完美解决了困扰开发者多年的 RelativeLayout 与 LinearLayout 布局嵌套过多的问题。
ConstraintLayout 从Android Api 9 以上就可以使用啦!从 Android Studio 2.3 起,官方的默认布局模板就使用的是 ConstraintLayout。
尽管目前网上有很多详细介绍ConstraintLayout的优秀文章,我还是决定写一篇关于ConstraintLayout的文章,目的是删繁就简。不会过多的去延伸和拓展相关知识,只为可以让开发者通过本篇对ConstraintLayout的介绍,快速掌握和使用ConstraintLayout。那么本篇的目的就达到啦!
我为什么需要它?
两句话解释。
第一句:ConstraintLayout 使用约束的方式指定各个控件的位置和关系。
(何谓约束的方式?参见下面章节详细解释)
第二句:当项目中遇到复杂布局时,会通过RelativeLayout 与 LinearLayout 互相多层嵌套实现(严重影响性能),而ConstraintLayout则一个就搞定!
(为何一个就搞定?参见第一句解释)
我什么时候使用它?
一句话建议。
当布局嵌套超过3层 (>=3) 的时候,就可以考虑直接上 ConstraintLayout 啦!
我怎么使用它?
第一步:引用
由于 ConstraintLayout 并不是Android SDK集成的,而是作为非绑定( Unbundled ) 的支持库去引用 ,所以,我们首先需要在 app 的 build.gradle 里引用它。
dependencies {
compile 'com.android.support.constraint:constraint-layout:1.1.3'
}
(请确保你的Android Studio版本在 2.2 以上)
第二步:新建布局xml
在工程的 res → layout 下面新建 layout_constraint.xml 文件(名字可自定),初始化布局如下:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/constraintLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
</android.support.constraint.ConstraintLayout>
第三步:可视化拖拽控件至ConstraintLayout窗体中
从事Android开发的同学应该都知道,尽管IDE为我们提供了可视化的布局界面,但一般我们进行布局的时候,都不会使用它,因为操作起来非常不方便,很难快速实现我们想要的布局效果。所以我们会直接通过代码进行布局。
But 对于 ConstraintLayout 而言,可视化布局恰恰成为了一大利器。因为 ConstraintLayout 非常适合利用可视化布局界面进行布局,操作简便,且能快速实现所需效果。
如下图所示,在 ConstraintLayout 中可视化布局一个 Button ,呈居中效果。
可视化布局.gif
布局完成后,Android Studio 会自动生成相关代码。如上图,布局完成后的代码如下:
<?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:id="@+id/constraintLayout"
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_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:text="Button"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
看到这段代码后,我想大家应该都注意到了 Button 中末尾4行的属性,也就是
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
这4个属性是 ConstraintLayout 所独有的。命名空间是“app:”,即来源于本地包的命名空间。我们后面会详细讲解这些属性。如果有些同学习惯用代码进行布局的话,就需要更加熟练掌握这些属性的含义和用法哦!
第四步:ConstraintLayout布局属性解析
先来给大家上幅图,关于相对位置约束的。
相对位置约束.png
接下来我们再来说说一直都在说的 ConstraintLayout约束布局 到底是个什么东西?
ConstraintLayout约束布局:其实就是根据布局中的其他元素或视图控件,确定当前视图控件在屏幕中的位置。其中视图控件受到三类约束,即其他视图控件 、 父容器( parent )、基准线 ( Guideline )。
4.1 标准属性
它的标准属性模板如下:
app:layout_constraint [本源位置]_[目标位置]="[目标 ID]"
例如:
app:layout_constraintTop_toTopOf="@+id/constraintLayout"
即在约束布局 ConstraintLayout 中,视图控件的约束是:将“当前视图 的顶部”约束至“目标视图的顶部”,目标视图的名称 ID 是 constraintLayout。 也就是说,把当前视图的顶部对齐到 constraintLayout 布局的顶部。这是约束布局的表述方式,在约束布局中,所有复杂的排列都是通过类似的形式组合在一起的。具体的有如下一些属性:
-
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
- 当前视图结束点与目标视图结束点对齐
以上属性根据上面提供的 相对位置约束图 ,相信大家可以很好的理解属性的含义及用法。那么问题来了,图上面 left 与 start,以及 right 与 end 都位于同一个地方,那么是否也就是说如下两个属性含义相同?
app:layout_constraintLeft_toLeftOf
app:layout_constraintStart_toStartOf
答案是 不同。再准确点来说,视两种情况而定。
- 布局方向是 LTR(left-to-right),那么 start == left,end == right;(默认)
- 布局方向是 RTL(right-to-left),那么 start == right,end == left。
Android默认的布局方向是 LTR,也就是说,一般情况下,都是第一种情况。如果想要设置成 RTL,则需要在 Manifest文件 中的 <application> 节点中添加属性 android:supportsRtl=true。此时,便成了第二种情况。(需要Android API >= 17)
4.2 Bais属性
由第三步可知,通过下面4个属性,可以让Button呈现 居中 效果。但如果项目的布局要求该Button偏向其中一侧布局,应该怎样做呢?
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
此时就需要用到 Bais 属性了。主要分为两种:
- layout_constraintHorizontal_bias 水平偏差
- layout_constraintVertical_bias 垂直偏差
例如,我们设置水平偏差为0.3,也就是30%,则会呈现下图所示效果。
layout_constraintHorizontal_bias=0.3.png
同样适用于垂直偏差。回到我们的布局文件 layout_constraint.xml 上来,如果我们设置水平偏差=0.3,垂直偏差=0.6。则效果如下:
Bais属性设置.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:id="@+id/constraintLayout"
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_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:text="Button"
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"
app:layout_constraintVertical_bias="0.6" />
</android.support.constraint.ConstraintLayout>
好啦!关于Bais属性的就介绍到这里啦!接下来,我们会介绍其他好玩儿的属性
4.3 角度约束
角度约束,顾名思义,就是通过 角度(angle) 和 距离(radius) 来约束两个控件的中心位置。老规矩,先来张图。
角度约束.png
角度约束主要涉及到以下三个属性:
- layout_constraintCircle : [目标 ID]
- layout_constraintCircleRadius : [距离目标控件中心的长度]
- layout_constraintCircleAngle : [与目标控件的角度(0-360)]
两个控件中心位置之间的关系如下图所示:
空间中心位置关系.png
回到我们的布局文件 layout_constraint.xml 上来,新增一个 Button2 ,通过设置角度定位属性(角度45°,距离200dp)实现如下效果。
角度定位示例.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:id="@+id/constraintLayout"
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_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:text="Button"
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"
app:layout_constraintVertical_bias="0.6" />
<Button
android:id="@+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button2"
app:layout_constraintCircle="@id/button1"
app:layout_constraintCircleAngle="45"
app:layout_constraintCircleRadius="200dp" />
</android.support.constraint.ConstraintLayout>
需要注意的是,角度定位属性是在 ConstraintLayout API 1.1 以 才出现的新增属性。所以如果你想要使用该属性,需要确保你引用的ConstraintLayout 的 版本号 >=1.1。目前最新的版本是 1.1.3。
4.4 尺寸约束
在Android布局文件中,对于尺寸的设置必不可少,然而对于Layout的尺寸约束,在以前经常让我们头痛,比如,对于LinearLayout的尺寸约束,如果想要限制它的最大宽,我们一般会考虑设置
android:maxWidth=100dp
然后,就没有然后了 接着,我们尝试用
android:layout_maxWidth=100dp
然后,可爱的红色感叹号就跳出来了
现在,ConstraintLayout的横空出世,终于,让这个问题不再是问题!
4.4.1 最大最小尺寸约束
可以通过如下四个属性对 ConstraintLayout 自身 进行最大最小宽高约束。
- android:minWidth 最小宽度
- android:minHeight 最小高度
- android:maxWidth 最大宽度
- android:maxHeight 最大高度
由于这几个属性的含义和使用场景大家已经烂熟于心了,我这里就不一一演示了
4.4.2 内部控件尺寸约束
ConstraintLayout 内部控件的尺寸设置可以通过如下三种方式进行:
- 设置固定尺寸
- 使用 wrap_content 自动计算大小
- 使用 0dp,相当于设置 MATCH_CONSTRAINTS
前两种尺寸设置无需赘言。重点是第三种约束设置。如果设置了0dp,那么则可以根据需要进行如下三种关联设置:
- 最小值
- layout_constraintWidth_min
- layout_constraintHeight_min
- 最大值
- layout_constraintWidth_max
- layout_constraintHeight_max
- 百分比
- layout_constraintWidth_percent
- layout_constraintHeight_percent
老规矩,先上效果图如下。
尺寸约束.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:id="@+id/constraintLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/button1"
android:layout_width="0dp"
android:layout_height="0dp"
android:text="Button"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintWidth_percent="0.7"
app:layout_constraintHeight_percent="0.2"/>
<Button
android:id="@+id/button2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="Button2"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/button1" />
<Button
android:id="@+id/button3"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="Button3"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintWidth_min="200dp"/>
</android.support.constraint.ConstraintLayout>
由代码可以清晰知道,
- button1 实现的是 percent 约束 的效果。
- button3 实现的是 最小值约束 的效果。(最大值同理)
- button2 实现的是 单纯0dp [MATCH_CONSTRAINTS] 的效果,直接进行宽度填充(因为0dp是设置在 width 属性上)
- Ratio 属性(宽高比 width:height)
当 layout_width 或者 layout_height 设置为 一个 0dp,另一个为固定值 后,可以进行宽高比属性(ratio)设置。一般常用于设置 ImageView 等控件上。
所涉及到的属性如下:
- layout_constraintDimensionRatio
照例先上效果图。
Ratio 属性示例.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:id="@+id/constraintLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:layout_width="0dp"
android:layout_height="200dp"
android:background="@color/colorAccent"
android:src="@drawable/naruto"
app:layout_constraintDimensionRatio="16:9"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:layout_width="0dp"
android:layout_height="200dp"
android:background="@color/colorAccent"
android:src="@drawable/naruto"
app:layout_constraintDimensionRatio="H,16:9"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent" />
</android.support.constraint.ConstraintLayout>
由以上代码及示例图可以清晰知道,ratio默认设置比为 W:H 即 width:height,即以下两句代码等价:
app:layout_constraintDimensionRatio="16:9"
app:layout_constraintDimensionRatio="W,16:9"
而如果设置为 “H,16:9” 则表示该比值为 H:W 即 height:width,如图中 第二个 ImageView 控件 所示。
4.5 链式约束
链式约束就比较有意思了,也非常实用,关键理解很简单。先上一张图看了再说!
链式约束类型.png
由上图可以得出,链式约束总共有 5种类型,具体含义如下:
- Spread Chain : 元素之间留有空隙,链的两侧也留有空隙,空隙的宽度相等,即两侧含有空隙的等宽排列 。
- Spread Inside Chain : 元素之间留有空隙,链的两侧紧贴于容器两 侧 ,空隙的宽度相等, 即两侧不含空隙的等宽排列。
- Weighted Chain : 元素之间不含空隙,紧密排列,宽度比例各不相同 。
- Packed Chain : 元素之间不含空隙,而链的两侧留有空隙,且空隙宽度相等 。
- Packed Chain with Bias : 元素之间不含空隙, 而链的两侧留有空隙 , 但空隙宽度不同。
链式约束涉及到的属性值有2个,分别是 水平链式约束 和 垂直链式约束
- layout_constraintHorizontal_chainStyle
- layout_constraintVertical_chainStyle
我们以 Spread Chain 为例来说明链式约束的使用。上图。
Spread Chain.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:id="@+id/constraintLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/btn1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Center1111"
app:layout_constraintHorizontal_chainStyle="spread"
app:layout_constraintLeft_toLeftOf="@+id/constraintLayout"
app:layout_constraintRight_toLeftOf="@+id/btn2"
app:layout_constraintTop_toTopOf="@+id/constraintLayout" />
<Button
android:id="@+id/btn2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Center2"
app:layout_constraintLeft_toRightOf="@+id/btn1"
app:layout_constraintRight_toLeftOf="@+id/btn3"
app:layout_constraintTop_toTopOf="@+id/constraintLayout" />
<Button
android:id="@+id/btn3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:text="Center3"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/btn2"
app:layout_constraintTop_toTopOf="@+id/constraintLayout" />
</android.support.constraint.ConstraintLayout>
除了上面所说的两种链式约束外,还有一种 比重链式约束(weight chains)。所涉及到的属性如下(同样分为 水平 和 垂直 两种):
- layout_constraintHorizontal_weight
- layout_constraintVertical_weight
该属性的含义、使用以及效果,同 LinearLayout 下的 weight 属性相似,故在此不再过多介绍。感兴趣的童鞋可以自己去试试
第五步:Guideline介绍
Guildline像辅助线一样,在预览的时候帮助你完成布局(不会显示在界面上)。关于Guideline的作用及使用场景留给童鞋们自己去探索
除了你所介绍的,我还可以从哪儿获取关于ConstraintLayout最新的信息?
一句话总结。
关于ConstraintLayout最权威的信息当然是直接去官方文档查看啦!
好啦!至此我们的第一篇《手把手教你掌握ConstraintLayout》就到此结束啦!希望可以给各位童鞋带起来一点点的帮助,那么本篇的目的就达到啦!
第一次写技术博客,难免有错误疏漏之处,欢迎各位童鞋批评指正!
我是 FredWhite,你们的好盆友,我在这儿等着你!