Android jetpack Compose之约束布局

概述

我们都知道ConstraintLayout在构建嵌套层级复杂的视图界面时可以有效降低视图树的高度,使视图树扁平化,约束布局在测量布局耗时上比传统的相对布局具有更好的性能,并且约束布局可以根据百分比自适应各种尺寸的终端设备。因为约束布局确实很好用,所以,官方也为我们将约束布局迁移到了Compose平台。本文就是介绍约束布局在Compose中的使用。

实例解析

在使用约束布局之前,我们需要先在项目中的app.gradle脚本中添加compose版本的ConstraintLayout依赖

implementation('androidx.constraintlayout:constraintlayout-compose:1.0.1')

引入依赖以后,我们就可以看下如何在Compose中使用约束布局了

1.创建引用

在传统View系统中,我们在布局XML文件中可以给View设置资源的ID,并将资源ID作为索引来声明对应组件的摆放位置。而在Compose的约束布局中,可以主动创建引用并绑定到某个具体组件上,从而实现与资源ID相似的功能,每个组件都可以利用其他组件的引用获取到其他组件的摆放位置信息,从而确定自己摆放位置。

Compose 创建约束布局的方式有两种,分别时createRef()和createRefs(),根据字面意思我们就可以很清楚的知道,createRef(),每次只会创建一个引用,而createRefs()每次可以创建多个引用(最多可以创建16个),创建引用的方式如下:

// 创建单个引用
 val text = createRef()
// 创建多个引用
 val (button1,button2,text) = createRefs()

2.绑定引用

当我们创建完引用后就可以使用Modifier.constrainAs()修饰符将我们创建的引用绑定到某个具体组件上,可以在contrainAs尾部Lambda内指定组件的约束信息。我们需要注意的是,我们只能在ConstraintLayout尾部的Lambda中使用createRefer,createRefs函数创建引用,并使用Modifier.constrainAs函数来绑定引用,因为ConstrainScope尾部的Lambda的Reciever是一个ConstraintLayoutScope作用域对象。我们可以先看下面一段代码了解下约束布局的引用绑定:

@Composable
fun ConstrainLayoutDemo()
{
    ConstraintLayout(modifier = Modifier
        .width(300.dp)
        .height(100.dp)
        .padding(5.dp)
        .border(5.dp, color = Color.Red)) {
        val portraitImageRef = remember {
            createRef()
        }

        Image(painter = painterResource(id = R.drawable.portrait)
            , contentDescription = null,
        modifier = Modifier.constrainAs(portraitImageRef){
            top.linkTo(parent.top)
            bottom.linkTo(parent.bottom)
            start.linkTo(parent.start)
        })

    }
}

运行结果:
在这里插入图片描述

上面的代码是实现一个用户卡片的部分代码,从代码中看到我们使用约束的时候需要用到Modifier.constrainsAs(){……}的方式。Modifier.constrainsAs的尾部Lambda是一个ConstrainScope作用域对象,可以在其中获取当前组件的parent,top,bottom,start,end等信息。并使用linkTo指定组件约束。在上面的界面中,我们希望用户的头像可以居左对齐,所以将top拉伸至父组件的顶部,bottom拉伸至父组件的底部,start拉伸至父组件的左边。我们再为卡片添加上用户的昵称和描述,全部代码如下所示:

@Composable
fun ConstrainLayoutDemo()
{
    ConstraintLayout(modifier = Modifier
        .width(300.dp)
        .height(100.dp)
        .padding(5.dp)
        .border(5.dp, color = Color.Red)) {
        val (portraitImageRef,usernameTextRef,descriptionTextRef) = remember {
            createRefs()
        }

        Image(painter = painterResource(id = R.drawable.portrait)
            , contentDescription = null,
        modifier = Modifier.constrainAs(portraitImageRef){
            top.linkTo(parent.top)
            bottom.linkTo(parent.bottom)
            start.linkTo(parent.start)
        })

        Text(text = "旅游小美女", fontSize = 16.sp, maxLines = 1,
        textAlign = TextAlign.Left,
        modifier = Modifier.constrainAs(usernameTextRef){
            top.linkTo(portraitImageRef.top)
            start.linkTo(portraitImageRef.end,10.dp)
        })

        Text(text = "个人描述。。。。。。。。", fontSize = 14.sp,
            color = Color.Gray,
            fontWeight = FontWeight.Light,
            modifier = Modifier.constrainAs(descriptionTextRef){
                top.linkTo(usernameTextRef.bottom,5.dp)
                start.linkTo(portraitImageRef.end,10.dp)
            }
        )
    }
}

