挑着写点电表里用到的。
ConstraintLayout 是 Google 在 2016 年的 I/O 大会上提出的一种新的布局,主要是用来解决原有的 LinearLayout、RelativeLayout 等通常会导致布局嵌套过多而影响性能的问题。同时 ConstraintLayout 的出现也让 Android Studio 里的可视化布局编辑器真正处于可用状态(从 Android Studio 3.6 开始打开一个布局文件时默认只会展示可视化编辑器)——话虽如此,但我还是一直在并且习惯于直接写布局代码(是不是没救了)。另外,ConstraintLayout 也是 Android X 推荐使用的布局。
所以从一开始就使用 Android X 而不是 Android Support 的电表自然而然地在绝大多数情况下(为什么是绝大多数呢?这个在系列后面的文章里会提到)使用了 ConstraintLayout。之前在完成课程作业的时候用的还是 Android Support + LinearLayout & RelativeLayout,可以说在原有的布局还没搞清楚的情况下直接换新布局是挺冒险的一件事的,不过要是不冒险的话怎么能给我有水博客的机会呢。
咳咳,跑题了。关于 ConstraintLayout 的详细属性什么的,网上已经有很多不错的文章讲得很清楚了,我这里就不班门弄斧了。这次主要是聊一聊电表中我是怎么用 ConstraintLayout 实现我想要的布局的——不过话说回来,如果仔细研究了 ConstraintLayout 的各个属性的话,也不需要我这篇文章了。
match_parent
vs0dp
Google 推荐在使用 ConstraintLayout 时使用 0dp
来代替 match_parent
进行 Widget 大小设置——当然想要使用 match_parent
也不是不可以。
match_parent
对于 ConstraintLayout 中的 Widget,如果对宽度设置了 match_parent
,则可以无需设置水平约束。同理,如果对高度设置了,则可以无需设置垂直约束。
0dp
在 ConstraintLayout 中,将 Widget 的宽度或高度设置为 0dp
,等效于设置为 match_parent
,这是一个特殊的表示长度的值。但是,这个值要求设置对应方向上起始位置(top、start 或者 left)和结束位置(bottom、end 或者 right)的约束,否则宽度或者高度真的会变成 0。基于此,我们可以将宽度或者高度设置为 0dp
,并设置好约束,可以使 Widget 占满父 View 对应方向上的所有空间。如果宽度和高度均为 0dp
并设置了两个方向上的四个约束,则 Widget 占满父 View 的全部空间。
居中
为 Widget 设置好对应方向上的两个约束,可以使其在该方向上处于居中位置。上面提到的设置宽或高为 0dp
的同时设置约束以占满父 View 对应方向的空间,即是居中的特殊表现。
居中偏移
margin
设置法
有时候我们需要使 Widget 处于某个方向的中间偏左或偏右的位置,只需要在居中的基础上设置好左(start 或 left)或右(end 或 right)方向上的 margin
即可。
bias
比例设置法
电表中有这么一个需求:我们知道,空教室列表和使用过的水机列表在数据项不足的时候,高度没有办法撑满整个屏幕,为此希望列表紧靠其上方的 Widget,并随着数据项逐渐变多而增加高度,直至撑满屏幕(或达到底部约束)。
根据需求描述,很容易想到,将列表的高度设置为 wrap_content
,并设置好顶部约束和底部约束。但是,这时候我们会发现,列表处于屏幕的中间而非顶端。由于不同设备的屏幕大小不同,因此我们无法通过简单地设置 margin
来使其偏移。
在 ConstraintLayout 中,提供了 layout_constraintHorizontal_bias
和 layout_constraintVertical_bias
和两个属性来分别设置水平和垂直方向上的约束偏移,取值为 0~1 的浮点数,表示 Widget 在水平或垂直方向上离起始方向(start 或 top)的距离,值越小则距离越大。当值为 0 时则完全贴靠起始方向,为 1 时则完全贴靠结束方向(end 或 bottom)。
以上面提到的电表中的具体需求为例,首先我们需要设置好 top 和 bottom 两个位置的约束,然后将列表的高度设置为 wrap_content
,最后将 layout_constraintVertical_bias
设置为 0 即可。
Guideline(辅助线)
Guideline 的本质是一个没有大小且不可见的 View,利用辅助线可以简化布局嵌套。
还是以电表为例。在成绩查询功能中,每个科目的卡片一共有三行,其中第三行有“学分”和“绩点”两个元素。为了使界面美观,我们可能会希望“学分”位于水平方向的起始位置,而“绩点”的起始位置对应在水平方向上的中间(具体效果可以参照 2.0.0 以后版本的电表)。
原始的做法是,引入 RelativeLayout 并设置两个 TextView 起始位置的百分比。注意我们在创建科目卡片时已经有了一个根布局(忽略 Material CardView),因此这样会导致不必要的布局嵌套,影响渲染性能。
ConstraintLayout 中引入了 Guideline 之后问题就比较好办了。添加一个 Guideline,设置其位置标识为百分比方式并且 layout_constraintGuide_percent
值为 0.5,然后使“绩点”的 TextView 的 layout_constraintStart_toStartOf
属性约束到 Guideline 即可。
除了百分比方式外,Guideline 还可以设置为相对父 View 左边缘或右边缘(是的,只能是相对于父 View,不能手动设置约束到 Widget)指定的距离,具体用法不在此赘述,可以自行查阅文档或搜索相关文章。
后
可能有人注意到,上面在描述水平方向上的位置时,我使用的是“起始(结束)”而非“左(右)”,这是因为如果使用 start 或 end 来描述位置时,如果遇到 RTL(从右向左)语言,start 或 end 可以自动转为该语言下正确的对应位置。相反,left 和 right 则是基于绝对方向。因此 Google 也是建议采用 start 和 end 来描述。