Compose知识分享

news2024/11/27 8:44:11

前言

“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? ComposeView



    if (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 attach

        setParentCompositionContext(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 present

        setOwners()

        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): R
    fun <R> foldOut(initial: R, operation: (Element, R) -> R): R
    fun any(predicate: (Element) -> Boolean): Boolean
    fun all(predicate: (Element) -> Boolean): Boolean
    infix fun then(other: Modifier): Modifier = ...
    interface Element : Modifier {

        ...

    }

    companion object : Modifier {

        ...

    }

}

Modifier 接口有三个直接实现类或接口:伴生对象 Modifier、内部子接口Modifier.Element、CombinedModifier

  1. 伴生对象 Modifier:最常用的 Modifier即在代码中使用 Modifier.xxx()
  2. 内部子接口 Modifier.Element:当我们使用Modifier.xxx()时,其内部实际会创建一个Modifier 实例。如使用 Modifier.size(100.dp) 时,内部会创建一个 SizeModifier 实例;

其背后的继承关系是

而类似LayoutModifier的中间类显示如下,不同的中间Modifier.Element子类将使得Modifier链建立时使用CombinedModifier来链接;

② Modifier链的构建过程

  1. then()

size属性定义时建立的SizeModifier实例被当作参数传入 then() 方法中。而这个 then() 方法就是 Modifier 间相互连接的关键方法。

Modifier.size(100.dp)


fun Modifier.size(size: Dp) = this.then( // 关键方法

    SizeModifier(
        ...
    )

)

// 返回待连接的Modifier

companion object : Modifier {

    ...

    override infix fun then(other: Modifier): Modifier = other

}
  1. 继续链式调用后续属性,BackGround, padding

Modifier
    .size(100.dp)
    .background(Color.Red)

fun Modifier.background(
    color: Color,
    shape: Shape = RectangleShape
) = this.then( // 当前 this 指向 SizeModifier 实例
    Background(
        ...
    )

)
  1. 而此时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: NodeCoordinator
    get() = nodes.innerCoordinator


internal val layoutDelegate = LayoutNodeLayoutDelegate(this)    

internal val outerCoordinator: NodeCoordinator
    get() = nodes.outerCoordinator

innerCoordinator是指最内层的协调器,直接与LayoutNode关联,负责处理Modifier链的内部操作,包括处理内部Modifier、管理LayoutNode子LayoutNode,协助他们的度量工作以及传递事件;

outerCoordinator是指最外层的协调器,负责处理Modifier链的外部操作,包括处理外部Modifier,协调Modifier的调用顺序以及管理LayoutNode父节点;

其中内部Modifier和外部Modifier的分辨规则是:

  1. 内部 Modifier 是指那些主要影响组件的布局和测量的修饰符。这些修饰符通常在LayoutNode的内部进行操作,改变组件的尺寸、位置和布局行为。( padding, size, fillMaxWidth / fillMaxHeight, wrapContentSize, aspectRatio, weight)
  2. 外部 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 -> PaddingModifier


fun <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汇总

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MidJourney付费失败的原因以及失败后如何取消或续订(文末附MidJourney,GPT-4o教程)

MidJourney付费失败的原因 MidJourney付费失败的原因可能包括支付方式无效、支付信息错误、网络问题、账户设置问题等。 ‌支付方式无效或信息错误‌&#xff1a;如果用户提供的支付方式&#xff08;如信用卡&#xff09;信息不正确&#xff0c;或者支付方式本身不支持该地区…

Python使用matplotlib计算并绘制图像的直方图

除了使用OpenCV计算图像直方图外&#xff0c;matplotlib也提供了直方图计算并绘制功能&#xff0c;只需要把图像&#xff08;或对应通道&#xff09;作为参数输入&#xff0c;即可通过matplotlib输出直方图&#xff08;标准直方图&#xff0c;非条形图表达&#xff09;&#xf…

LVS负载均衡群集-DR模式

一、负载均衡群集 1.数据包流向分析 客户端发送请求到 Director Server&#xff08;负载均衡器&#xff09;&#xff0c;请求的数据报文&#xff08;源 IP 是 CIP,目标 IP 是 VIP&#xff09;到达内核空间。Director Server 和 Real Server 在同一个网络中&#xff0c;数据通过…

MKS MWH-5匹配器Automatc matching impedance Network手侧

MKS MWH-5匹配器Automatc matching impedance Network手侧

Golang基础语法学习与速成

作者&#xff1a;CSDN-PleaSure乐事 欢迎大家阅读我的博客 希望大家喜欢 目录 1.golang介绍 1.1介绍 1.2优势 2.语法 2.1控制台输出 2.2算术运算符 2.3变量常量 2.4for循环 2.5if语句 2.6switch语句 2.7作用域 2.8浮点数和零值 2.8.1浮点数 2.8.2零值 2.9格式化…

【鸿蒙学习】HarmonyOS应用开发者高级认证 - 自由流转

学完时间&#xff1a;2024年8月21日 学完排名&#xff1a;第2253名 一、基本概念 1. 流转 在HarmonyOS中&#xff0c;将跨多设备的分布式操作统称为流转。流转能力打破设备界限&#xff0c;多设备联动&#xff0c;使用户应用程序可分可合、可流转&#xff0c;实现如邮件跨设…

【时时三省】(C语言基础)指针进阶