运行结果:
在这里插入图片描述
在上面的代码中我们也可以在ConstrainScope中指定组件的宽高信息,在ConstrainScope中直接设置width与height的可选值如下所示:

在ConstrainScope中指定组件的宽高信息时,通过在Modifier.constrainAs(xxxRef){width = Dimension.可选值}来设置,可选值如下:
Dimension.wrapContent: 实际尺寸为根据内容自适应
Dimension.matchParent: 实际尺寸为铺满父组件的尺寸
Dimension,wrapContent: 实际尺寸为根据约束信息拉伸后的尺寸
Dimension.preferredWrapContent: 如果剩余空间大于更具内容自适应的尺寸时,实际尺寸为自适应的尺寸,如果剩余空间小于内容自适应尺寸时,实际尺寸为剩余空间尺寸
Dimension.ratio(String): 根据字符串计算实际尺寸所占比率:如1 :2
Dimension.percent(Float): 根据浮点数计算实际尺寸所占比率
Dimension.value(Dp): 将尺寸设置为固定值
Dimension.perferredValue(Dp): 如果剩余空间大于固定值时,实际尺寸为固定值,如果剩余空间小于固定值时,实际尺寸则为剩余空间尺寸

我们想象下,假如用户的昵称特别长,那么按照我们上面的代码展示则会出现展示不全的问题,所以我们可以通过设置end来指定组件所允许的最大宽度,并将width设置为preferredWrapContent,意思是当用户名很长时,实际的宽度会做自适应调整。我们将上面展示用户名的地方改一下,代码如下:

// 上面的代码只用改这个部分
   Text(
            text = "旅游小美女美美美美美名字很长长长长长长长长长",
            fontSize = 16.sp,
        textAlign = TextAlign.Left,
        modifier = Modifier.constrainAs(usernameTextRef){
            top.linkTo(portraitImageRef.top)
            start.linkTo(portraitImageRef.end,10.dp)
            end.linkTo(parent.end,10.dp)
            width = Dimension.preferredWrapContent
        })

运行结果:
在这里插入图片描述

辅助布局工具

在传统View的约束布局中有Barrier,GuideLine等辅助布局的工具,在Compose中也继承了这些特性,方便我们完成各种复杂场景的布局需求。

1.Barrier分界线

Barrier顾名思义就是一个屏障,使用它可以隔离UI布局上面的一些相互挤压的影响,举一个例子,比如我们希望两个输入框左对齐摆放,并且距离文本组件中的最长者仍保持着10dp的间隔,当用户名和密码等发生变化时,输入框的位置能够自适应调整。这里使用Barrier特性可以简单的实现这一需求:

@Composable
fun InputFieldLayout(){
    ConstraintLayout(
        modifier = Modifier
            .width(400.dp)
            .padding(10.dp)
    ) {
        val (usernameTextRef, passwordTextRef, usernameInputRef, passWordInputRef) = remember { createRefs() }
        val barrier = createEndBarrier(usernameTextRef, passwordTextRef)
        Text(
            text = "用户名",
            fontSize = 14.sp,
            textAlign = TextAlign.Left,
            modifier = Modifier
                .constrainAs(usernameTextRef) {
                    top.linkTo(parent.top)
                    start.linkTo(parent.start)
                }
        )

        Text(
            text = "密码",
            fontSize = 14.sp,
            modifier = Modifier
                .constrainAs(passwordTextRef) {
                    top.linkTo(usernameTextRef.bottom, 20.dp)
                    start.linkTo(parent.start)
                }
        )
        OutlinedTextField(
            value = "",
            onValueChange = {},
            modifier = Modifier.constrainAs(usernameInputRef) {
                start.linkTo(barrier, 10.dp)
                top.linkTo(usernameTextRef.top)
                bottom.linkTo(usernameTextRef.bottom)
                height = Dimension.fillToConstraints
            }
        )
        OutlinedTextField(
            value = "",
            onValueChange = {},
            modifier = Modifier.constrainAs(passWordInputRef) {
                start.linkTo(barrier, 10.dp)
                top.linkTo(passwordTextRef.top)
                bottom.linkTo(passwordTextRef.bottom)
                height = Dimension.fillToConstraints
            }
        )
    }
}

