Jetpack Compose - ConstraintLayout

Compose系列文章,请点原文阅读。原文:是时候学习Compose了!

0、介绍

在xml的时代我们已经了解过了ConstraintLayout的强大功能,比例、相对位置、引导线、屏障、链条等让我们开发页面布局可以大展身手。那么在Compose的时代,ConstraintLayout是如何使用的呢?首先官方给它的介绍很简单:

根据子项之间的约束,定位子项的布局。

但是实际用法上有了很多的差异,接下来一起研究下吧。

1、属性一览

【目前基于alpha08版本的属性】首先请看它的两个函数:

@Composable fun ConstraintLayout(
    modifier: Modifier = Modifier, 
    content: ConstraintLayoutScope.() -> Unit
): Unit
@Composable fun ConstraintLayout(
    constraintSet: ConstraintSet, 
    modifier: Modifier = Modifier, 
    content: () -> Unit
): Unit

属性参数含义:

参数含义
modifier: Modifier = Modifier应用于布局的修饰符
【用法1】content: ConstraintLayoutScope.() -> Unit子级内容,可以很方便的创建 约束布局参照物
【用法2】constraintSet: ConstraintSet,对于子级约束相关描述
【用法2】content: () -> Unit使用ConstraintSet参数而定义的子级内容

2、使用示例

我们先从一些基本概念说起:

2.0、ID

到目前为止我们并没有见到过Compose类似xml中那种给控件命名id的形式,那既然Compose是约束布局,必须要有个控件的编号或者ID,才能实现各种约束条件, 目前在ConstraintLayout中有两种方式来创建这种编号的方式:

如果使用的参数是ConstraintLayoutScope()的方式,那么可以使用如下函数创建编号并应用给控件:Modifier.constrainAs()和createRef()、createRefs()

如果使用的参数是ConstraintSet的方式,那么则可以使用如下方式给控件创建ID和然后根据ID创建编号:Modifier.layoutId()和createRefFor()

关于这两种创建编号的方法我们就点到这里,下面我们分别在Guideline和Barrier中进行演示;

2.1、Guideline

引导线,可以从特定的位置(某一方向上的偏移量或者某一方向上的比例)创建一条实际并不可见的参考线。总共有如下几种方式:

  • createGuidelineFromStart(offset: Dp)
  • createGuidelineFromAbsoluteLeft(offset: Dp)
  • createGuidelineFromStart(fraction: Float)
  • createGuidelineFromAbsoluteLeft(fraction: Float)
  • createGuidelineFromEnd(offset: Dp)
  • createGuidelineFromAbsoluteRight(offset: Dp)
  • createGuidelineFromEnd(fraction: Float)
  • createGuidelineFromAbsoluteRight(fraction: Float)
  • createGuidelineFromTop(offset: Dp)
  • createGuidelineFromTop(fraction: Float)
  • createGuidelineFromBottom(offset: Dp)
  • createGuidelineFromBottom(fraction: Float)

别被这么多方法吓到,其实就是从上、下、左、右四个方向,分别支持某一偏移量,或者某一比例进行创建引导线。而带有Absolute的表示绝对的左右方向,这里其实就是国际化的问题。

如下代码,我们使用参数为ConstraintLayoutScope()的函数来进行演示:


