ConstraintLayout详解
回忆当初稍微复杂的界面,布局的层级嵌套多层,布局最终会解析成 View 的树形结构,这对渲染性能产生了一定的影响,并且也增大了代码的维护难度。Google 工程师正是考虑到这一因素,推出了 ConstraintLayout!!不得不说,真特么好用。。。
ConstraintLayout翻译为约束布局,也有人把它称作增强型的相对布局,由 2016 年 Google I/O 推出。扁平式的布局方式,无任何嵌套,减少布局的层级,优化渲染性能。从支持力度而言,将成为主流布局样式,完全代替其他布局。有个成语用的非常好,集万千宠爱于一身,用到这里非常合适,约束集 LinearLayout(线性布局),RelativeLayout(相对布局),百分比布局等的功能于一身,功能强大,使用灵活。纸上得来终觉浅,绝知此事要躬行。让我们在实际开发的场景中去检验约束布局,在实战中积累经验。
接下来我会以实际开发中遇到的几个场景来讲解。
题外话,本文需要您对 ConstraintLayout 有一定的熟悉了解度,若您对 ConstraintLayout 不熟悉请链接一下地址:
为了要使用ConstraintLayout,我们需要在app/build.gradle文件中添加ConstraintLayout的依赖,如下所示。
dependencies {
compile 'com.android.support.constraint:constraint-layout:1.0.0-beta4'
}
ConstraintLayout常用属性:
layout_constraintTop_toTopOf— 期望视图的上边对齐另一个视图的上边。
layout_constraintTop_toBottomOf— 期望视图的上边对齐另一个视图的底边。
layout_constraintTop_toLeftOf— 期望视图的上边对齐另一个视图的左边。
layout_constraintTop_toRightOf— 期望视图的上边对齐另一个视图的右边。
layout_constraintBottom_toTopOf— 期望视图的下边对齐另一个视图的上边。
layout_constraintBottom_toBottomOf— 期望视图的底边对齐另一个视图的底边。
layout_constraintBottom_toLeftOf— 期望视图的底边对齐另一个视图的左边。
layout_constraintBottom_toRightOf— 期望视图的底边对齐另一个视图的右边。
layout_constraintLeft_toTopOf— 期望视图的左边对齐另一个视图的上边。
layout_constraintLeft_toBottomOf— 期望视图的左边对齐另一个视图的底边。
layout_constraintLeft_toLeftOf— 期望视图的左边对齐另一个视图的左边。
layout_constraintLeft_toRightOf— 期望视图的左边对齐另一个视图的右边。
layout_constraintRight_toTopOf— 期望视图的右边对齐另一个视图的上边。
layout_constraintRight_toBottomOf— 期望视图的右边对齐另一个视图的底边。
layout_constraintRight_toLeftOf— 期望视图的右边对齐另一个视图的左边。
layout_constraintRight_toRightOf— 期望视图的右边对齐另一个视图的右边
1、Circular positioning(圆形定位)
标题后面的中文是自己翻译的,可能不是很准确。
官方文档是这么介绍的:
You can constrain a widget center relative to another widget center, at an angleanda distance. This allows you to position a widget on a circle
我是这么理解的,您可以将一个控件的中心以一定的角度和距离约束到另一个控件的中心,相当于在一个圆上放置一个控件。
示例代码如下:
效果图:
c_1
图文并茂,理解起来较容易些。圆形定位使用其他布局是很难实现的(除自定义外),该功能在实际的开发中用的并不多,可以用来实现类似钟表的效果。该功能只不过是约束布局的冰山一角,且往下看。2、WRAP_CONTENT : enforcing constraints(强制约束)
官方文档是这么介绍的:
If a dimensionisset to WRAP_CONTENT,inversions before1.1they will be treatedasa literal dimension -- meaning, constraints willnotlimit the resulting dimension. Whileingeneral thisisenough (andfaster),insome situations, you might want to use WRAP_CONTENT, yet keep enforcing constraints to limit the resulting dimension. In that case, you can add one of the corresponding attribute
英文一直是我的弱项,我是这么理解的,1.1.0 版本之前是没有这个功能的,说的是控件的宽设置为WRAP_CONTENT(包裹内容)时,如果实际宽度超过了约束的最大宽度,那么约束会失效(高同理),为了防止约束失效,增加了以下属性:
app:layout_constrainedWidth=”true|false” //默认false
app:layout_constrainedHeight=”true|false” //默认false
官网并没有过多说明,那么怎么去理解呢,接下来以app:layout_constrainedWidth属性来看两个例子。
a、例子
c_2
B控件位于A控件右侧与屏幕右侧的中间。代码如下:
那么我改变B控件的内容,使宽度增大:
c_3
通过效果图可以得出,给B控件添加的左右约束失效。为了防止约束失效,在1.1.0版本中新增了app:layout_constrainedWidth="true"属性。注意控件的左右都应该有约束条件, 如下:
app:layout_constraintLeft_toRightOf="@+id/bt_1"//控件的左边位于xx控件的右边app:layout_constraintRight_toRightOf="parent"//控件的右边位于xx控件的右边
效果图如下:
c_4
app:layout_constrainedWidth="true" 会导致渲染变慢,变慢时长可忽略不计。
3、MATCH_CONSTRAINT dimensions(填充父窗体约束)
官方文档是这么介绍的:
When a dimension issetto MATCH_CONSTRAINT, thedefaultbehavior is to have the resulting size take all the available space. Several additional modifiers are available
在约束布局中宽高的维度match_parent被0dp代替,默认生成的大小占所有的可用空间。那么有以下几个属性可以使用:
layout_constraintWidth_min and layout_constraintHeight_min //设置最小尺寸
layout_constraintWidth_max and layout_constraintHeight_max //设置最大尺寸
layout_constraintWidth_percent and layout_constraintHeight_percent //设置相对于父类的百分比
开发中有这样一个需求,位于父控件的中间且宽度为父控件的一半,那么我们可以这么去实现:
c_6
4、goneMargin(隐藏边距)
当约束目标的可见性为View.GONE时,还可以通过以下属性设置不同的边距值:
layout_goneMarginStart
layout_goneMarginEnd
layout_goneMarginLeft
layout_goneMarginTop
layout_goneMarginRight
layout_goneMarginBottom
如以下例子:
c_16
Margins and chains (in 1.1),Optimizer (in 1.1)略。
5、约束之百分比布局
百分比布局大家肯定不会陌生,由于Android的碎片化非常严重,那么屏幕适配将是一件非常令人头疼的事情,百分比适配也就应运而生,约束布局同样也可以实现百分比的功能,并且更加强大,灵活。
经常我们会遇到这样的需求,个人主页要求顶部的背景图宽高16:9来适配,如下图:
c_7
约束布局的实现方式如下:
<!-- "W,9:16" 同样的效果 -->
新增了如下属性:
app:layout_constraintDimensionRatio="H,16:9"
官网的介绍是这样的:
You can also define one dimension of a widget as a ratio of the other one. In order todothat, you need to have at least one constrained dimension besetto0dp (i.e., MATCH_CONSTRAINT),andsetthe attribute layout_constraintDimensionRatio to a given ratio
意思是说约束布局支持子控件设置宽高比,前提条件是至少需要将宽高中的一个设置为0dp。为了约束一个特定的边,基于另一个边的尺寸,可以预先附加W,或H以逗号隔开。
然后需求变动,需要将宽度调整为屏幕的一半:
c_8
只需要新增app:layout_constraintWidth_percent="0.5"属性。
接着需要控件左对齐:
c_9
同时新增了app:layout_constraintHorizontal_bias="0"属性。
官网的介绍如下:
Thedefaultwhen encountering such opposite constraints is to center the widget; but you can tweak the positioning to favor one side over anotherusingthe bias attributes:
具有相反方向约束的控件,我们可以改变偏好值,来调整位置偏向某一边。有点类似LinearLayout的weight属性。
最后需要调整控件距离顶部的高度为父控件高度的20%:
c_10
这里用到了虚拟辅助类Guideline,同时1.1.0版本还添加了两个虚拟类Barrier,Group。它们是虚拟对象,并不会占用实际的空间,但可以帮助我们更好更精细地控制布局。综上的需求变化我们可以相对于父控件任意改变控件大小,控件的位置,从而能够更好的适配各大屏幕。
5、Guideline
Guideline 与 LinearLayout 类似可以设置水平或垂直方向,android:orientation="horizontal",android:orientation="vertical",水平方向高度为0,垂直方向宽度为0。Guideline 具有以下的三种定位方式:
layout_constraintGuide_begin 距离父容器起始位置的距离(左侧或顶部)
layout_constraintGuide_end 距离父容器结束位置的距离(右侧或底部)
layout_constraintGuide_percent 距离父容器宽度或高度的百分比
例如,设置一条垂直方向距离父控件左侧为100dp的Guideline:
效果图如下:
c_17
6、Barrier
Barrier,直译为障碍、屏障。在约束布局中,可以使用属性constraint_referenced_ids属性来引用多个带约束的组件,从而将它们看作一个整体,Barrier 的介入可以完成很多其他布局不能完成的功能,如下:
开发中有这样的一个需求,看下图:
c_11
姓名,联系方式位于 A 区域(随着文本的宽度变化 A 区域的宽度也随之变化),B 区域在 A 区域的右侧。使用传统的布局方式实现嵌套过多,布局不够优雅。那么我们一起来看看约束布局是怎么去实现的:
barrierDirection指定方向,constraint_referenced_ids引用的控件 id(多个id以逗号隔开)。
7、Group
Group用于控制多个控件的可见性。
e.g:
c_12
若android:visibility="gone"那么 A,B 控件都会隐藏。