Jetpack Compose - 约束布局​ConstrainLayout

3,401 阅读7分钟

ConstrainLayout为约束布局,它并不是Compose独有的一种布局类型,在xml中我们应该也有所接触,并且现在xml的模板就是约束布局,它可以通过多种约束(constrain)来组合布局种其它组件,一般用来替代布局种多个columnrow或者box等,可以简化布局的复杂性。

在Compose使用约束布局,需要提前添加一下依赖:

implementation("androidx.constraintlayout:constraintlayout-compose:1.1.0-alpha11")

目前最新版本为:1.1.0-alpha11,稳定版本还停留在:1.0.1

此篇文章将从基础、GuideLine、Barrier和Chain四个方面介绍约束布局

基础

在刚接触约束布局时,我们用一个最简单的示例来学习下如何在Compose种使用约束布局,下面我们使用约束布局创建一个位于屏幕正中间的文本组件

在上面代码中,我们先通过createRef()创建了一个约束布局的引用,然后通过Modifier.constrainAs(text){}将创建的引用和Text关联起来,最后在constrainAs第二个参数Lambda中添加约束条件,使它上下左右都和父布局关联,这样它就会显示在屏幕的正中间。

效果如下图所示

需要注意的是createRef()Modifier.constrainAs()只可以作用于约束布局下面,非约束布局是不可以创建引用和关联引用的

  • Modifier.constrainAs(){}的Lambda中除了可以用linkTo设置约束条件外,还可以设置宽、高、visibility是否可见、透明度等信息;
  • linkTo()还可以设置可见margin和不可见margin,都是非常实用的功能。

上面我们只提到了子组件和父组件的关联方式,那么子组件和子组件之间的关联该如何处理呢,接下来看看这种场景

我们在上面的代码中再添加一个按钮组件,使它位于文本正下方

这里我们创建引用的方式由createRef()变成了createRefs(),此方式可以创建多个引用,采用的是解构的写法,但是要注意的是最多只能创建16个引用,千万别超出这个数量限制

然后在TextButton的关联方式中将自身的top链接到textbottom,这样就达到了按钮在文本下方的效果。

  • createRefcreateRefs是为约束布局中可组合项创建引用的方式
  • constrainAs是关联创建的引用,并且它的Lambda中可以指定其约束条件
  • 约束条件中parent是一个现成的引用,指向的是约束布局本身,子组件可指定其约束布局创建约束条件。

GuideLine引导线

通过基础部分我们了解到在约束布局中可以指定组件进行约束条件,此时有个需求,屏幕中有两个文本,他们是处于屏幕的正中间,这样就可以采用GuideLine来实现此需求了,它是一种辅助性工具,并不会创建实际的可组合项,下面看代码如何实现

上面代码中我们通过createGuidelineFromTop(0.5F)创建了一个水平的引导线,并且它距离屏幕顶部50%的距离,也就是屏幕垂直方向居中的位置,然后将第一个Text的下方约束条件链接到引导线的位置,将第二个Text的上方约束条件链接到引导线的位置,这样两个文本的位置就分别位于引导线上方和下方,也就达到了他俩位于屏幕中心的效果。

引导线一共分为水平垂直两个方向,并且可以按比例来创建,也可以按照具体距离值创建引导线

这里我只列出五种方式创建水平或者垂直方向的引导线,其中第二种方式以具体距离方式创建引导线适用于所有的方法。引导线可以帮助我们解决开发中很多对齐的问题,大家可以采用感受下。

Barrier屏障线

屏障线和引导线相同,它也是一种辅助性的工具,也不会创建实际的可组合项,在解释屏障线的作用之前,我们先看一个界面

界面中有三个文本,上方、下方和右边,这时候需要实现的效果最右边的文本是位于上方和下方文本的右边,并且不可以重叠,上方和下方的文本谁的长度更长,右边的文本就要对齐最长的那个。

如果不采用约束布局的情况下,可能需要在上方和下方文本外层再包一个Column组件,然后让右边的文本对齐Column的右侧

但是约束布局中的屏障线可以完美的解决此问题,而且不需要添加额外的组件。我们在编码之前先思考🤔一下方案:

  • 首先通过屏障线将上下文本包裹起来,然后在其右侧添加一个引导线
  • 然后将左侧本文的start约束条件对齐屏障线,这样此文本组件就会对齐上下文本最长的那个了

好了,思路有了就直接编码试试看

通过createEndBarrier(topTv,bottomTv)就可以在上下文本的右侧创建一个屏蔽线了,这样在约束条件中可以轻松的设置对齐约束,非常简易的帮助我们实现了上述需求,在不需要创建额外可组合项的条件下。

除了createEndBarrier()以外,Compose的约束布局还提供了createTopBarrier()createBottomBarrier()createStartBarrier()三中方式创建屏蔽线。

Chain链

约束布局中的Chain链可以帮助我们实现线性布局中等分的功能,有点类似于权重的概念(但是和权重还是有所区别),并且还可以实现除可组合项外剩余空间的权重,可能解释有点晦涩难懂,下面通过实际代码来看看它带来的效果,我们先通过Chain来实现文本等分的效果

布局中一共有三个文本,它们整个水平方向均匀分布,首先通过createHorizontalChain()创建了一个水平方向的链,将三个文本与其关联,这样我们就不需要在文本的约束条件中再设置水平方向的约束条件,链会自动根据ChainStyle帮助我们布局。

createHorizontalChain()第二个参数为chainStyle,一共有三大种(有一种可自定义),这里我们先采用默认的Spread方式,先来看看实现的效果

在效果图中,将除文本以外的空间用红框标注起来,可以看出四个红框的宽度是等宽的,然后三个文本分别位居于四个红框中间,这样三个文本在水平方向上就是均匀分布了。

然后我们将createHorizontalChain第二个参数换成ChainStyle.SpreadInside方式,再来看看效果

SpreadInside方式的效果就是将可组合项最左边和最右边的空间给去除掉了,然后中间剩余的空间变大,可组合项的位置也随之改变。

最后再看看ChainStyle.Packed方式,此方式有个参数bias表示偏差,默认为0.5f,我们先看看默认的偏差效果

Packed(0.5F)方式和SpreadInside有点类似,它去除的是可组合项中间的空间,然后最左面和最右边的空间等分了,这样我们三个文本就会挤在水平方向的最中间,如果将bias设置为0.1F会是什么效果呢?

bias的值修改为0.1F之后就会变成上面图片的效果,左边和右边的剩余空间比例就会变成1:9,这种还是比较容易理解的,在水平方向的屏蔽线,bias会影响左右剩余空间的比例。

屏蔽线除了createHorizontalBarrier()还有createVerticalChain()用于创建垂直方向的屏蔽线,用法和水平屏蔽线一致。

关于ConstrainLayout的知识就介绍这么多啦,后面我们尝试用约束布局中MotionLayout实现一个可折叠的标题栏

关于我

我是Taonce,如果觉得本文对你有所帮助,帮忙关注、赞或者收藏三连一下,谢谢~