Compose常用布局
Compose布局基础知识
上一节对Compose做了简单的介绍,本章节主要介绍Compose中常用的布局,其中包括三个基础布局(Colmun、Row、Box);以及其他常用布局(ConstraintLayout 、BoxWithConstraints、HorizontalPager、VerticalPager、LazyColumn、 LazyRow ) 等,学习了本章您将会可以自己写出各种炫酷的布局。完成Compose中各种UI开发。
Jetpack Compose可以让您更轻松地设计和构建应用的界面。Compose通过以下方法将状态转换为界面元素:
1、元素的组合
2、元素的布局
3、元素的绘制
Compose中布局的目标
布局系统的Jetpack Compose实现有二个主要目标:
(1)实行高性能
(2)让开发者能够轻松编写自定义布局
注意:使用Android View系统时,在嵌套某些View(如RelativeLayout)时,可能会出现一些性能问题。由于Compose可以避免多次测量,因为可以根据需要进行深层次的嵌套,而不会影响性能。
这里留下疑问请问为什么呢?为什么Compose不会引起性能问题?带着这些疑问我们进入这篇文章。
可组合函数的基础知识
可组合函数是Compose的基础构建块。可组合函数是一种发出Unit的函数,用于描述界面中的某一部分。该函数接受一些输入并生成屏幕上显示的内容。
一个可组合函数可能会发出多个界面元素。不过,如果您没有提供有关如何排列这些元素的指导,Compose可能会以您不喜欢的方式进行拍了他们。例如,一下代码会生成二个文本元素:
@Composable
fun ArtistCard() {Text("Alfred Sisley")Text("3 minutes ago")
}
如果您未提供有关如何排列这个两个文本元素的指导,Compose会将它们堆叠在一起,使其无法阅读:

我们可以看到是混排在一起的。而Compose提供了一些系列现成可用的布局来帮助我们排列界面元素,并可以让您轻松定义自己的更专业的布局。
标准布局组件
在许多情况下,我们只需要使用Compose的标准布局元素即可。
1、Colmun布局
使用Column可将多个项垂直地放在屏幕上。这个布局类似安卓View中的LinearLayout布局设android:orientation=“vertical” 时候的场景,纵向排布。
@Composable
fun ArtistCard() {Column {Text("姓名:LSL")Text("性别:男")}
}

2、Row布局
同样,使用Row可以将多个项水平放置在屏幕上。Column和Row都支持配置它们的所含元素的对齐方式。Row这个布局类似与安卓view中的LineraLayout中android:orientation="horizontal"设置横向排布。
@Composable
fun ArtistCard() {Row {Text("姓名:LSL")Text("年龄:25")}
}

Column和Row包含的在屏幕上的纵向排布和横向排布。上边已经了解了Column和Row的基本使用。
3、Box布局
使用Box可以将元素放在其他元素上。Box还支持为其包含的元素配置特定的对齐方式。此处的modifier不做详细讲解,后边章节会详细描述。
@Composable
fun ArtistCard() {Box(modifier = Modifier.size(44.dp)) {Image(painter = painterResource(id = R.mipmap.head),contentDescription = "11",modifier = Modifier.size(44.dp).clip(CircleShape))Image(painter = painterResource(id = R.mipmap.icon),contentDescription = "22",modifier = Modifier.align(Alignment.BottomEnd))}
}

Box布局类似安卓View中的FrameLayout布局,可以重叠布局。
通常、我们只需要这些构函数。我们自行编写可组合函数,将这些布局组合成更精美的布局,让其适合我们的应用。