山不在高&#xff0c;有仙则名。水不在深&#xff0c;有龙则灵。 ----CSDN 时时三省 指针的概念 1 .指针就是个变量&#xff0c;用来存放地址&#xff0c;地址唯一标识一块内存空间。 2 .指针的大小是固定的4 /8个字节(32位平台/64位平台)。 3 .指针是有类型&#xff0c;指…

蓝队技能-应急响应篇挖矿病毒系统样本家族威胁情报异常定性排查分析处置封锁

知识点 1、应急响应-挖矿病毒-定性&排查 2、应急响应-挖矿病毒-应急&处置演示案例-蓝队技能-挖矿病毒-样本&定性&排查&应急&处置 挖矿病毒 随着虚拟货币的疯狂炒作&#xff0c;挖矿病毒已经成为不法分子利用最为频繁的攻击方式之一。 可以利用个人电…

【具体数学 Concrete Mathematics】1.1.1 汉诺塔问题

【具体数学 Concrete Mathematics】1.1.1 汉诺塔问题 汉诺塔问题的设定是&#xff1a;给定一个由8个圆盘 1 − 8 1-8 1−8( 1 1 1号圆盘最小&#xff0c; 8 8 8号圆盘最大)和三根柱子 A , B , C A,B,C A,B,C&#xff0c;从上向下这些圆盘大小逐渐递减&#xff08;即圆盘不能放在…

KNN算法与模型选择及调优

KNN算法-分类 1 样本距离判断 &#xff08;1&#xff09;欧式距离 欧式距离&#xff08;Euclidean distance&#xff09;&#xff0c;也称为欧氏度量&#xff0c;是用来衡量两个点之间直线距离的方法。 &#xff08;2&#xff09;曼哈顿距离 曼哈顿距离&#xff08;Manhatta…

探索CompletableFuture:高效异步编程的利器

目录 一、CompletableFuture基本功能安利 二、CompletableFuture使用介绍 &#xff08;一&#xff09;任务创建使用 1.supplyAsync创建带有返回值的异步任务 2.runAsync创建没有返回值的异步任务 &#xff08;二&#xff09;异步回调使用 1.异步回调&#xff1a;thenApp…

Android 修改SystemUI 音量条的声音进度条样式

一、前言 Android System UI 开发经常会遇到修改音量进度条样式的需求&#xff0c;主要涉及的类有VolumeDialogImpl与xml文件&#xff0c;接下来会逐步实现流程。先看看效果。 修改前 修改后 二、找到对应类 通过aidegen 打断点调试对应代码类VolumeDialogImpl定位到volume_d…

中国第一起名大师的老师颜廷利: 名字中的姓氏家谱字辈的最新解析

在探讨文化和文明的深层含义时&#xff0c;我们常常发现&#xff0c;对传统的尊重与现代价值观之间存在着一种微妙的张力。这种张力在一个简单的例子中得到了生动的体现&#xff1a;姓名的选择。 在古代社会&#xff0c;名字不仅仅是个体的标识&#xff0c;更是家族传承和社会结…

JavaScript_11_练习:小米搜索框案例(焦点事件)

效果图 代码 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>练习&#xff1a;小米搜索框案例&#…

第十二届青少年蓝桥杯Python组省赛试题

一、选择题 1.设s’Hello Lan Qiao’,执行print(s[4:11])输出的结果为()。 *选择题严禁使用程序验证 A、lo Lan Qi B、lo Lan Q C、o Lan Qi D、o Lan Q 提示&#xff1a;切片 2.循环语句for i in range(8,-4,-2):执行了几次循环()。 *选择题严禁使用程序验证 A、4 B、5 C、6…

LabVIEW锅炉燃烧远程监控系统

随着信息技术的发展&#xff0c;远程监控技术已经广泛应用于各种工业过程。开发了一个基于LabVIEW和互联网技术的锅炉燃烧远程监控系统&#xff0c;该系统不仅提高了锅炉运行的安全性和效率&#xff0c;还具备了故障远程诊断的功能&#xff0c;为锅炉管理提供了一种全新的解决方…

[论文笔记]Improving Retrieval Augmented Language Model with Self-Reasoning

引言 今天带来一篇百度提出的关于提升RAG准确率的论文笔记&#xff0c;Improving Retrieval Augmented Language Model with Self-Reasoning。 为了简单&#xff0c;下文中以翻译的口吻记录&#xff0c;比如替换"作者"为"我们"。 检索增强语言模型(Retrie…

谷歌浏览器自动填充密码怎么设置

谷歌浏览器的自动填充密码功能为用户提供了一种安全而便捷的在线体验&#xff0c;让用户在下次登录网站的时候&#xff0c;减去重复输入密码的麻烦。下面就给大家分享一下关于谷歌浏览器自动填充密码的相关内容&#xff0c;让你更加轻松的管理自己的账户。 谷歌浏览器自动填充密…

26.删除有序数组中的重复项---力扣

题目链接&#xff1a; . - 力扣&#xff08;LeetCode&#xff09;. - 备战技术面试&#xff1f;力扣提供海量技术面试资源&#xff0c;帮助你高效提升编程技能,轻松拿下世界 IT 名企 Dream Offer。https://leetcode.cn/problems/remove-duplicates-from-sorted-array/descript…

使用maven快速生成打包文件

最近在部署基于SpringBoot开发的项目时&#xff0c;由于微服务较多&#xff0c;本地工程编译后只得出一个JAR包&#xff0c;部署起来实在不方便&#xff0c;因此总想着怎么偷偷懒&#xff0c;执行一次命令编译出整个部署的文件。先说结果&#xff0c;最后期望打包的目录如下&am…