运行结果:
在这里插入图片描述

2.Guideline引导线

Barrier分界线需要依赖其他引用,从而确定自身的位置,而使用Guideline不依赖任何引用,例如,我们希望将用户头像摆放在距离屏幕顶部2:8的高度位置,头像以上是用户背景,以下是用户信息,这样的需求就可以使用Guideline实现,代码如下:

@Composable
fun GuidelineDemo(){
   ConstraintLayout(modifier = Modifier
       .height(300.dp)
       .background(color = Color.Gray)) {
       val guideline = createGuidelineFromTop(0.2f)
       val (userPortraitBackgroundRef,userPortraitImgRef,welcomeRef) = remember {
           createRefs()
       }

       Box(modifier = Modifier
           .constrainAs(userPortraitBackgroundRef) {
               top.linkTo(parent.top)
               bottom.linkTo(guideline)
               height = Dimension.fillToConstraints
               width = Dimension.matchParent
           }
           .background(Color(0xFF673AB7)))

       Image(painter = painterResource(id = R.drawable.portrait),
           contentDescription = null,
           modifier = Modifier
               .constrainAs(userPortraitImgRef) {
                   top.linkTo(guideline)
                   bottom.linkTo(guideline)
                   start.linkTo(parent.start)
                   end.linkTo(parent.end)
               }
               .size(100.dp)
               .clip(CircleShape)
               .border(width = 2.dp, color = Color(0xFF96659E), shape = CircleShape))

       Text(text = "不喝奶茶的小白兔",
       color = Color.White,
       fontSize = 26.sp,
       modifier = Modifier.constrainAs(welcomeRef){
           top.linkTo(userPortraitImgRef.bottom,10.dp)
           start.linkTo(parent.start)
           end.linkTo(parent.end)
       })

   }
}

运行结果:
在这里插入图片描述

在上面的代码中,我们使用createGuidelineFromTop()方法创建了一条从顶部出发的引导线,然后用户背景就可以依赖这条引导线确定宽高了,然后对于头像,我们只需要将top和bottom连接至引导线即可

3.Chain链接约束

ContraintLayout的另一个好用的特性就是Chain链接约束,通过链接约束可以允许多个组件平均分配布局空间,类似于weight修饰符。例如我们要展示一首古诗,用Chain链接约束实现如下:

@Composable
fun showQuotesDemo() {
    ConstraintLayout(
        modifier = Modifier
            .size(400.dp)
            .background(Color.Black)
    ) {
        val (quotesFirstLineRef, quotesSecondLineRef, quotesThirdLineRef, quotesForthLineRef) = remember {
            createRefs()
        }

        createVerticalChain(
            quotesFirstLineRef, quotesSecondLineRef, quotesThirdLineRef, quotesForthLineRef,
            chainStyle = ChainStyle.Spread
        )

        Text(text = "窗前明月光,",
            color = Color.White,
            fontSize = 20.sp,
            fontWeight = FontWeight.Bold,
            modifier = Modifier.constrainAs(quotesFirstLineRef) {
                start.linkTo(parent.start)
                end.linkTo(parent.end)
            })

        Text(text = "疑是地上霜。",
            color = Color.White,
            fontSize = 20.sp,
            fontWeight = FontWeight.Bold,
            modifier = Modifier.constrainAs(quotesSecondLineRef) {
                start.linkTo(parent.start)
                end.linkTo(parent.end)
            })

        Text(text = "举头望明月,",
            color = Color.White,
            fontSize = 20.sp,
            fontWeight = FontWeight.Bold,
            modifier = Modifier.constrainAs(quotesThirdLineRef) {
                start.linkTo(parent.start)
                end.linkTo(parent.end)
            })

        Text(text = "低头思故乡。",
            color = Color.White,
            fontSize = 20.sp,
            fontWeight = FontWeight.Bold,
            modifier = Modifier.constrainAs(quotesForthLineRef) {
                start.linkTo(parent.start)
                end.linkTo(parent.end)
            })
    }
}