注意:Compose可以有效地处理嵌套布局,堪称设计复杂界面的绝佳工具。这与Android View相比是一个进步;在Android View 中,出于性能方面的原因,您避免使用嵌套布局。
其他常用布局
1、ConstraintLayout约束布局
原来ConstraintLayout 平铺约束布局,为了解决View嵌套的问题。
(1)先引入constraintlayout包
implementation "androidx.constraintlayout:constraintlayout-compose:1.0.1"
(2)ConstraintLayout 在Compose中的使用
可以相对父布局、其他函数布局进行相对约束,这个布局比较自由。每个布局都可以相对于其他布局设置位置。下面是一个简单的ConstraintLayout 的使用。
首先 val (button, text) = createRefs(),创建多个ConstraintLayoutReferences的方便方法,这些引用需要作为Modifier.constrainAs的一部分分配给ConstraintLayout内的布局。因为Compose中没有ID属性,所以这里设置Modifier.constrainAs(button) 相当于 Button按钮的唯一标识的属性了。其他布局可以根据这个唯一标识找到它,并根据这个布局进行布局。
Button按钮设置 top.linkTo(parent.top, margin = 16.dp) 相对于父布局顶部、距离付布局16dp; start.linkTo(parent.start)相对于父布局的开始位置, end.linkTo(parent.end)相对于父布局的结束位置。start和end的设置使得如下图Button位置就居中了。
Text文本的布局设置 top.linkTo(button.bottom, margin = 16.dp) 位于Button的底部,距离Button 16dp。
设置start.linkTo(button.start)时Text文本布局位于Button开始位置,设置 end.linkTo(button.end)时Text文本布局位于Button的结束位置。
通过上面例子可以看出,在ConstraintLayout布局中,可以任意设置相对于某个布局位置,布局相对灵活。给我们在界面布局中提供的了很大的方便的,使用也比较简单。
@Preview
@Composable
fun ConstraintLayoutCompose() {ConstraintLayout(modifier = Modifier.fillMaxWidth().height(100.dp).background(Color(0xff00ffff))) {val (button, text) = createRefs()Button(onClick = {}, modifier = Modifier.constrainAs(button) {top.linkTo(parent.top, margin = 16.dp)start.linkTo(parent.start)end.linkTo(parent.end)}) {Text("Button")}Text("Android", Modifier.constrainAs(text) {top.linkTo(button.bottom, margin = 16.dp)start.linkTo(button.start)end.linkTo(button.end)})}
}

2、BoxWithConstraints 约束Box布局
BoxWithConstraints 带有约束布局的参数可以根据可用空间的大小来决定布局的内容。布局的尺寸信息是到布局阶段才可以使用的,也就是说这些布局在组合阶段无法确定显示的内容。
BoxWithConstraints 布局是相对于Box而言的。BoxWithConstraints 可以将内容组合推迟到布局阶段,此时布局信息已经确定,可以根据BoxWithConstraintsScope参数设置不同的布局。
interface BoxWithConstraintsScope : BoxScope {/*** The constraints given by the parent layout in pixels.** Use [minWidth], [maxWidth], [minHeight] or [maxHeight] if you need value in [Dp].*/val constraints: Constraints/*** The minimum width in [Dp].** @see constraints for the values in pixels.*/val minWidth: Dp/*** The maximum width in [Dp].** @see constraints for the values in pixels.*/val maxWidth: Dp/*** The minimum height in [Dp].** @see constraints for the values in pixels.*/val minHeight: Dp/*** The maximum height in [Dp].** @see constraints for the values in pixels.*/val maxHeight: Dp
}
根据布局的最大宽度展示,小、中、大四个布局了。这个可以更好适配不同的屏幕。
@Composable
fun BoxWithConstraintsLayout() {BoxWithConstraints(modifier = Modifier.fillMaxWidth().height(100.dp).background(Color(0xffef5a54)),contentAlignment = Alignment.Center) {when {maxWidth < 150.dp -> SmallLayout()maxWidth < 250.dp -> MediumLayout()else -> BigLayout()}}
}
3、HorizontalPager和VerticalPager 分页布局
HorizontalPager和VerticalPager 相对于安卓View组件来说,对应的是ViewPager。
HorizontalPager可以使界面左右滑动、VerticalPager 可以使界面上下滑动。
@OptIn(ExperimentalFoundationApi::class)
@Preview
@Composable
fun HorizontalPagerCompose() {val state = rememberPagerState(5)HorizontalPager(10,state = state,pageSize = PageSize.Fixed(100.dp),pageSpacing = 30.dp) { page ->var scale by remember { mutableStateOf(0.5f) }if ((state.currentPage + 1) == page) {scale = 1f} else {scale = 0.75f}Column(modifier = Modifier.fillMaxWidth().height(200.dp).graphicsLayer {scaleX = scalescaleY = scale}) {Text(text = "HorizontalPager Page: $page",modifier = Modifier.fillMaxWidth().height(100.dp).background(colorList[page]).wrapContentSize(Alignment.Center).clickable {ToastUtil.toastShort(AppUtils.getContext(), "page $page")},color = Color(0xFFffffff))}}
}

