本文是 ConstraintLayout 小课堂系列第 2 讲,课程目录:

平均间隔

先看一个需求:存在多个 TextView,他们的宽度是可变的,但它们之间的间隔是相等的,并且要平均分配整个屏幕的宽度。

使用原生的 LinearLayout 也能做到这点,简单看一下如何实现:

f5b4f8b20730

添加若干不可见的 View 来填充 TextView 之间的间隙,通过设置相同的 layout_weight 使得这些 View 的尺寸相同,这样 TextView 的间隔就是相同的了。

按照套路应该说这样做的缺点了:

可能产生 ViewGroup 嵌套(没错,用 ConstraintLayout 就是为了不嵌套)

多余的 View 在绘制过程中浪费 CPU(没错,不嵌套的终极目的就是提高绘制速度)

想动态操作这些 TextView 以及间隔,需要给这些间隔 View 添加引用,手动设置 visibility,使用起来很不方便。

那么用 ConstraintLayout 怎么实现这个功能呢?

使用 ConstraintLayout 中的 Chain(链)可以更简单地实现这个需求,不用添加任何辅助对象,只需要在这些 TextView 之间形成链,默认就是平均分配间隔的样子。

Chain(链)

什么是 Chain?

两个 View 在一个方向上互相约束对方就形成了链,多个 View 两两成链,就形成了更长的链,见下图(图来自官网):

f5b4f8b20730

这里吐个槽,本文写作时最新 Android Studio 3.3.2 版本无法通过拖拽创建链,需要手写代码。

代码这么写,举个栗子:

android:id="@+id/buttonOK"

app:layout_constraintEnd_toStartOf="@+id/buttonCancel" 👉 左边的右手牵左手

app:layout_constraintStart_toStartOf="parent" />

android:id="@+id/buttonCancel"

app:layout_constraintStart_toEndOf="@+id/buttonOK" 👉 右边的左手牵右手

app:layout_constraintEnd_toEndOf="parent" />

形成链的一系列 View 会表现出一些整体特征,可以设置一些特殊的布局效果,见下图(图来自官网):

f5b4f8b20730

Spread Chain:伸展链,默认设置,形成链的 View 分散排列,间隔相等。

Spread Inside Chain:内部伸展链,也是平均分配间隔,但 View 和 parent 直接没有间隔。

Weighted Chain:权重链,与 LinearLayout 的 weight 相似,按比例分配空间大小。

Packed Chain:打包链,将所有 View 打包在一起,当做整体,居中。

Packed Chain with Bias:带偏斜的打包链。

如何设置不同的布局效果

数据结构里的链表都有个头节点,它的引用就是整个链表的引用。在 ConstraintLayout 中,左侧第一个或者上方第一个成链的 View 就是头节点。给这个头节点设置链属性,就相当于给整个链设置了链属性。实现以上各种效果需要设置哪些链属性呢,我们一个一个看。

Spread Chain

这个就是开篇提到的平均间隔布局了,只要结成链,默认就是这个效果。

设置方法:

链头节点设置 chainStyle

app:layout_constraintHorizontal_chainStyle="spread"

app:layout_constraintVertical_chainStyle="spread"

所有 View 设置 wrap_content

android:layout_width="wrap_content"

android:layout_height="wrap_content"

与 LinearLayout 的实现相比:

没有多余的 View,不用维护多个间隔 View 的引用

如果要隐藏某个 TextView,直接设置为 gone 就行了,剩余的 TextView 仍然是平均分隔的。

通过设置 margin 可以微调一下间隔,margin 相当于扩大了 TextView 的占用空间,剩余的空间再平分给间隔,有 margin 的 View 看起来间隔会大一些。注意 ConstraintLayout 中使用的 margin 只能是非负数,设置成负数无效。

Spread Inside Chain

设置方法:

链头节点设置 chainStyle

app:layout_constraintHorizontal_chainStyle="spread_inside"

app:layout_constraintVertical_chainStyle="spread_inside"

所有 View 设置 wrap_content

android:layout_width="wrap_content"

android:layout_height="wrap_content"

这个设置成两边贴着 parent,如果要求左侧贴着 parent,右侧不贴呢?

如果是 LinearLayout,去掉左侧贴边的辅助 View 即可,比较方便。用 ConstraintLayout 的 Chain 怎么做呢?

直接去掉左侧第一个 TextView 对 第二个 TextView 的约束,也就是第一个的右手放开第二个的左手。这样整个链就少了一个 View,第一个 View 没有了右侧约束,直接靠在了左侧 parent 上,剩下的 View 仍然是一个链,分配剩余的空间。

f5b4f8b20730

放手之前

f5b4f8b20730

放手之后

Weighted Chain

设置方法:

chainStyle:设置成任何值都没区别,不用设置

至少有一个 View 设置成 match_constraint

android:layout_width="0dp"

android:layout_height="0dp"

可选的 weight

app:layout_constraintHorizontal_weight="1"

app:layout_constraintVertical_weight="1"

只要在链上有一个 View 设置为了 match_constraint,这些 View 之间的剩余空间都会被占用,因此 chainStyle 属性设置为任何一个都没有区别。

如果不设置 weight 属性,就有点复杂了:

所有 match_constraint 的 View 都不设置。大家平分,与都设置了 weight="1" 的效果相同。

一些 View 设置了 weight 属性,一些 View 没设置。没设置的 View 完全消失了,所占空间被分配给剩余的 View 了。

设置了 weight="0" 的 View 完全消失,剩余空间被其他 View 瓜分。

有趣的是,如果只有不设置的和设置为 weight="0" 的,则相当于都不设置,大家平分。

Packed Chain & Packed Chain with Bias

设置方法:

链头节点设置 chainStyle

app:layout_constraintHorizontal_chainStyle="packed"

app:layout_constraintVertical_chainStyle="packed"

所有 View 设置为 wrap_content

android:layout_width="wrap_content"

android:layout_height="wrap_content"

链头节点设置 bias

app:layout_constraintHorizontal_bias="0.1"

打包链将整个链中的所有 View 打包成一个整体,可以与其他 View 关联约束。可能的应用场景是多个可变长的 TextView 紧挨在一起,并且它们的整体要在某个范围内居中。如果不用 ConstraintLayout,就只能将它们嵌套在一个单独的 View 中来做到了,你看,这就是 ConstraintLayout 能减少嵌套的原因👏👍。

Bias 属性是用来设置一个 View 被居中时,偏向某一侧的程度。通常取值范围是 [0, 1],表示左侧或上侧的空白区域占所有空白区域的范围。有趣的是,这个值也可以取 [0, 1] 之外的值,这个 View 就超出了约束的范围,小于 0 向左移,大于 0 向右移。

回到 Packed Chain with Bias,由于打包效果,整个链上的 View 对于其他 View 来说相当于一个 View,设置的 bias 属性也与一个 View 的 bias 相同。

总结

经过上面的讲解可以看到 ConstraintLayout 的链布局非常强大,可以实现的布局效果非常丰富,如果有什么难度较高的布局普通方法实现不了,可以考虑用链式布局来思考。

(ole)

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