运行结果:
在这里插入图片描述
如上面代码所示,我们要展示四句诗就需要创建四个引用对应四句诗,然后我们就可以创建一条垂直的链接约束将四句诗词连接起来,创建链接约束时末尾参数可以传一个ChainStyle,用来表示我们期望的布局样式,它的取值有三个,效果和意义如下所示:

(1)Spread:链条中的每个元素平分整个父空间

 createVerticalChain(
            quotesFirstLineRef, quotesSecondLineRef, quotesThirdLineRef, quotesForthLineRef,
            chainStyle = ChainStyle.Spread)

运行效果:

在这里插入图片描述

(2)SpreadInside:链条中的首尾元素紧贴边界,剩下的每个元素平分整个父空间

 createVerticalChain(
            quotesFirstLineRef, quotesSecondLineRef, quotesThirdLineRef, quotesForthLineRef,
            chainStyle = ChainStyle.SpreadInside)

运行效果:

在这里插入图片描述

(3)Packed:链条中的所有元素都聚集到中间,效果如下

 createVerticalChain(
            quotesFirstLineRef, quotesSecondLineRef, quotesThirdLineRef, quotesForthLineRef,
            chainStyle = ChainStyle.Packed)

运行效果:

在这里插入图片描述

总结

关于Compose约束布局的内容就是这些了,本文主要是简单的介绍了Compose中约束布局的基本使用,要熟练掌握Compose约束布局,还需要读者多去联系,多使用约束布局写界面,这样就会熟能生巧,在此我只做一个抛砖引玉的活。有任何疑问,欢迎在评论区交流。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/15905.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

下载——安装——使用FinalShell

下载——安装——使用FinalShell FinalShell简介:下载:使用: FinalShell简介: FinalShell是一款免费的国产的集SSH工具、服务器管理、远程桌面加速的软件,同时支持Windows,macOS,Linux&#xf…

No.050<软考>《(高项)备考大全》【冲刺4】《软考之 119个工具 (2)》

《软考之 119个工具 (2)》 21.检查:22.偏差分析:23.滚动式规划:24.紧前关系绘图法(PDM):25.确定依赖关系:26.时间提前量与滞后量:28.发布的估算数据:29.自下而上估算:30.项目管理软件:31.储备分析:32.类比估算:33.参数估算:34.三点估算:35.进度网络分析:…

JS实现拼音(字母)匹配(搜索)汉字(姓名)

这就是个模糊查询,我们平常做的都是直接输入汉字去把对应的值过滤出来,但我还真是第一次通过拼音去查询(当然不只是拼音,汉字也是可以的),以前还真没注意这个。唉,这可咋搞,我怎么知…

使用Statsmodel进行假设检验和线性回归

如果你使用 Python 处理数据,你可能听说过 statsmodel 库。Statsmodels 是一个 Python 模块,它提供各种统计模型和函数来探索、分析和可视化数据。该库广泛用于学术研究、金融和数据科学。在本文中,我们将介绍 statsmodel 库的基础知识、如何…

(七)ArcCatalog应用基础——图层操作与数据输出

(七)ArcCatalog应用基础——图层操作与数据输出 目录 (七)ArcCatalog应用基础——图层操作与数据输出 1.地图与图层操作1.1创建图层1.2设置文件特征1.3保存独立的图层文件 2.地理数据输出2.1输出为Shapefile2.2输出为Coverage2.3属…

笔记本电脑没有声音了怎么恢复

笔记本电脑 在使用的过程中,突然没有声音的话,对于人们来说会很麻烦。那么笔记本电脑没有声音了怎么恢复呢?下面小编为大家整理了笔记本电脑没有声音的恢复方法,一起来看看吧。 方法/步骤: 方法一:网络适配器检查音频…

UE5实现建筑剖切效果

文章目录 1.实现目标2.实现过程2.1 材质参数集2.2 材质遮罩函数2.3 更新Box3.参考资料1.实现目标 基于BoxMask材质节点,在UE5中实现建筑物的剖切效果,GIF动图如下: 2.实现过程 实现原理与之前“BoxMask实现建筑生长效果”的原理相同,都是基于BoxMask材质节点实现。 具体实…

操作系统之内存管理

