Android ConstraintLayout图文并茂详解(一)
不知道从什么时候开始,创建的layout默认的layout是ConstraintLayout,一直不知道这是啥鬼东西,总是将其手动的改成LinearLayout,也就是说习惯了靠编写XML代码完成界面。偶然的一次机会,朋友推荐了一篇介绍ConstraintLayout的博客,一发不可收拾的出来了这篇博客。下面跟着我来了解ConstraintLayout。
LayoutEditor
后来了解到,ConstraintLayout是Android Studio 2.2中主要的新增功能之一,也是Google在去年的I/O大会上重点宣传的一个功能。在Android Stuido中有个可视化工具 - Layout Editor(布局编辑器),自从接触Android Studio以来(beat 0.6),它一直都存在,,鉴于种种原因,我们一直都是习惯于编写xml来完成界面。其最根本的原因还是视图的拖动,并不能完美的解决屏幕适配。
自从ConstLayout出现,我们可以放弃编写xml了,为什么呢?ConstraintLayout的所有功能都可以直接从可视化工具 - Layout Editor中实现,因为布局API和Layout Editor是专门为彼此构建的。因此,可以完全通过拖放使用ConstraintLayout来构建布局而不是编辑XML。
俗话说,工欲善其事必先利其器,接下来我们揭开Layout Editor的庐山真面目。
Layout Editor全景
- 工具栏(Toolbar):提供在编辑器中配置布局外观和编辑布局属性的按钮
- Palette:提供小部件和布局的列表,您可以将它们拖动到编辑器内的布局中
- Component Tree:显示布局的视图层次结构。在此处点击某个项目将看到它在编辑器中被选中
- 设计视图:用来显示界面
- blueprint视图:在该视图中可以清晰的看到布局相关信息,约束关系、边距等
- 属性(Attributes):针对当前选择的视图提供属性控件
Design和Text选项卡
- Design:可以在LayoutEditor中配置布局的外观
- Text :查看xml中的布局代码,同时在Preview窗口中查看当前界面显示
在”Design”选项中,如果没有选中任何视图,按着ctrl然后点击鼠标左键UI视图的任何位置,将会切换到Text;如果选中了某个视图,按着ctrol然后点击鼠标左键UI视图的任何位置,不仅会切换到Text,同时定位到选中的视图的code在XML的位置。
更改预览外观
- Design and blueprint:用于选择在编辑器中查看布局的方式。Design 视图显示布局的彩色预览,而 Blueprint 视图仅显示每个视图的轮廓。或者,您可以并排查看 Design + Blueprint 。
提示:您可以通过按 B 在这些视图之间进行切换。
- Screen orientation:用于在横屏和竖屏方向之间旋转设备。
- Device type and size:用于选择设备类型(手机/平板电脑、Android TV 或 Android Wear)和屏幕配置(尺寸和密度)。您可以从多个预配置的设备类型和您自己的 AVD 定义中进行选择,或从列表中选择 Add Device Definition 新建 AVD 定义。
提示:您可以通过拖动布局的右下角来调整设备尺寸。 - API version:用于选择要在上面预览布局的 Android 版本。
- App Theme:用于选择将应用到预览的 UI 主题背景。注:此按钮仅对支持的布局样式有效;因此,此列表中的许多主题背景会导致错误。
- Language:用于选择显示 UI 字符串的语言。此列表仅显示字符串资源中可用的语言。如果您要编辑译文,请从下拉菜单中点击 EditTranslations(请参阅使用 Translations Editor 对 UI 进行本地化)。
- Layout Variants:用于切换到此文件的一个备用布局,或创建一个新布局(请参阅下文的创建布局变体)。
视图的inspector
“Attributes”窗口在顶部显示视图的inspector,位于src和contentDescription的视图属性上方。inspector显示UI元素的约束和边距,以及用于调整元素沿水平和垂直轴的位置偏差的进度条。
- hide:使用此按钮关闭“Attributes”窗口
- margin:通过单击值并选择不同的值来更改边距
- height/width类型:表示控件的宽度和长度
- Bias:使用进度来改变具有相反约束的元素的水平和垂直约束偏移
- 尺寸比(ratio):尺寸大小的比例
- 约束:表示当前侧面的约束,单击后可以删除约束
工具栏
- Align:设置UI视图和blueprint视图中是否显示约束、边距等
- Autoconnect:设置是否自动设置约束
- 删除所有视图的约束
- 自动推断所有视图的约束
- 设置默认边距
- 设置视图填充可用空间
- 设置对齐方式
- 添加GuideLine
ConstraintLaytout概述
对于传统布局而言,比如说,RelativeLayout以控件之间相对位置或相对父容器位置进行排列,还有LinearLayout以控件之间的线性关系进行排列。传统布局完全可以完成任意界面,ConstraintLayout又有什么优势呢?
对于复杂的的布局,使用传统布局,我们最多的解决办法是各种嵌套,尽管有include可以使用,但并没有在根源上解决,除非这样的界面,通过自定义View来实现,又有点得不偿失。而,ConstraintLayout,即约束布局,可以在平面视图层次结构创建大而复杂的布局(无嵌套视图组),减少布局的层级, 优化渲染性能。它类似于RelativeLayout,因为所有视图都是根据兄弟视图和父容器之间的关系来布局的,但它比RelativeLayout更灵活,并且更易于与Android Studio的Layout Editor一起使用。至于,在ConstraintLayout中如何完成界面编写,后续讲解。
前面我们已经详细讲解了Layout Editor界面,了解到可以完全通过拖放使用ConstraintLayout来构建布局而不是编辑XML,这样大大的提高了编写界面的效率,这种优雅的的方式,后续展现。
在这里又有一个疑问,ConstraintLaytout对于低版本是否兼容呢?ConstraintLaytout本事是由support库来提供的,它可适用于与Android 2.3(API级别9)及更高版本兼容的API库。当然,要使用ConstraintLayout,首先需要在app/build.gradle文件中添加ConstraintLayout的依赖:
dependencies {
...
compile 'com.android.support.constraint:constraint-layout:1.0.2'
}
美中不足的是,它对buildTools还有一定的要求,必须在25.0.3以上。如果配置低的话,升级下即可。
buildToolsVersion "25.0.3"
添加和删除约束
在ConstraintLaytout中,如果要指定视图的位置,必须为视图添加至少一个水平和一个垂直约束。这里又提到了约束的概念,约束表示与另一个视图,父布局或不可见的guideline连接或对齐。约束不难理解,把视图放在ConstraintLaytout中,如果想要确定视图的位置,必须提供一个位置的相对关系,不管使用坐标系还是相对关系。这里,我们先了解到约束的作用就是用来在ConstraintLaytout中,限定视图位置的。
接下来,我们在ConstraintLaytout中完成界面。首先要知道的是如何创建一个ConstraintLaytout?
创建ConstraintLayout
转换布局
如果Android Studio自动默认创建的是传统布局,比如RelativeLayout,我们可以通过如下操作将它转换成ConstraintLayout。
- 在Android Studio中打开您的布局,然后单击编辑器窗口底部的“Design”选项卡。
- 在Component Tree窗口中,右键单击布局,然后单击“Convert layout to ConstraintLayout”。
创建一个新的布局
要创建新的ConstraintLayout文件,按照下列步骤操作:
- 在Project窗口中,单击File > New > XML > Layout XML
- 输入布局文件的名称,并为根标记输入“android.support.constraint.ConstraintLayout”
- 点击“OK”
为视图添加一个约束
如果想为视图添加约束,首先将视图从“Palette”窗口拖动到Layout Editor中。当在ConstraintLayout中添加视图时,它会在每个角上显示一个带有方形调整大小控键的边框,并在每边显示圆形约束控键。
- 调整大小控键:可以拖动方形调整大小的句柄来调整元素的大小。 拖动时,手柄变成角度变角。
- 约束控键:单击约束句柄,在元素的每一侧显示为圆形,然后拖动到另一个约束句柄或父边界以创建约束。 约束由Z字形线表示。
点击视图以选择它。 然后单击并按住其中一个约束控键并将该行拖动到可用的锚点(另一个视图的边缘,父布局的边缘或GuideLine)。当释放时,将添加约束,默认边距分隔两个视图。这里,我们将一个Button的左侧面约束到父容器的的左侧:
这样,我们就为Button创建了一个约束。
可是,如上图,仔细看Layout Editor,有个标志,点开后提示:大概意思是,Button缺少垂直约束,在运行后,其将在布局的左边。我们来看看约束的规则:
- 每个视图必须至少有两个约束:一个水平和一个垂直。
- 只能在约束控键和共享相同平面的锚点之间创建约束。因此,视图的垂直平面(左侧和右侧)可以仅限于另一个垂直平面; 基线只能限制在其他基线上
- 每个约束控键只能用于一个约束,但可以创建多个约束(从不同的视图)到相同的锚点’
这也就是说,对于Button的约束而言,因为缺少了垂直约束,Layout Editor则会给出错误提示。尽管缺少的约束不会导致编译错误,但是会导致界面不能按照我们预想的显示。以后,我们在为视图添加约束时,尤其要注意Layout Editor的提示,查看是否是否有缺少约束。为了避免缺少约束,Layout Editor可以使用自动连接(Autoconnect)和infer约束功能以便自动添加约束,至于这两种添加约束的方式,是否可行,后续再说。
约束到父容器
将视图的一边约束到布局的相应边缘。
在下图中,将视图的左侧连接到父容器的左边缘。 可以定义边缘与边距的距离。
相对位置
在前面,我们已经知道如何给视图添加约束。现在,我们将通过约束来指定视图的位置。在ConstraintLayout中,Relative positioning,即相对位置是创建布局的基本构建模块之一。它类似RelativeLayout的layout_toLeftOf、alignParentLeft等这些属性。这些约束用来指定给定的控件相对于另一个控件的位置,可以在水平和竖直方向约束控件。
- 水平方向: Left, Right, Start 和 End sides
- 垂直方向: top, bottom sides 和 text baseline
通常,将一个控件的约束边位于任何其他控件的另一侧。
例如:将按钮B放在按钮A的右边:
如果
我么可以这么做:
<android.support.constraint.ConstraintLayout
...>
<Button
android:id="@+id/btnRealA"
.../>
<Button
android:id="@+id/btnRealB"
...
app:layout_constraintStart_toEndOf="@+id/btnRealA" />
</android.support.constraint.ConstraintLayout>
上述代码,告诉我们,按钮B的左边被约束到按钮A的右侧,表示按钮B与按钮的A的右侧对齐。
在ConstraintLayout中,定义了一系列的相对位置的约束属性,它们都将引用一个控件id,可以是任何其他视图,也可以是父容器(即引用父容器,即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
刚才在设置按钮的B的约束是在xml里面设置的,前面已经提到了可以在Layout Editor里面通过拖动设置约束,我们可以这么做:
- 将两个Button拖到ConstraintLayout中
- 将约束从第二个Button的左侧拖到第一个右侧侧,如下图所示。 此约束将第二个Button水平放置第一个按钮,默认边距为8dp:
这样,就不用再xml一行行的码代码了,只要拖动就好了,爽的要不得。。。
自动添加约束
在ToolBar中,曾提到一个工具 Autoconnect,它是用来设置是否自动设置约束。如果将自动设置约束打卡,自动连接可以为父容器的视图元素创建两个或多个约束(因为一个视图最少有一个横向和一个竖向约束)。 将视图拖动到布局之后,它将基于元素的位置创建约束。
- :打开自动连接
-
:关闭自动连接
- 打开layout_autoconnect.xml并切换到 Desgin选项卡
- Autoconnect工具位于工具栏中。默认情况下启用。
- 在blueprint或UI视图中,单击,然后将ImageView拖动到布局的中心,直到出现点阵指示。
- 当看到从上到下以及从左到右的GuidLine时,释放鼠标按钮。 将看到,表示约束的蓝色曲折线将视图的顶部,左侧,底部和右侧边缘连接到父视图的边缘,将ImageView放置在布局中
约束被自动添加到视图的所有四面,我们可以看到ImageView位于Layout的中心,这是为何?让我们来看xml是如何定义的:
<ImageView
android:id="@+id/imageView5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@mipmap/ic_snow" />
从xml中可以看出,layout_constraintLeft_toLeftOf和layout_constraintRight_toRightOf是两个完全相反的约束。在ConstraintLayout中,如果在视图中添加两个相反的约束,则像弹簧一样,约束变得像一个弹簧一样表示相反的力。当视图大小设置为“fixed”或“wrap_content”时,效果最为明显.在这种情况下,视图以约束为中心。同样的,layout_constraintBottom_toBottomOf和layout_constraintTop_toTopOf也是两个完全相反的约束。这也不难解释为什么ImageView位于Layout的中心了。
删除约束
当ConstraintLayout里面的视图过多时,总避免不了一时手抖,或者其他原因,导致约束链接错误。我们该如何删除错误的约束呢?
-
选择该视图,然后单击预删除的约束控键。此时,只删除当前视图指定的约束。
-
通过选择视图,然后单击清除约束,来删除该视图的所有约束
-
点击工具栏的,删除所有视图的约束
对齐视图
让我们来看上面的界面,其中一个明显的特点就是不同视图之间的对齐。UI在设计界面时,为了让界面更简洁,视图的对齐,肯定是一个优雅的处理方式之一。那么问题就来了,在ConstraintLayout该如何对齐呢?我们先了解对齐的概念,对齐就是将视图的边缘对齐另一个视图的相同边缘,或者是视图的中心对齐另一个视图的中心。
在上图图中,B的左侧与A的左侧对齐,我们只需要将B左侧约束到A的左侧即可:
刚才,只是将两个视图的一侧对齐了,如果想两个视图的中心对齐,应该怎么做呢?前面,我们提到过两个相反约束的作用,这里可以试一下,在两个视图的两边都创建约束:
对于,在视图两边创建约束,视图对齐中心的原因这里不多描述,不了解可以看下前面对于两个相反约束的作用解释。
刚才只是,做了两个视图的对齐,如果是多个视图对齐,是不是就需要两两作对分别做约束呢,不用说也是特别的繁琐,必然不合理。既然不合理,那不用说也有工具了。在工具栏 7的作用是设置对齐方式。那么我们可以这么做
-
选择需要对齐的视图
- 在Component Tree里手动选择
- 在设计视图或者blueprint视图通过拖动的方式选择
-
通过工具栏选择对齐方式
-
在Component Tree里右键点击被选择的视图,在Align或者Center中可以选择对齐方式
-
在工具栏中选择对齐方式
-
下面,我们在在Component Tree中选择所需对齐的视图,然后在Component Tree里右键点击被选择的视图,在Align中选择视图中心对齐:
基线约束
不管是英语还是其他语言(汉语除外),为了规范书写会设有四条线,从上至下第三条就是基线,以保证书写更整齐。如下所示:
对于文本视图而言,例如TextView,EditText或Button,以使文本基线对齐,也为此设置了BaseLine相关属性,比如,RalativeLayout中的android:layout_alignBaseline属性用于设置当前组件与参照组件的基线对齐,或者是LinearLayout中的android:baselineAligned属性用于设置是否允许用户调整它内容的基线。关于传统布局的基线对齐,这里不多描述。我们还是关心ConstraintLayout中的文本视图的基线对齐。在ConstraintLayout中,使用基线约束来对齐使用不同文本大小的视图。即使视图中的文本元素大小不一,依然可以使用基线约束来对齐元素。这里我们来做个输入账号的界面:
- 将一个TextView从Palette拖到布局,其文本为”ID”,字体大小为18sp
- 将“Plain Tex”元素从“Palette”拖到布局(纯文本元素是EditText视图),其hint为”Please input the id”,字体大小为14sp
-
单击TextView“id”元素并将其指针悬停在其上。元素下方出现基线对齐的ab按钮,因为元素中包含文本:
-
单击ab按钮显示文本基线。然后从TextView“id”的基线(绿色闪烁)中单击并拖动到lain Tex的基线,如下图所示:
如果两个视图中的文本是多行,而且文本大小不一时,通过基线约束对齐,具体效果见下图:
从上图中,我们可以清晰的看到:
- 如果作为基线的控件的内容为多行,则以第一行作为基线
- 基线对齐只是针对视图中的文本,而不是视图本身
约束到GuideLine
这里,先说说GuideLine是什么鬼。Guideline是ConstraintLayout的Guideline辅助对象的实用程序类,该辅助对象不会显示在设备上,它被默认是View.GONE,而且不可改。另外,Guideline是专门为ConstraintLayout创建的,也就是它仅也只能用于ConstraintLayou布局。
Guideline可以是水平或垂直的:
- 垂直Guideline的宽度为0,高度为ConstraintLayout的高度
- 水平Guideline的宽度为ConstraintLayout的宽度,高度为0
GuideLine也可以认为是视图,其可以根据相对于布局边缘的dp或百分比,将GuideLine定位在布局中,位置确定有三种方式:
- layout_constraintGuide_begin:指定与布局左侧或顶部的固定距离(dp)
- layout_constraintGuide_end:指定与布局右侧或底部的固定距离(dp)
- layout_constraintGuide_percent:指定与布局的宽度或高度的百分比
如果你想创建一个GuideLine,可以点击工具栏的,然后在弹出框中选择水平的还是垂直的:
在创建GuideLine以后,其默认的位置确认方式是layout_constraintGuide_begin,我们可以拖动虚线来重新指定它的位置。如果你想改变它的定位方式,单击GuideLine边缘的圆圈以切换测量模式。如下图所示。
对于GuideLine的情况先说到这,接下来我们来了解Guideline有什么用?下面这个图,是登录界面一部分,可以清楚看出来,布局左右两边的pading是44dp。
现在,我们创建一个GuideLine,其layout_constraintGuide_begin的值为44dp,然后把TextView(”账号”和密码)约束到GuideLine:
到这里,我们知道GuideLine其实就是一种约束规则,供其他视图使用,以统一处理距离ConstraintLayout边缘的距离,而不用单独处理约束偏差。
对于GuideLine有个不解的地方,先看下面一段代码:
<android.support.constraint.ConstraintLayout
....>
<android.support.constraint.Guideline
android:id="@+id/guideline"
.../>
<android.support.constraint.Guideline
android:id="@+id/guideline2"
.../>
<Button
android:id="@+id/button19"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="aaaaaaaaaaaaaaaaaaaaaaaaa"
app:layout_constraintEnd_toStartOf="@+id/guideline2"
app:layout_constraintStart_toStartOf="@+id/guideline"/>
</android.support.constraint.ConstraintLayout>
如上述代码所示,一个Button的左右两侧约束到两个垂直GuideLine,其width类型为wrap_content。如果Button中文本的长度超过两个GuideLine之间的距离时,Button本事会超过GuideLine的边界。如果将width类型为fixed,并指定其宽度大于两个GuideLine之间的距离时,依然如此。这样,必然会造成视图被覆盖的现象。
如果将width类型修改为MATCH_CONSTRAINT,效果是这样的:
造成这种问题的根本原因在于ConstraintLayout对于空间的计算方式。早在ConstraintLayout 1.0.2版本以前,有barriers用来协助约束,但是在1.0.2已经取消了这个辅助类。在这种情况下,是将width类型修设置为MATCH_CONSTRAINT还是限制文本显示的内容,这是我们值得考虑的问题。
本来打算一篇写完的,后来发现添加图片过多,导致篇幅很长,那就先说到这里,后续再写。到这里,本文主要介绍了Layout Editor的相关界面及在ConstraintLayoput中如何定位视图、对齐视图等。接下来,将了解如何修改视图的属性?如何处理带有链的线性视图组?
参考文档
上一篇: Constranint布局配置和使用教程
推荐阅读
-
Android编程之基于Log演示一个activity生命周期实例详解
-
Android AOP注解Annotation详解(一)
-
Android利用ConstraintLayout实现漂亮的动画详解
-
Android从网络中获得一张图片并显示在屏幕上的实例详解
-
解析Android开发优化之:对界面UI的优化详解(一)
-
Android工程:引用另一个Android工程的方法详解
-
详解Android获取设备唯一ID的几种方式
-
详解关于Android Studio中安装和gradle的一些坑
-
Android md5加密与php md5加密一致详解
-
Android开发笔记之:一分钟学会使用Logcat调试程序的详解