@Composable
fun ConstraintLayoutDemo() {
    ConstraintLayout(
        modifier = Modifier.fillMaxSize()
    ) {

        val guideline = createGuidelineFromStart(0.2f)
        val (box1, box2) = createRefs()

        Box(
            modifier = Modifier.fillMaxSize()
                .background(color = Color.Yellow)
                .constrainAs(box1) {
                    end.linkTo(guideline)
                }
        )

        Box(
            modifier = Modifier.fillMaxSize()
                .background(color = Color.Red)
                .constrainAs(box2) {
                    start.linkTo(guideline)
                }
        )
}

首先ConstraintLayout是一个填充全屏幕的布局,然后在该布局中从开始(左侧)的百分之20的位置,创建一条竖向引导线。
然后创建了两个Box布局,分别是结束部分链接到引导线,开始部分链接到引导线。所以部分效果如下所示:
在这里插入图片描述

2.2、Barrier

屏障,这个也容易理解,如下图所示:我们需要填写姓名和身份证信息,由于“姓名”和“身份证”这两个标题长度不一致,但是我们后面填写的信息又要对齐处理。你可能会想到固定前面标题的长度或者限制字符数,这样都没问题,但是更灵活的办法就是使用屏障了。
我们可以在“姓名”和“身份证”这两个标题的右边创建一个屏障,这样你哪个标题长,屏障就会在哪个标题的右边,比如下图中的红线部分,所以后面的内容我们都可以根据屏障进行布局了。
在这里插入图片描述
先看下创建屏障的几种函数:

  • createStartBarrier()
  • createAbsoluteLeftBarrier()
  • createTopBarrier()
  • createEndBarrier()
  • createAbsoluteRightBarrier()
  • createBottomBarrier()

一共就四个方向,加上两个绝对方向,同样是国际化的原因。

好了下面一起看下如何使用Barrier结合ID来实现上述的屏障效果。
首先我们在content中先声明了3个Box布局,这三个布局都是只指定了大小和颜色以及他们分别的ID,但是并不知道如何进行排列。
然后在constraintSet中我们根据ID创建了编号,然后对这些编号使用constrain()函数进行约束声明,如果你在xml的时代使用ConstraintLayout布局熟悉的话,你能很快理解这里的写法,就是通过linkTo函数将控件的约束关系描述出来,而且目前的link语法检查也很强大,比如说你指定一个控件的顶部需要对齐到另一个控件的左或者右部,这是不应该的,会直接报错,也就是说top.linkTo(parent.start)这种类似的都是错误的,原因不用说了吧,代码如下:


@Composable
fun ConstraintLayoutIdDemo() {
    ConstraintLayout(
        ConstraintSet {
            val box1 = createRefFor("box1")
            val box2 = createRefFor("box2")
            val box3 = createRefFor("box3")

            constrain(box1) {
                top.linkTo(parent.top)
                start.linkTo(parent.start)
            }

            constrain(box2) {
                top.linkTo(box1.bottom)
                start.linkTo(parent.start)
            }

            val barrier = createEndBarrier(box1, box2)

            constrain(box3) {
                start.linkTo(barrier)
                top.linkTo(box1.top)
                bottom.linkTo(box2.bottom)
            }
        }
    ) {
        Box(
            modifier = Modifier.layoutId("box1")
                .background(color = Color.Red)
                .width(100.dp)
                .height(100.dp)
        )
        Box(
            modifier = Modifier.layoutId("box2")
                .background(color = Color.Yellow)
                .width(150.dp)
                .height(100.dp)
        )
        Box(
            modifier = Modifier.layoutId("box3")
                .background(color = Color.Blue)
                .width(200.dp)
                .height(100.dp)
        )
    }
}

实现结果如下所示:
在这里插入图片描述
修改布局参数,让红色部分比黄色部分宽50dp,运行结果如下:

在这里插入图片描述

2.3、Chain

链,可以参考xml中的chain,大概意思就是将一系列组件按顺序打包成一行或一列。官方将此api标记为了可以改进的状态,猜测可能会被之前提到的Flow布局代替?
先看下api,只有两个,创建横向和竖向的链:

  • createHorizontalChain()
  • createVerticalChain()

第一个参数是需要打包到一起的控件的编号,第二个属性是链的类型,目前共有三种类型:

  • Spread
    所有控件平均分布在父布局空间中,是默认的类型
  • SpreadInside
    第一个和最后一个分布在链条的两端,其余的控件平均分布剩下的空间
  • Packed
    所有控件打包在一起,并放置在链条的中间

一起看下横向链条的示例代码,我们创建三个色彩不同的Box,然后创建横向链条将这三个Box打包到一起:

@Composable
fun ConstraintLayoutChainDemo() {
    ConstraintLayout(modifier = Modifier.fillMaxSize()) {
        val (box1, box2, box3) = createRefs()
        createHorizontalChain(box1, box2, box3)
        
        Box(modifier = Modifier.size(100.dp).background(Color.Red).constrainAs(box1) {})
        Box(modifier = Modifier.size(100.dp).background(Color.Yellow).constrainAs(box2) {})
        Box(modifier = Modifier.size(100.dp).background(Color.Blue).constrainAs(box3) {})
    }
}

代码渲染结果如下:
在这里插入图片描述
当链条上的元素总宽度超出屏幕时,代码渲染结果如下,注意此时是没有滚动效果的,请注意这种情况:
在这里插入图片描述

2.4、小结

其实经过上面的代码示例,我们可以发现除了定义ID和编号外,实现约束位置的主要在constrainAs()和constrain()这两个方法中:
我们来看下他们所支持的一些属性:

  • parent
  • start
  • absolutLeft
  • top
  • end
  • absoluteRight
  • bottom
  • baseline
    目前有这么多的属性可以供我们使用,使用linkTo()链接到另一个控件的相应属性上即可,切记一点不能乱用,你不可以让一个控件的左侧对齐另一个控件的上侧,这些IDE都会直接给你报错的。

3、版本更新

暂无

4、未解决问题

目前约束布局的基本的使用已经没有大碍了,更多的是开发过程中的细节问题,遇到后再进行补充。在xml中的根据角度约束的功能目前在compose中还没有发现,敬请期待吧。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值