连续分配 一、单一连续 直接为要运行的进程分配一个内存,只适合单任务,只能用于单对象、单任务,内存被分配为系统区和用户区,系统区在低地址,用户区是一个用户独享 二、等分分区 由于分配一个内存只能执行单任务&a…

MongoDB【常用命令】

目录 1:基本常用命令 1.1:演示案例 1.2:数据库操作 1.2.1:选择和创建数据库,查看当前正在使用的数据库命令 1.2.2:数据库的删除 1.3:集合操作 1.3.1:集合的显式创建&#xff0…

C++ srand()和rand()用法

参考C rand 与 srand 的用法 计算机的随机数都是由伪随机数,即是由小M多项式序列生成的,其中产生每个小序列都有一个初始值,即随机种子。(注意: 小M多项式序列的周期是65535,即每次利用一个随机种子生成的随…

【机器学习】HOG+SVM实现行人检测

文章目录 一、准备工作1. 下载数据集2. 解压数据集 二、HOG特征简介1. 梯度(Gradient)2. 格子(Cell)3. 块归一化(Block Normalization)4. HOG特征(HOG Feature)5. 使用skimage.featu…

docker容器原理及简单且详细的使用

docker原理简单介绍 docker是一种虚拟化容器技术。 虚拟化:早期为了节约成本和学习只有在宿主机中基于 kvm(基于内核的虚拟机)等技术虚拟出来完整的操作系统,而这个完整的操作系统会大量的占用宿主机的硬件资源,当创建…

Oracle LiveLabs实验:DB Security - Data Masking and Subsetting (DMS)

概述 本实验介绍了适用于 Enterprise Manager 的 Oracle 数据屏蔽和子集 (DMS) 包的各种特性和功能。 它使用户有机会学习如何配置这些功能,以便在非生产环境中保护他们的敏感数据。 此实验申请地址在这里,时间为60分钟。 本实验也是DB Security Adva…

无惧黑暗强光,纯视觉导航也能全天候作业

对于一台激光导航扫地机器人而言,全天候作业并非难事,那么纯视觉导航扫地机器人能做到吗? 无论对于人,还是机器人,光线环境的变化对“眼睛”的影响都是致命的。由于视觉传感器对于光线十分敏感,在家庭场景…

linux入门---软硬链接

软链接 使用指令ln -s 被链接的文件 生成的软链接文件 便可以创建软连接文件,ln是link的简写表明当前要创建链接文件,s是soft的简写表明当前创建的链接文件为软链接文件,然后加上被链接的文件,最后写上生成的链接文件的文件名比如…

使用 ArcGIS Pro 进行土地利用分类的机器学习和深度学习

随着技术进步,尤其是地理信息系统 (GIS)工具的进步,可以更有效地对土地利用进行分类。分类的使用可用于识别植被覆盖变化、非法采矿区和植被抑制区域,这些只是土地利用分类的众多示例中的一部分。 分类的一大困难是确定要解决的问题的级别。我分类的目的是什么?分类是否需…

【科普知识】电机的10种工作制说明:S1~S10

如今,在我们的生活中,电机几乎无处不在,从国防、工农、运输、临床器械、通讯到生活中的洗衣机、风扇、吸尘器、电动机器人等,都在应用着各式各样的电动机。 电机作为一种能够将电能转换成机械能的装置,是现代工业生产和…

【搭建私有云盘】无公网IP,在外远程访问本地微力同步

文章目录 1.前言2. 微力同步网站搭建2.1 微力同步下载和安装2.2 微力同步网页测试2.3 cpolar的安装和注册 3.本地网页发布3.1 Cpolar云端设置3.2 Cpolar本地设置 4. 公网访问测试5. 结语 1.前言 私有云盘作为云存储概念的延伸,虽然谈不上多么新颖,但是其…

快速上手非关系型数据库Redis

一、Redis介绍 1.非关系型数据库,纯内存操作,key-value存储,性能很高,可持久化(内存---->保存到硬盘上) 2.缓存,计数器,验证码,geo地理位置信息,发布订阅…

RocketMq windows 安装

RocketMq安装步骤: 1、直接在官网下载。也可以从这里自取 https://rocketmq.apache.org/download/ 2、修改bin目录下的文件 runserver.cmd 和 runbroker.cmd文件。主要修改所占用内存的大小。 runserver.cmd 修改如下: runbroker.cmd 修改如下&#xff…