VerticalPager上下滑动和HorizontalPager一样 只是滑动方向不同。
@Composable
fun VerticalPagerCompose() {VerticalPager(10, modifier = Modifier.height(100.dp)) { page ->Text(text = "VerticalPager Page: $page",modifier = Modifier.fillMaxWidth().height(100.dp).wrapContentSize(Alignment.Center))}
}
4、LazyColumn和 LazyRow 之列表布局
LazyColumn和LazyRow布局相对于安卓View布局来说,类似ListView和GridView,和现在常用的RecyclerView都类似都是实现列表展示的功能,使用也非常简单。
传入dataList即可展示所有的列表,MessageItemCard可以自定义自己的列表样式。
LazyColumn {items(dataList){ item: MessageModel ->MessageItemCard(item)}
}
这里写MessageItemCard每个条目的布局根据条目展示类似聊天界面的数据。如下图所示。
@Composable
fun MessageItemCard(it: MessageModel) {if (it.index % 2 == 0) {Row {Image(painter = painterResource(id = R.mipmap.star),contentDescription = "1111111",modifier = Modifier.size(44.dp).clip(CircleShape).border(1.5.dp, MaterialTheme.colorScheme.primary, CircleShape))Column(modifier = Modifier.padding(5.dp)) {Text(text = it.sender, fontSize = 18.sp)Text(text = it.content, fontSize = 13.sp)}}} else {Row(modifier = Modifier.fillMaxWidth().wrapContentSize(align = Alignment.CenterEnd)) {Column(modifier = Modifier.padding(5.dp)) {Text(text = it.sender, fontSize = 18.sp)Text(text = it.content, fontSize = 13.sp)}Image(painter = painterResource(id = R.mipmap.star),contentDescription = "1111111",modifier = Modifier.size(44.dp).clip(CircleShape))}}
}
最终展示效果

LazyRow的使用和LazyColumn使用一样只是方向不同。
Compose布局中的固有特性测量
Compose有一项规则,即,子项只能测量一次,测量两次就会引发运行时异常。但是,有时需要先收集一些关于子项的信息,然后在测量子项。
借助固有特性,我们可以先查询子项,然后在进行实际测量。
对于可组合项,您可以查询其intrinsicWidth或intrinsicHight:
(min|max)IntrinsicWidth:给定此高度,可以正确绘制内容的最小/最大宽度是多少?(min|max)IntrinsicHeight:给定此宽度,可以正确绘制内容的最小/最大高度是多少?
例如,如果您查询具有无限 width 的 Text 的 minIntrinsicHeight,它将返回 Text 的 height,就好像该文本是在单行中绘制的一样。
注意:请求固有特性测量不会两次测量子项。 系统在测量子项前会先查询其固有测量值,然后父项会根据这些信息计算测量其子项时使用的约束条件。
固有特性的实际运用
如果我们要实现如下图所效果,左边文字,右边文字,中间分割线效果。我们需要怎么做?

