Compose知识分享
前言
“Jetpack Compose 是一个适用于 Android 的新式声明性界面工具包。Compose 提供声明性 API,让您可在不以命令方式改变前端视图的情况下呈现应用界面,从而使编写和维护应用界面变得更加容易。”
以上是Compose官网中对于Compose这套全新的Android UI库的介绍,Compose的到来为Android引入了声明式、响应式的全新开发方式,直接以函数调用的形式使用组件,取代了原先冗长的Activity定义以及.xml文件编写;这一点也是当前TDF团队Kuikly框架同样所具备的能力,给予声明式、响应式的特性加速用户开发,并且Kuikly直接复用了Android的view体系,与Compose以全新的Composable以及Compose树而言,一定程度上可以加速新用户对于底层的理解。
Compose执行生命周期
我们继续Compose,Compose在声明式函数的基础上,引入了重组的能力,结合后续的度量策略,Compose UI在发生变化时,会被系统的快照系统SnapManager监测到,并使用协程管道的方式获取信息,然后执行重组动作,重组动作会对状态变更的UI元素进行重新渲染,而不修改状态未改变的其余UI元素。
Compose程序的执行之后,一直到页面渲染出结果以及后续的持续UI变更,共可以分为4个阶段,分别是“组合 --> 布局 --> 渲染 <--> 重组”;
组合阶段
“在组合阶段,Compose 运行时会执行可组合函数,并 输出一个表示界面的树结构。这个界面树由 包含下一阶段所需的所有信息的布局节点”(官方文档)
因此我们不难理解,Compose仍然是采用了树型结构作为UI加载、布局、渲染的底层结构,但是有所变更的是如今的Compose树的节点类型为ComposeUiNode,在组件树真正开始被载入时会变成LayoutNode类型的节点。LayoutNode是ComposeUiNode的子类,其继承了ComposeUiNode中所有组件设置的属性。对应的Row、Image、Column以及Text组件所形成的LayoutNode树显示如下:
因此我们可以明确了第一点,即组合阶段就是产生所有组件的LayoutNode树,为后续布局阶段提供遍历的内容。
结合源码我们梳理了组合阶段的执行流程,显示如下:
相关源码展示如下,setContent中内部的函数调用在此不做展示;可以结合流程图去检索源码即可了解其中的调用链。
private object TutorialSnippet1 {class MainActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContent {Text("Hello world!")}}}}public fun ComponentActivity.setContent(parent: CompositionContext? = null,content: @Composable () -> Unit) {val existingComposeView = window.decorView.findViewById<ViewGroup>(android.R.id.content).getChildAt(0) as? ComposeViewif (existingComposeView != null) with(existingComposeView) {setParentCompositionContext(parent)setContent(content)} else ComposeView(this).apply {// Set content and parent **before** setContentView// to have ComposeView create the composition on attachsetParentCompositionContext(parent)setContent(content)// Set the view tree owners before setting the content view so that the inflation process// and attach listeners will see them already presentsetOwners()setContentView(this, DefaultActivityContentLayoutParams)}}
布局阶段
在布局阶段,Compose 会使用在组合阶段生成的界面树 作为输入,计算每个布局节点在界面树中的测量和放置位置,此过程中将遍历每个LayoutNode节点执行子节点测量、自我测量以及放置子节点三个过程。
以上图的LayoutNode节点树为例,此过程可描述为:
1、Row 测量其子项 Image 和 Column。 2、系统测量了 Image。它没有任何子元素,因此它决定自己的 尺寸,并将尺寸报告给 Row。 3、接下来测量 Column。它会测量自己的子项(两个 Text 可组合项)。 4、系统将测量第一个 Text。由于没有任何子元素,因此它决定 自己的尺寸,并将其尺寸报告给 Column。系统测量第二个 Text。由于没有任何子元素,因此它决定 并将其报告给 Column。 5、Column 使用子测量值来确定自己的尺寸。它使用 最大子元素宽度及其子元素高度之和。 6、Column 会相对于自身放置其子项,并将其置于子项下方 相互垂直 7、Row 使用子测量值来确定自己的尺寸。它使用 子元素的最大高度及其子元素宽度之和。然后 子项。 |
采用深度遍历的方式执行LayoutNode树的遍历,基于constrains约束,树上每个组件所在的位置在遍历完成后都将计算完成,即完成布局阶段,等待后续的渲染阶段。
而布局阶段中,Modifier类已经开始在遍历的时候就被加载,并在宽高计算时被认为是待度量的因素。具体各组件的度量策略我附上一张表格。
!!! 目前核心关心的是Modifier属性是怎么被加载的?
① 组成Modifier链
Surface(modifier = Modifier.size(200.dp) // 设置组件大小.background(Color.LightGray) // 设置背景颜色.offset(x = 10.dp, y = 20.dp) // 设置偏移量) {Text(text = "Hello, Jetpack Compose!",fontSize = 20.sp,color = Color.Black,modifier = Modifier.padding(8.dp) // 内部文本的额外内边距)}
以此代码为例,Surface组件使用Modifier伴生对象基于链式调用的方式声明了多个属性,也正如此Modifier是Compose中修饰 UI 组件的关键数据结构。
interface Modifier {fun <R> foldIn(initial: R, operation: (R, Element) -> R): Rfun <R> foldOut(initial: R, operation: (Element, R) -> R): Rfun any(predicate: (Element) -> Boolean): Booleanfun all(predicate: (Element) -> Boolean): Booleaninfix fun then(other: Modifier): Modifier = ...interface Element : Modifier {...}companion object : Modifier {...}}
Modifier 接口有三个直接实现类或接口:伴生对象 Modifier、内部子接口Modifier.Element、CombinedModifier。
- 伴生对象 Modifier:最常用的 Modifier, 即在代码中使用 Modifier.xxx();
- 内部子接口 Modifier.Element:当我们使用Modifier.xxx()时,其内部实际会创建一个Modifier 实例。如使用 Modifier.size(100.dp) 时,内部会创建一个 SizeModifier 实例;
其背后的继承关系是
而类似LayoutModifier的中间类显示如下,不同的中间Modifier.Element子类将使得Modifier链建立时使用CombinedModifier来链接;
② Modifier链的构建过程
- then()
size属性定义时建立的SizeModifier实例被当作参数传入 then() 方法中。而这个 then() 方法就是 Modifier 间相互连接的关键方法。
Modifier.size(100.dp)fun Modifier.size(size: Dp) = this.then( // 关键方法SizeModifier(...))// 返回待连接的Modifiercompanion object : Modifier {...override infix fun then(other: Modifier): Modifier = other}
- 继续链式调用后续属性,BackGround, padding
Modifier.size(100.dp).background(Color.Red)fun Modifier.background(color: Color,shape: Shape = RectangleShape
) = this.then( // 当前 this 指向 SizeModifier 实例Background(...))
- 而此时Modifier链也将引入新的类型CombinedModifier来链接所对应的中间Modifier.Element子类不一致时的Modifier
interface Modifier {infix fun then(other: Modifier): Modifier =if (other === Modifier) this else CombinedModifier(this, other)}class CombinedModifier(private val outer: Modifier,private val inner: Modifier) : Modifier
形成以下的样式
所有属性全部上链会形成
至此Modifier链已经构建完成,而在LayoutNode遍历时会基于nodes属性访问管理Modifier链的Nodechain类;
internal val nodes = NodeChain(this)
而辅助遍历时的数据结构是
internal val innerCoordinator: NodeCoordinatorget() = nodes.innerCoordinatorinternal val layoutDelegate = LayoutNodeLayoutDelegate(this) internal val outerCoordinator: NodeCoordinatorget() = nodes.outerCoordinator
innerCoordinator是指最内层的协调器,直接与LayoutNode关联,负责处理Modifier链的内部操作,包括处理内部Modifier、管理LayoutNode子LayoutNode,协助他们的度量工作以及传递事件;
outerCoordinator是指最外层的协调器,负责处理Modifier链的外部操作,包括处理外部Modifier,协调Modifier的调用顺序以及管理LayoutNode父节点;
其中内部Modifier和外部Modifier的分辨规则是:
- 内部 Modifier 是指那些主要影响组件的布局和测量的修饰符。这些修饰符通常在LayoutNode的内部进行操作,改变组件的尺寸、位置和布局行为。( padding, size, fillMaxWidth / fillMaxHeight, wrapContentSize, aspectRatio, weight)
- 外部 Modifier 是指那些主要影响组件的绘制和偏移的修饰符。这些修饰符通常在LayoutNode的外部进行操作,改变组件的外观、透明度和位置偏移等。( background, border, offset, alpha, clip, shadow);
而其中访问Modifier链的过程将基于foldin与foldout方法取到Modifier上所有的设置信息,从而将外部 or 内部 属性的定义值反馈到当前LayoutNode的width, height上,并最后在父节点的constrains限制下输出最大的width和height;
fun <R> foldIn(initial: R, operation: (R, Element) -> R): R
// foldIn(): 正向遍历 Modifier 链,SizeModifier-> Background -> PaddingModifierfun <R> foldOut(initial: R, operation: (Element, R) -> R): R
// foldOut(): 反向遍历 Modifier 链, PaddingModifier -> Background ->SizeModifier
绘制阶段
绘制代码期间的状态读取会影响绘制阶段。常见示例包括 Canvas()、Modifier.drawBehind 和 Modifier.drawWithContent。
Modifier知识补充
Compose UI组件 Modifier子类 类图
Compose 常用UI组件 LayoutNode、Modifier、MeasurePolicy汇总
相关文章:

Compose知识分享
前言 “Jetpack Compose 是一个适用于 Android 的新式声明性界面工具包。Compose 提供声明性 API,让您可在不以命令方式改变前端视图的情况下呈现应用界面,从而使编写和维护应用界面变得更加容易。” 以上是Compose官网中对于Compose这套全新的Androi…...

python-study-day5
urllib中handler的使用 import urllib.request url "http://www.baidu.com" headers {User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0 } # 请求地址的定制 reques…...

Telegram mini app 本地开发配置
前言: 为了能在telegram里本地调试mini app,参考了网上很多方案,踩了不少坑。最后整了一个适合自己的方案,记录一下。 这个方案一定不是最好的,不过是目前适合我上手开发的方案了。 本文章适合需要在 telegram 本地…...

python发票查验接口助您拒绝做糊涂账、发票ocr
发票识别发票查验接口让发票真假立现。仅需一键上传发票图片,即可实现发票真伪的秒速、批量验证,操作简单方便,避免因人工核验失误所导致“错账”现象的发生,减轻财务工作负担,提升企业工作效率,降低因假票…...

【Linux】线程控制|POSIX线程库|多线程创建|线程终止|等待|线程分离|线程空间布局
目录 编辑 POSIX线程库 多线程创建 独立栈结构 获取线程ID pthread_self 线程终止 return终止线程 pthread_exit pthread_cancel 线程等待 退出码问题 线程分离 测试 线程ID及地址空间布局 编辑 POSIX线程库 pthread线程库是 POSIX线程库的一部分…...

JimuReport 积木报表 v1.8.0 版本发布,开源可视化报表
项目介绍 一款免费的数据可视化报表工具,含报表和大屏设计,像搭建积木一样在线设计报表!功能涵盖,数据报表、打印设计、图表报表、大屏设计等! Web 版报表设计器,类似于excel操作风格,通过拖拽完…...

性能优化理论篇 | swap area是个什么东西
我们知道每台计算机的内存(RAM)都是有限的,而我们的应用程序需要加载到内存才能被运行,如果一台机器运行多个应用程序时,内存可能会耗尽。Linux 系统中的“交换空间(也称为交换分区)”可以帮助缓…...

Photoshop (PS)下载安装win/mac版
目录 一、概述 下载 二、安装步骤 三、使用教程 四、快捷键汇总 一、概述 Adobe Photoshop,简称“PS”,是由Adobe Systems开发和发行的图像处理软件。它主要处理以像素所构成的数字图像,涵盖了诸多领域,如图像编辑、图像合成…...

初识redis:Set类型
Set有很多种含义,比如集合,比如设置(和get相对应)。 在这里我们说的set是指的redis中的集合,并且这里的集合是无序的,和之前的list是对应的。 List : [1,2,3] 和 [2,1,3] 是两个不同的listSe…...

Qt 设置QTableView的某列或某行只读
我的做法是实现一个委托(delegate),然后把它设给你要只读的列或行 class ReadOnlyDelegate: public QItemDelegate {public:ReadOnlyDelegate(QWidget *parent NULL):QItemDelegate(parent){}QWidget *createEditor(QWidget *parent, const…...

这么多焊工证,我该拥有那种焊工证呢?
焊接与切割作为特种作业工作,国家根据不同的环境和作业特点,从事焊接工作的焊工需要持证上岗工作,那么是不是焊接就只有一种工作证呢?如果不是,焊工所在的工作岗位需要什么样的焊工证,如何取得焊工证&#…...

HttpWebRequest访问https请求被中止: 未能创建 SSL/TLS 安全通道
编写代码,使用HttpWebRequest访问网页,错误提示: 未能创建 SSL/TLS 安全通道 页面本来是Http网址,突然转换为https网址,所以遇到这个问题也算正常。将解决方法记录下来给需要的人。 //只需要在 HttpWebRequest 发送…...

响应式Web设计:纯HTML和CSS的实现技巧
文章目录 响应式Web设计:纯HTML和CSS的实现技巧一、响应式Web设计概述二、实现响应式设计的技巧1. 使用媒体查询(Media Queries)2. 使用弹性布局(Flexbox)3. 使用网格布局(CSS Grid)4. 使用相对…...

linux centos 防火墙常用命令
1、开放端口 firewall-cmd --zonepublic --add-port80/tcp --permanent 1 2、查看某端口是否开放 firewall-cmd --query-port80/tcp 1 3、查看端口开启列表 firewall-cmd --list-port 1 4、重启防火墙 firewall-cmd --reload 1 5、关闭防火墙 systemctl stop firewalld.se…...

iOS18 Beta7 最终测试版推送:苹果的又一次技术飞跃
苹果公司以其一贯的创新精神和对完美的追求,再次引领了科技界的新潮流。今天,我们聚焦于苹果最新推送的iOS18 Beta7最终测试版,这一版本不仅是苹果软件更新的里程碑,更是用户体验的一次重大升级。 最终测试版的亮相 在众多果粉的…...

超详细!2024叉车证办理及考试流程详解!速看!
根据《特种设备安全监察条例》规定:叉车操作员必须经专业培训和考核,取得地、市级以上质量技术监督行政部门颁发的作业人员资格证书,方可从事该项工作。叉车驾驶证是属于特种车辆驾驶证,要到全国市级质量监督局指定叉车训练基地报…...

浅谈监听器插件之SSHMon Samples Collector
浅谈监听器插件之SSHMon Samples Collector SSHMon Samples Collector 是 JMeter 的一个强大插件,它允许用户通过 SSH 协议远程监控目标服务器的资源利用率,为性能测试提供实时的系统性能数据。这对于评估应用在高负载条件下的表现以及定位性能瓶颈至关…...

R语言论文插图模板第7期—分组散点图
在之前的文章中,分享过R语言折线图的绘制模板: 柱状图的绘制模板: 本期再来分享一下散点图(分组)的绘制方法。 先来看一下成品效果: 特别提示:本期内容『数据代码』已上传资源群中,…...

设计模式 抽象工厂方法模式
定义与特点 工厂方法模式(FactoryMethod),定义一个创建产品对象的工厂接口,让工厂子类决定实例化那一个产品类。工厂方法使一个类的实例化延迟到其子类。 我们把被创建的对象称为“产品”,把创建产品的对象称为“工厂…...

PowerShell 一键配置IP
前言 实现一键更改Windows 网卡IP,子网,网关,dns,重命名网卡,获取的接口索引名称,获取接口名称,刷新组策略,刷新系统,脚本可重复配置,,以下环境我是两个网卡配置IP 前提条件 开启wmi,配置网卡,参考 创建更改网卡脚本 实验环境,两个网卡,清除默认,重命名(配置)…...

【Pyhthon读取 PDF文件表格 ,转为 CSV/TSV/JSON文件】
tabula-py tabula-py 是一个将 PDF 表格转换为 pandas DataFrame 的工具。 tabula-py 是 tabula-java 的包装器,需要您的机器上有 java。 tabula-py 还允许您将 PDF 中的表格转换为 CSV/TSV 文件。 tabula-py 的 PDF 提取准确度与 tabula-java 或 tabula app 相…...

<数据集>商品条形码识别数据集<目标检测>
数据集格式:VOCYOLO格式 图片数量:3748张 标注数量(xml文件个数):3748 标注数量(txt文件个数):3748 标注类别数:1 标注类别名称:[Barcode] 序号类别名称图片数框数1Barcode37484086 使用标注工具&am…...

无人机系统的关键技术
一、飞控系统:是无人机完成整个飞行过程的关键,决定了无人机的飞行性能和稳定性。 二、导航系统:提供无人机所需的位置、速度和飞行姿态等信息,引导无人机按照指定航线飞行。 三、动力系统:提供飞行动力,…...

(24)(24.4) MultiWii/DJI/HDZero OSD (version 4.2 and later)(一)
文章目录 前言 1 基于遥测的OSD 前言 ArduPilot 使用基于MSP(MultiWii 串行协议)的协议支持多种类型的屏幕显示(OSD): 基于 MSP 遥测的操作系统,如大疆 FPV 护目镜 V1/V2、大疆护目镜 RE、FatShark Byte…...

HiveSQL:提取json串内容——get_json_oject和json_tuple
提取json串中内容,json格式示例如下 方法(运行环境Hive) get_json_objectjson_tuple get_json_object select json_data,get_json_object(json_data,$.price) as price -- 取第一层,get_json_object(json_data,$.inquiry_params) as in…...

Clickhouse 二进制安装
下载对应版本https://github.com/ClickHouse/ClickHouse 分别选择arm64/x86_64 对应的tgz 场景是需要把ck所有文件安装指定目录,使用rpm会散布在各个目录不方便管控 下载client,common-static,server这三个 将相同的目录合并成一个(这里注意需要在l…...

大数据开发工程师面试整理-项目经验
在面试或职业发展中,分享项目经验是展示个人能力和专业知识的重要途径。大数据开发工程师的项目经验通常涉及数据处理、系统设计、性能优化以及与团队协作等多个方面。以下是如何有效地展示大数据项目经验的指导,以及一个示范性的项目经验描述。 1. 结构化描述项目经验 ● 项…...

游戏引擎详解——图片
图片 图片的格式 图片文件格式pngjpg 纹理压缩格式ETC1/2PVRTCASTC 图片的属性 图片属性解释分辨率宽高像素值(pt),如:1024*1024位深度用来存储像素颜色的值,如RGBA8888,红黄蓝透明度4个维度每个8bit&…...

电商API数据接口在电商运营电商数据分析中的作用?
电商运营中,品牌方使用电商API数据接口可以带来众多益处,具体包括但不限于以下几点: 实时数据同步:通过API接口,品牌方可以实时获取商品库存、订单状态、价格变动等信息,保证数据的时效性和准确性ÿ…...

Java OkHttp使用(二)
文章目录 引言使用 OkHttp 发送回调其他 引言 记录一下 OkHttp 的使用;OkHttp 异步发送回调请求,增加回调失败重试。 使用 OkHttp 发送回调 /*** 回调重试类*/ Data public class CallBackRetryData {/*** 回调信息JSON*/private JSONObject bodyRequ…...