- Constraint Layout Practice and S
- 我又开发了一个非常好用的开源库,调试Android数据库有救了
- 新版bintray Android library上传到JCen
- Principles and Practice of Const
- A Beginner’s Guide to Auto Layou
- Constraint Layout 2.0 新特性 - Moti
- [译]Constraint Layout 动画 |动态 Con
- Constraint Layout 不完全使用手册
- ConstraintLayout的属性介绍
- [译] ConstraintLayout 可视化[Design]
转自:Constraint Layout Practice and Summary-1
Constraint Layout 2.0 several new features-2
从约束布局发布后,一直没有将其应用到工作中,只是停留在Demo阶段(使用的越多越感觉这种布局更加灵活、高效)。去年下半年到今年的工作中,将一部分原有的相对布局、线性布局和帧布局等切换到使用约束布局。刚开始有一些生疏不适应,元素间的关系相互制约的比较紧密。刚开始用完的感觉就是维护起来比较难,因为关心的要素比以往的布局要多。
本文比较简单,因为用了一段时间了,便将之前用到的部分进行了简单的总结,主要是1.0的基本运用,还包含了2.0的一些新特性。对于运用的知识加以理解和深入,并进行内化,最好的方式就是实践加总结。再者是通过实践后用文章检验纠错,不断调整,不断学习。最好的输入就是输出,文章是众多输出方式中的一种。
1. What is?
ConstraintLayout
作为最受欢迎的Jetpack
库之一,TA是一个ViewGroup
。TA的出现主要是为了解决布局嵌套过多的问题,以灵活的方式定位和调整部件。几个月前2.0也发布了,最新稳定版为2.0.4
(December 17, 2020;Alpha Release:2.1.0-alpha2),从studio 2.3以后布局中默认开始使用Constraint Layout
。
2. Why use?
ConstraintLayout
可以使用扁平视图层次结构创建复杂的布局,降低页面布局层级,提升页面渲染性能。与RelativeLayout
相似,却有更高的灵活性,并且更易于与Android Studio
的布局编辑器配合使用。
先来感受下约束的效果~~
比如一张图片在布局中水平对齐,如下代码
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
在studio中Design
视图下可以直接进行操作。
视图中添加了正反约束,约束线像是弹簧一样弯弯曲曲的拉着imageview
,在两个约束条件下显示水平居中。
还可以添加约束偏差,上边视图在两个约束条件之间居中且默认偏差为50%,下图可以在属性窗口中进行自由调整。
2.gif视图绘制大致分为测量、布局和绘制三个阶段,在绘制过程的每个阶段都需要对视图树进行自顶向下的遍历操作,因此视图层次结构嵌套越多,绘制所需时间和计算功耗就会越多,通过扁平化的层次是解决该问题的方式之一。
官方用LinearLayout
和RelativeLayout
进行布局,然后使用Constraintlayout
做同样界面的布局结构,用OnFrameMetricsAvailableListener
分析了两种布局所执行的每次测量和布局操作所花费时间,以收集有关应用界面渲染的逐帧时间信息。结果为ConstraintLayout
在测量和布局阶段的性能比传统布局大约高40%
(蓝色为Traditional,红色为Constraintlayout)。
3. Usage
本文使用Android Studio 4.0(Build #AI-193.6911.18.40.6514223)
和Constraintlayout:2.0.4
对于相对位置(layout_constraintLeft_toLeftOf、layout_constraintLeft_toRightOf
等),边距(android:layout_marginStart、android:layout_marginEnd、android:layout_marginRight、layout_goneMarginLeft
等),水平垂直居中,水平垂直偏移(layout_constraintHorizontal_bias、layout_constraintVertical_bias
)不再进行讲解,可以参考官方文档。
3.1 Add ConstraintLayout to your project
// Add the dependencies for the artifacts you need in the build.gradle file for your app or module:
// Need Sync Project with Gradle Files
dependencies {
// latest version 2.1.0-alpha2
// December 17, 2020
implementation "androidx.constraintlayout:constraintlayout:2.0.4"
}
4.png
3.2 Margin
边距,这里主要介绍个新特性,就是layout_goneMarginStart、layout_goneMarginLeft、layout_goneMarginTop,当目标控件设置为隐藏(GONE)的时候,gonemargin*边距值仍然生效。其他对应的属性查看官方文档即可,这里不再叙述。
5.png举个栗子:
假设text2
的左边约束在text1
的右边,并给TextView2设置app:layout_goneMarginLeft="16dp"
,效果如下:
当将text1
的可见性设为android:visibility=gone
时,左侧的16dp生效了,效果如下:
说明: 如Text1显示时距左侧是6dp,当不显示时Text2也应该是距离左侧6dp(但当Text1显示时,Text2距离左侧是0dp),那么使用goneMarginLeft
则可以满足该场景。
3.3 Ratio
宽高比,使用app:layout_constraintDimensionRatio
来设置宽高比,该比率有两种设置方式,如下:
- 浮点值:表示宽度和高度之间的比率(比如:1.0F);
- 比率:'宽度:高度'形式的比率(比如:1:1)。
// formula -> height:width=ratio
h:w=ratio
在使用过程中,刚开始理解有误,所以主要针对比率进行说明。
1、app:layout_constraintDimensionRatio="16:9"
表示宽:高=16:9
2、app:layout_constraintDimensionRatio="w,3:4"
表示宽:高=4:3,w表示分母是宽。
3、app:layout_constraintDimensionRatio="h,16:9"
表示宽:高=16:9,h表示分母是高。
4、指定值情况下:
举栗:
① w已经设定固值,无法通过先获取h再通过ratio进行计算。w=100dp(方便计算),h=0dp,当w,2:1时,有w:h=1:2,然后将值代入,100:h=1:2,换算为1h=200,则h=200。
② w=100dp,h=0p,当h,2:1时,有h已经设置为固定值,不能通过ratio计算h值,有w:h=2:1,为2h=100dp,则h=50dp。
3.4 Chain
链,在横轴或或者竖轴上的控件相互约束时,可以组成一个线性的链式约束,在一个维度(水平or垂直)上,管理一组控件的方式。
多个view相互引用即创建了一个链,第一个为链头,链的属性也由链头的属性进行控制。水平方向左边第一个控件为链头,垂直方向上最上边控件为链头。
总共分为4中样式,可以通过 app:layout_constraintHorizontal_chainStyle
或 app:layout_constraintVertical_chainStyle
设置链式控件的样式,包含spread
、spread inside
和 packed
。
1) Spread(default)
元素均匀的分散进行分布。
// 省略了layout_width、layout_height等属性
// textX为head,链的属性由链头控制
<TextView
android:id="@+id/textX"
android:text="A"
app:layout_constraintHorizontal_chainStyle="spread"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@id/textD"
/>
<TextView
android:id="@+id/textD"
android:text="Better"
app:layout_constraintLeft_toRightOf="@id/textX"
app:layout_constraintRight_toLeftOf="@id/textF" />
<TextView
android:id="@+id/textF"
android:text="You"
app:layout_constraintLeft_toRightOf="@id/textD"
app:layout_constraintRight_toRightOf="parent" />
执行以上代码,效果如下:
7.png
2) Spread inside
类似spread模式,但链的端点不会分散。即第一个和最后一个视图固定在链两端的约束边界上,其余视图均匀分布。
// 其余代码省略
...
app:layout_constraintHorizontal_chainStyle="spread_inside"
9.png
执行以上代码,效果如下:
8.png
当设置为spread
或spread inside
模式时,可以通过将一个或多个视图设置为“match constraints”(0dp)
来填充剩余空间。默认情况下,设置为“match constraints”
的每个视图之间的空间均匀分布。也就引出下一模式:权重模式。
android:layout_width="0dp"
效果如下:
3) Weighted
权重模式,类似于LinearLayout
中的layout_weight
,原理是相同的。权重值最高的视图获得的空间最大;相同权重的视图获得同样大小的空间。
// 省略部分代码
// chain style is spread or spread_inside
<TextView
android:id="@+id/textX"
android:layout_width="wrap_content"
app:layout_constraintHorizontal_chainStyle="spread" />
// 文本D占用2份可用空白空间
<TextView
android:id="@+id/textD"
android:layout_width="0dp"
app:layout_constraintHorizontal_weight="2" />
// 文本F占用2份可用空白空间
<TextView
android:id="@+id/textF"
android:layout_width="0dp"
app:layout_constraintHorizontal_weight="2" />
执行代码,效果如下:
10.png
4) Packed
视图将被包装在一起(在考虑外边距之后)。也可以通过更改链的头视图偏差(Bias,下边有介绍)调整整条链的偏差(左/右或上/下)。
执行代码,效果如下:
11.png
举栗子:
在开发中有显示用户头像、姓名和编号的区域,有个需求是当有编号时三项全部显示,没有编号则只显示头像和姓名,那么采用这种方式能容易的实现。
// 省略layout_width、layout_margin*等基础属性代码
<ImageView
android:id="@+id/iv_avatar" />
<TextView
android:id="@+id/tv_name"
app:layout_constraintLeft_toRightOf="@id/iv_avatar"
app:layout_constraintTop_toTopOf="@id/iv_avatar"
app:layout_constraintBottom_toBottomOf="@id/iv_avatar"
app:layout_constraintBottom_toTopOf="@id/tv_code"
app:layout_constraintVertical_chainStyle="packed"
tools:text="姓名" />
<ImageView
android:id="@+id/iv_switch_identity"
app:layout_constraintBottom_toBottomOf="@id/tv_name"
app:layout_constraintLeft_toRightOf="@id/tv_name"
app:layout_constraintTop_toTopOf="@id/tv_name" />
<TextView
android:id="@+id/tv_code"
app:layout_constraintLeft_toRightOf="@id/iv_avatar"
app:layout_constraintTop_toBottomOf="@id/tv_name"
app:layout_constraintBottom_toBottomOf="parent"/>
12.png
3.5 Center
居中,这个比较有特色的是彼此互为冲突即可居中。
// 水平居中
<TextView
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent/>
// 垂直居中
<TextView
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent/>
3.6 Bias
定义控件的方向偏差,使用Bias可以改变两边的权重,类似于LinearLayout
的weight
属性。
属性说明
-
layout_constraintHorizontal_bias
: 水平偏差,取值范围:0.0~1.0。 -
layout_constraintVertical_bias
: 垂直偏差,取值范围:0.0~1.0。
<TextView
// 水平占比30%
app:layout_constraintHorizontal_bias="0.3"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent/>
4. 辅助工具
4.1 Guideline
像辅助线,引导线一样,可以添加垂直或水平的引导线来约束视图,并且应用用户看不到该引导线(不显示在设备上,因为默认设置为View.GONE)。可以根据相对于布局边缘的dp
单位或百分比在布局中定位引导线。
android_orientation
控制是横向or纵向。
三种定位方式:
-
layout_constraintGuide_begin
:距离父容器起始位置的距离(对于垂直和水平来讲为左侧或顶部)。 -
layout_constraintGuide_end
:距离父容器结束位置的距离(右侧或底部)。 -
layout_constraintGuide_percent
:距离父容器宽度或高度的百分比,取值范围0.0-1.0,优先级高于begin和end。
<TextView
... // 省略代码
app:layout_constraintRight_toLeftOf="@id/guideline"
app:layout_constraintTop_toTopOf="parent" />
<TextView
... // 省略代码
app:layout_constraintLeft_toRightOf="@id/guideline"
app:layout_constraintTop_toTopOf="parent" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.5" />
运行效果如下:
13.png
4.2 Barrier
直译为屏障、分界线,可以用它来约束视图,不会定义自己的位置。相反,屏障的位置会随着其中所含视图的位置而移动。
比如填写收货地址的表单中,左侧为标签内容提示文案,右侧为输入文本区域,中间一条垂直分界屏障线,左右两侧据此则可以向中间靠拢。当然实现这个效果也可以使用Guideline
,需要根据具体需求进行选择。
属性说明
-
barrierDirection
: 屏障所在的位置,可设置的值有:start、end、left、right、top、bottom
。 -
constraint_referenced_ids
: 屏障引用的控件,可设置多个(用逗号“,”隔开)。
// 省略部分代码
<!-- 图中width变化为50和150切换 -->
<TextView
android:id="@+id/tvName"
android:layout_width="150dp"
android:text="name"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tvSchoolAddress"
android:text="SchoolAddress"
app:layout_constraintTop_toBottomOf="@id/tvName" />
<androidx.constraintlayout.widget.Barrier
android:id="@+id/barrier"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="right"
app:constraint_referenced_ids="tvName,tvSchoolAddress" />
<TextView
android:id="@+id/tvClassTime"
android:text="ClassTime"
app:layout_constraintLeft_toRightOf="@id/barrier" />
14.gif
4.3 Group
这个很容易理解, 用于控制一组控件的是否显示,继承于ConstraintHelper。
使用时注意两点:一是组内某个控件设置View.setVisibility(int)
来控制控件显示/隐藏时是无效的。二是控件存在于多个group中,只有最后一个生效。
属性说明
-
constraint_referenced_ids
: 以逗号分隔的ID列表来引用控件。
// 控件代码省略,通过group设置一组控件(id1、id2......)的显示状态
<androidx.constraintlayout.widget.Group
android:id="@+id/group"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:constraint_referenced_ids="id1,id2"
android:visibility="invisible"
/>
4.4 Circular positioning
圆形定位,以角度和半径距离约束控件中心相对于另一个控件中心。
属性说明
-
layout_constraintCircle
: 引用的另一个控件(目标控件)ID值。 -
layout_constraintCircleRadius
: 源控件的中心到其他控件(目标控件)中心的距离。 -
layout_constraintCircleAngle
: 源控件应该处于哪个角度(以度为单位,从0到360)。
// 省略宽高边距等设置代码
<ImageView
android:id="@+id/iv_header"
android:layout_marginLeft="19dp"
android:layout_marginTop="19dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/iv_gender"
app:layout_constraintCircle="@id/iv_header"
app:layout_constraintCircleRadius="35dp"
app:layout_constraintCircleAngle="140"/>
图片iv_gender
在图片iv_header
的140度角半径为35dp的位置上,效果如下:
5. Summary
- 嵌套层级少。当一个复杂布局中,有id强关联的,要复用比较难。所以需要在开发前进行拆分设计,复杂布局要分拆到不同的文件中。
- 可视化编辑:
ConstraintLayout
还有一个独立的编辑器,文章开始页展示了操作效果,只需要托拽就可以完成整个布局。 - 布局高效,轻松应对复杂布局,适配性好,有百分比布局、设置自身宽高比例,各种辅助组件。
- 减少测绘/布局时间,提升效率。性能检测方式如下:
// 1、记录每个帧的界面操作 window.addOnFrameMetricsAvailableListener( frameMetricsAvailableListener, frameMetricsHandler); // 2、触发OnFrameMetricsAvailableListener回调 Window.OnFrameMetricsAvailableListener { _, frameMetrics, _ -> val frameMetricsCopy = FrameMetrics(frameMetrics); // 布局测量采用纳秒级 val layoutMeasureDurationNs = frameMetricsCopy.getMetric(FrameMetrics.LAYOUT_MEASURE_DURATION);
<font size=3 color=#999999>输出是最好的输入方式!</font>
网友评论