我可以可以将二个Text放在同一个Row中,并且最大程序扩展,另外在中间放置一个Divider。我们需要将分割线的高度设置为与最高的Text相同,粗细设置为1.dp
fun TwoTexts(text1: String,text2: String,modifier: Modifier = Modifier
) {Row(modifier = modifier) {Text(modifier = Modifier.weight(1f).padding(start = 4.dp).wrapContentWidth(Alignment.Start),text = text1)Divider(color = Color.Black,modifier = Modifier.fillMaxHeight().width(1.dp))Text(modifier = Modifier.weight(1f).padding(end = 4.dp).wrapContentWidth(Alignment.End),text = text2)}
}
运行效果,我们发现分割线扩展到整个屏幕,这并不是我们想要的效果:

性能问题
Compose通过只测量一次子项来实现高性能。单遍测量对性能有利,使Compose能够高效地处理较深得界面树。
如果某个元素测量了它的子元素两次、而该子元素又测量了它的子元素两次、以此类推,那么一次尝试布置整个界面就不得不做大量工作,这将很难让应用保持良好的性能。
如果布局由于某种原因需要多次测量,compose会提供一个特殊的系统,即‘固有特性测量’。如需详细了解此功能,请参阅Compose布局中的固有特性测量。
由于测量和放置是布局传递的不通子阶段,因此任何仅影响项的放置而不影响测量的更新都可以单独执行。
本章内容已经基本结束,主要介绍各种布局的使用,学习Compose中的布局,帮助您构建出各种需求的UI。如果您阅读过程中遇到各种问题,请随时给您宝贵的建议。
上一章节内容 Compose入门
相关文章:
Compose常用布局
Compose布局基础知识 上一节对Compose做了简单的介绍,本章节主要介绍Compose中常用的布局,其中包括三个基础布局(Colmun、Row、Box);以及其他常用布局(ConstraintLayout 、BoxWithConstraints、HorizontalP…...
使用keytool查看Android APK签名
文章目录 一、找到JDK位置二、使用方法2.1 打开windows命令行工具2.2 查看签名 三、如何给APK做系统签名呢? 一、找到JDK位置 安卓AS之后,可选择继续安装JDK,如本文使用amazon版本默认位置:C:\Users\66176.jdks\corretto-1.8.0_342可通过自…...
数据库学习日常案例20231221-oracle libray cache lock分析
1 问题概述: 阻塞的源头为两个ddl操作导致大量的libray cache lock 其中1133为gis sde的create table as语句。 其中697为alter index语句。...
【数据结构】最短路径算法实现(Dijkstra(迪克斯特拉),FloydWarshall(弗洛伊德) )
文章目录 前言一、Dijkstra(迪克斯特拉)1.方法:2.代码实现 二、FloydWarshall(弗洛伊德)1.方法2.代码实现 完整源码 前言 最短路径问题:从在带权有向图G中的某一顶点出发,找出一条通往另一顶点…...
算法模板之队列图文详解
🌈个人主页:聆风吟 🔥系列专栏:算法模板、数据结构 🔖少年有梦不应止于心动,更要付诸行动。 文章目录 📋前言一. ⛳️模拟队列1.1 🔔用数组模拟实现队列1.1.1 👻队列的定…...
[node]Node.js 中REPL简单介绍
[node]Node.js 中REPL简单介绍 什么是REPL为什么使用REPL如何使用REPL 命令REPL模式node的全局内容展示node全局所有模块查看全局模块具体内容其它命令 实践 什么是REPL Node.js REPL(Read Eval Print Loop:交互式解释器) 表示电脑的环境,类似 Windows 系统的终端或…...
AtomHub 开源容器镜像中心开放公测,国内服务稳定下载
由开放原子开源基金会主导,华为、浪潮、DaoCloud、谐云、青云、飓风引擎以及 OpenSDV 开源联盟、openEuler 社区、OpenCloudOS 社区等成员单位共同发起建设的 AtomHub 可信镜像中心正式开放公测。AtomHub 秉承共建、共治、共享的理念,旨在为开源组织和开…...
java8实战 lambda表达式、函数式接口、方法引用双冒号(中)
前言 书接上文,上一篇博客讲到了lambda表达式的应用场景,本篇接着将java8实战第三章的总结。建议读者先看第一篇博客 其他函数式接口例子 上一篇有讲到Java API也有其他的函数式接口,书里也举了2个例子,一个是java.util.functi…...
FPGA高端项目:UltraScale GTH + SDI 视频编解码,SDI无缓存回环输出,提供2套工程源码和技术支持
目录 1、前言免责声明 2、相关方案推荐我这里已有的 GT 高速接口解决方案我目前已有的SDI编解码方案 3、详细设计方案设计框图3G-SDI摄像头LMH0384均衡EQUltraScale GTH 的SDI模式应用UltraScale GTH 基本结构参考时钟的选择和分配UltraScale GTH 发送和接收处理流程UltraScale…...
为什么react call api in cDidMount
为什么react call api in cDM 首先,放到constructor或者cWillMount不是语法错误 参考1 参考2 根据上2个参考,总结为: 1、官网就是这么建议的: 2、17版本后的react 由于fiber的出现导致 cWM 会调用多次! cWM 方法已…...
openGauss学习笔记-171 openGauss 数据库运维-备份与恢复-导入数据-深层复制
文章目录 openGauss学习笔记-171 openGauss 数据库运维-备份与恢复-导入数据-深层复制171.1 使用CREATE TABLE执行深层复制171.1.1 操作步骤 171.2 使用CREATE TABLE LIKE执行深层复制171.2.1 操作步骤 171.3 通过创建临时表并截断原始表来执行深层复制171.3.1 操作步骤 openGa…...
[kubernetes]控制平面ETCD
什么是ETCD CoreOS基于Raft开发的分布式key-value存储,可用于服务发现、共享配置以及一致性保障(如数据库选主、分布式锁等)etcd像是专门为集群环境的服务发现和注册而设计,它提供了数据TTL失效、数据改变监视、多值、目录监听、…...
序列化类的高级用法
1.3.3 模型类序列化器 如果我们想要使用序列化器对应的是Django的模型类,DRF为我们提供了ModelSerializer模型类序列化器来帮助我们快速创建一个Serializer类。 ModelSerializer与常规的Serializer相同,但提供了: 基于模型类自动生成一系列…...
4.svn版本管理工具使用
1. 什么是SVN 版本控制 它可以记录每一次文件和目录的修改情况,这样就可以借此将数据恢复到以前的版本,并可以查看数据的更改细节! Subversion(简称SVN)是一个自由开源的版本控制系统。在Subversion管理下,文件和目录可以超越时空 SVN的优势 统一的版本号 Subversi…...
ZKP Algorithms for Efficient Cryptographic Operations 1 (MSM Pippenger)
MIT IAP 2023 Modern Zero Knowledge Cryptography课程笔记 Lecture 6: Algorithms for Efficient Cryptographic Operations (Jason Morton) Multi-scalar Multiplication(MSM) Naive: nP (((P P) P) P)… (2(2P))…Binary expand $n e_0e_1\alphae_2\alpha2\dots\e_{\…...
Windows系统安装 ffmpeg
下载及解压 ffmpeg官方下载地址:https://ffmpeg.org/download.html 下载好后将其解压至你想保存的位置中。 环境变量设置 打开Windows设置,在搜索框输入:系统高级设置。 新建环境变量,并输入bin目录具体位置。 安装检查 按住 w…...
油猴脚本教程案例【键盘监听】-编写 ChatGPT 快捷键优化
文章目录 1. 元数据namenamespaceversiondescriptionauthormatchgranticon 2. 编写函数.1 函数功能2.1.1. input - 聚焦发言框2.1.2. stop - 取消回答2.1.3. newFunction - 开启新窗口2.1.4. scroll - 回到底部 3. 监听键盘事件3.1 监听X - 开启新对话3.2 监听Z - 取消回答3.3 …...
数据结构 | 查漏补缺
目录 数据的基本单位 冒泡排序 DFS和BFS中文 Prim 比较 中序线索二叉树 顺序栈 链栈 时间复杂度 循环队列 求第K个结点的值 数据的基本单位 数据元素 循环队列sq中,用数组elem[0‥25]存放数据元素,设当前sq->front为20,sq-&g…...
回溯算法练习题
78. 子集 中等 1.9K 相关企业 给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。 解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。 示例 1: 输入:nums [1,2,3] 输出&#x…...
代码随想录算法训练营 | day60 单调栈 84.柱状图中最大的矩形
刷题 84.柱状图中最大的矩形 题目链接 | 文章讲解 | 视频讲解 题目:给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。 求在该柱状图中,能够勾勒出来的矩形的最大面积。 1 < heights.len…...
Vim 调用外部命令学习笔记
Vim 外部命令集成完全指南 文章目录 Vim 外部命令集成完全指南核心概念理解命令语法解析语法对比 常用外部命令详解文本排序与去重文本筛选与搜索高级 grep 搜索技巧文本替换与编辑字符处理高级文本处理编程语言处理其他实用命令 范围操作示例指定行范围处理复合命令示例 实用技…...
为什么需要建设工程项目管理?工程项目管理有哪些亮点功能?
在建筑行业,项目管理的重要性不言而喻。随着工程规模的扩大、技术复杂度的提升,传统的管理模式已经难以满足现代工程的需求。过去,许多企业依赖手工记录、口头沟通和分散的信息管理,导致效率低下、成本失控、风险频发。例如&#…...
Java 加密常用的各种算法及其选择
在数字化时代,数据安全至关重要,Java 作为广泛应用的编程语言,提供了丰富的加密算法来保障数据的保密性、完整性和真实性。了解这些常用加密算法及其适用场景,有助于开发者在不同的业务需求中做出正确的选择。 一、对称加密算法…...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一)
宇树机器人多姿态起立控制强化学习框架论文解析 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一) 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化…...
GitHub 趋势日报 (2025年06月08日)
📊 由 TrendForge 系统生成 | 🌐 https://trendforge.devlive.org/ 🌐 本日报中的项目描述已自动翻译为中文 📈 今日获星趋势图 今日获星趋势图 884 cognee 566 dify 414 HumanSystemOptimization 414 omni-tools 321 note-gen …...
全志A40i android7.1 调试信息打印串口由uart0改为uart3
一,概述 1. 目的 将调试信息打印串口由uart0改为uart3。 2. 版本信息 Uboot版本:2014.07; Kernel版本:Linux-3.10; 二,Uboot 1. sys_config.fex改动 使能uart3(TX:PH00 RX:PH01),并让boo…...
浪潮交换机配置track检测实现高速公路收费网络主备切换NQA
浪潮交换机track配置 项目背景高速网络拓扑网络情况分析通信线路收费网络路由 收费汇聚交换机相应配置收费汇聚track配置 项目背景 在实施省内一条高速公路时遇到的需求,本次涉及的主要是收费汇聚交换机的配置,浪潮网络设备在高速项目很少,通…...
搭建DNS域名解析服务器(正向解析资源文件)
正向解析资源文件 1)准备工作 服务端及客户端都关闭安全软件 [rootlocalhost ~]# systemctl stop firewalld [rootlocalhost ~]# setenforce 0 2)服务端安装软件:bind 1.配置yum源 [rootlocalhost ~]# cat /etc/yum.repos.d/base.repo [Base…...
比较数据迁移后MySQL数据库和OceanBase数据仓库中的表
设计一个MySQL数据库和OceanBase数据仓库的表数据比较的详细程序流程,两张表是相同的结构,都有整型主键id字段,需要每次从数据库分批取得2000条数据,用于比较,比较操作的同时可以再取2000条数据,等上一次比较完成之后,开始比较,直到比较完所有的数据。比较操作需要比较…...
Golang——7、包与接口详解
包与接口详解 1、Golang包详解1.1、Golang中包的定义和介绍1.2、Golang包管理工具go mod1.3、Golang中自定义包1.4、Golang中使用第三包1.5、init函数 2、接口详解2.1、接口的定义2.2、空接口2.3、类型断言2.4、结构体值接收者和指针接收者实现接口的区别2.5、一个结构体实现多…...
