当前位置: 首页 > news >正文

一文彻底解析 Compose 的穿透刺客 -- CompositionLocal

Compose 官方说明一直很简洁:CompositionLocal 是通过组合隐式向下传递数据的工具。


两个核心:隐式、向下传递,咋一看很懵,先不着急去理解,我们先看一段非常简单的代码:

class MainActivity : ComponentActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContent {ComposeBlogTheme {val name = "Hi, Compose!"    // name 是局部变量ShowMessage(name)}}}
}@Composable
fun ShowMessage(message: String) {Text(message)
}

这段代码中局部变量 name 以参数的方式被暴露出来了,在 Compose 中这叫做 State Hoisting(状态提升)。

现在思考两个问题:

class MainActivity : ComponentActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContent {ComposeBlogTheme {val name = "Hi, Compose!"ShowMessage(name)}👉🏻 name =     // 思考 1: 这边能不能获取到 name?}}
}@Composable
fun ShowMessage(message: String) {👉🏻 name =             // 思考 2: 这边能不能获取到 name?Text(message)
}

很明显都是不行的!因为 name 这个局部变量的 作用域 是限定在 ComposeBlogTheme {} 范围内的。

现在我们再来改下代码:

class MainActivity : ComponentActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContent {ComposeBlogTheme {val name = "Hi, Compose!"// ShowMessage(name)    // 这段代码我不要了}}}
}@Composable
fun ShowMessage() {    // 参数我也去掉,不需要外面传参进来Text(name)         // 我就直接把外面的 name 写到这里,直接用
}

这样写行吗?肯定是不行的!但我就想这么写,并且希望它可以运行,那么这种行为就意味着 name 这个局部变量要拥有可以穿出它的 作用域,并且穿透进 showMessage 函数的能力!

好消息,这种牛逼的行为就可以通过我们的主角:CompositionLocal 实现,它提供了这种 「穿透能力」

再来看两张图你就更加明白 CompositionLocal 的神奇之处了。


在这里插入图片描述


我来解释一下这张图:绿色可组合项有个 stateData 参数,这个参数又跟顶层的可组合项有联系,正常代码中对于这种关联我们一般就是要不停的传参?很明显图中蓝色的可组合项就做了这些事,但这样你会发现,层积越多,参数可能就越多(因为别的可组合项也可能有自己的参数),这样代码的阅读和理解会特别费劲。

那如果用 CompositionLocal 呢?


在这里插入图片描述


现在整个树中不再需要蓝色的可组合项传递参数,哪里需要用 stateData,直接用就是了,因为顶层可组合项中通过 CompositonLocal 赋予了状态数据可以穿透的能力!

到这里你应该对 CompositionLocal 已经有了一个比较透测的理解了,接下来我们看看如何使用。


📓CompositionLocal


现在我们就来实现一个 CompostionLocal 功能,让上面的代码生效,总共需要经历三步:

1. 创建 CompositionLocal 实例

首先创建一个具有 「穿透能力」 的变量:

class MainActivity : ComponentActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContent {ComposeBlogTheme {val name = "Hi, Compose!"}}}
}@Composable
fun ShowMessage() {Text(name)
}// 1. 通过 compostionLocalOf 创建 CompositionLocal
val LocalName = compositionLocalOf<String> { "Compose" }

注意:在 Compose 中,一般定义这种具有「穿透能力」CompostionLocal,它的名称有个约定俗成的写法:LocalXXX


2. 为 CompositionLocal 提供值

CompostionLocal 实例定义好了,我们要通过 provides 给它绑定一个值。

class MainActivity : ComponentActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContent {ComposeBlogTheme {val name = "Hi, Compose!"// 2. CompositionLocalProvider 可组合项可将值绑定到给定层次结构的 CompositionLocal 实例CompositionLocalProvider(LocalName provides name) {ShowMessage()}}}}
}@Composable
fun ShowMessage() {Text(name)
}// 1. 通过 compostionLocalOf 创建 CompositionLocal
val LocalName = compositionLocalOf<String> { "Compose" }

3. 获取 CompositionLocal 数据

前面两步其实就已经创建好了一个包含 数据 + 可穿透CompositionLocal 了,最后一步就是获取到其中的数据。

class MainActivity : ComponentActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContent {ComposeBlogTheme {val name = "Hi, Compose!"// 2. CompositionLocalProvider 可组合项可将值绑定到给定层次结构的 CompositionLocal 实例CompositionLocalProvider(LocalName provides name) {ShowMessage()}}}}
}@Composable
fun ShowMessage() {// 3. 获取 nameText(LocalName.current)
}// 1. 通过 compostionLocalOf 创建 CompositionLocal
val LocalName = compositionLocalOf<String> { "Compose" }

现在运行程序试试?


📓 两种创建方式


前面的例子我们使用 compositionLocalOf 创建 CompositionLocal,另外官方还提供了 staticCompositionLocalOf

  1. compositionLocalOf:在重组期间更改提供的值只会使读取其 current 值的内容无效。
  2. staticCompositionLocalOf:与 compositionLocalOf 不同,Compose 不会跟踪 staticCompositionLocalOf 的读取。更改该值会导致提供 CompositionLocal 的整个 content lambda 被重组,而不仅仅是在组合中读取 current 值的位置。

😵😵😵,没看懂???我们来看下面的代码:

1. compositionLocalOf

class MainActivity : ComponentActivity() {private val themeBackground by mutableStateOf(Color.Blue)override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContent {ComposeBlogTheme {CompositionLocalProvider(LocalBackground provides Color.Red) {TextBackground()CompositionLocalProvider(LocalBackground provides themeBackground) {/*** 2. themeBackground 变化后,仅此范围重组* TextBackground()*/CompositionLocalProvider(LocalBackground provides Color.Red) {TextBackground()}}TextBackground()}}}}
}@Composable
fun TextBackground() {Surface(color = LocalBackground.current) {Text("Hi, Compose")}
}// 1. Compose 会记录跟踪每一个调用 LocalBackground.current 的区域
val LocalBackground = compositionLocalOf { Color.Blue }

2. staticCompositionLocalOf

class MainActivity : ComponentActivity() {private val themeBackground by mutableStateOf(Color.Blue)override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContent {ComposeBlogTheme {CompositionLocalProvider(LocalBackground provides Color.Red) {TextBackground()CompositionLocalProvider(LocalBackground provides themeBackground) {/*** 2. themeBackground 变化后,内部所有 content 全部重组* TextBackground()** CompositionLocalProvider(LocalBackground provides Color.Red) {*    TextBackground()* }*/}TextBackground()}}}}
}@Composable
fun TextBackground() {Surface(color = LocalBackground.current) {Text("Hi, Compose")}
}// Compose 不会记录跟踪每一个调用 LocalBackground.current 的区域
val LocalBackground = staticCompositionLocalOf { Color.Blue }

那么随之而来一个疑问:我们该用哪个创建 CompositionLocal?

3. 官方建议

如果为 CompositionLocal 提供的值发生更改的可能性微乎其微或永远不会更改,使用 staticCompositionLocalOf 可提高性能。

比如,我们看两个系统里面定义好的 CompositionLocal

// 上下文固定不变,用 staticCompositionLocalOf
val LocalContext = staticCompositionLocalOf<Context> {noLocalProvidedFor("LocalContext")
}// 内部内容颜色常变,用 compositionLocalOf
val LocalContentColor = compositionLocalOf { Color.Black }

📓 CompositionLocal 应用场景


这里我们列举一些 CompositionLocal 的适用场景。

1. 提供上下文

LocalContext.current    // 是不是很熟悉?这其实就相当于 getContext()

2. MaterialTheme

其实官方的 MaterialTheme 就用到了 CompositionLocal,我们看源码:

@Composable
fun MaterialTheme(colors: Colors = MaterialTheme.colors,typography: Typography = MaterialTheme.typography,shapes: Shapes = MaterialTheme.shapes,content: @Composable () -> Unit
) {val rememberedColors = remember { colors.copy() }.apply { updateColorsFrom(colors) }val rippleIndication = rememberRipple()val selectionColors = rememberTextSelectionColors(rememberedColors)// 通过 providers 将 rememberedColors 提供给了 LocalColorsCompositionLocalProvider(LocalColors provides rememberedColors,LocalContentAlpha provides ContentAlpha.high,LocalIndication provides rippleIndication,LocalRippleTheme provides MaterialRippleTheme,LocalShapes provides shapes,LocalTextSelectionColors provides selectionColors,LocalTypography provides typography) {ProvideTextStyle(value = typography.body1) {PlatformMaterialTheme(content)}}
}
object MaterialTheme {val colors: Colors@Composable@ReadOnlyComposableget() = LocalColors.currentval typography: Typography@Composable@ReadOnlyComposableget() = LocalTypography.currentval shapes: Shapes@Composable@ReadOnlyComposableget() = LocalShapes.current
}

相关文章:

一文彻底解析 Compose 的穿透刺客 -- CompositionLocal

Compose 官方说明一直很简洁&#xff1a;CompositionLocal 是通过组合隐式向下传递数据的工具。 两个核心&#xff1a;隐式、向下传递&#xff0c;咋一看很懵&#xff0c;先不着急去理解&#xff0c;我们先看一段非常简单的代码&#xff1a; class MainActivity : ComponentAc…...

iOS 位移枚举NS_OPTIONS(如何实现多个枚举值的同时传入判断)

一、场景 当我们使用枚举这个东西时&#xff0c;有时需要多个枚举值任一一个满足时就ture&#xff0c;但是常用的枚举NS_ENUM定义好的枚举只能挨个判断&#xff0c;写一坨就既不美观也不好阅读&#xff0c;如下&#xff1a; typedef NS_ENUM (NSInteger, RPTestType){RPTestT…...

【Axure高保真原型】树控制内联框架

今天和大家分享树控制内联框架的原型模板&#xff0c;点击树的箭头可以打开或者收起子节点&#xff0c;点击最后一级人物节点&#xff0c;可以切换右侧内联框到对应的页面&#xff0c;左侧的树是通过中继器制作的&#xff0c;使用简单&#xff0c;只需要按要求填写中继器表格即…...

Visual Studio常用快捷键及调试操作

CtrlF10 运行到光标处 调试时候不用一行行按F10了CtrlMM 折叠或展开当前方法CtrlMO 折叠所有方法CtrlML 展开所有方法CtrlEW 自动换行/取消自动换行CtrlU 选中文本转小写CtrlShiftU 选中文本转大写CtrlWinO 启动软键盘F9 光标行加断点CtrlAltB 打开断点窗口 或通过Debug -> …...

MySQL 从零开始:02 MySQL 安装

文章目录 1、下载 MySQL 安装程序2、安装 MySQL 要操作 MySQL &#xff0c;首先要安装 MySQL &#xff0c;本文将一步步展示如何安装 MySQL&#xff0c;简直详细到令人发指。 环境&#xff1a; 操作系统&#xff1a;Windows10 64位MySQL版本&#xff1a;社区版 8.0.11.0 1、下…...

GB28181/GB35114平台LiveGBS何如添加白名单,使指定海康、大华、华为等GB28181摄像头或录像机设备可以免密接入

1、什么是GB/T28181级联 协议定义中的解释如下&#xff1a; 级联 cascadednetworking 两个信令安全路由网关之间按照上下级关系连接,上级中心信令控制服务器通过信令安全路由网 关可调用下级中心信令控制服务器所管辖的监控资源,下级中心信令控制服务器通过信令安全路由网 关向…...

【计算机组成与体系结构Ⅱ】MIPS指令系统(实验)

实验2&#xff1a;MIPS指令系统 一&#xff1a;实验目的 了解和熟悉指令级模拟器。熟练掌握MIPSsim模拟器的操作和使用方法。熟悉MIPS指令系统及其特点&#xff0c;加深对MIPS指令操作语义的理解。熟悉MIPS体系结构。 二&#xff1a;实验要求 采用指令集和流水线操作级模拟器…...

jsonvue-mobile 联动方式说明。

目录 jsonvue-mobile的联动类型分为两种 一种是命令式的&#xff1a; 另一种是响应式的&#xff1a; 联动场景 场景一&#xff1a;某一个字段的值变化时&#xff0c;同步修改另一个字段的值 命令式&#xff1a; 响应式&#xff1a; 场景一演示效果GIF 场景二&#xff…...

abseil中的微操

给分支预测器的建议 原始代码 以下代码用于实现多线程中只调用一次的效果&#xff0c;这里的if大多数情况下都是false&#xff0c;即已经被调用过了。这里是否被调用过用的是一个std::atomic<uint32_t>的原子变量 template <typename Callable, typename... Args>…...

NLP论文阅读记录 - 2022 | WOS 数据驱动的英文文本摘要抽取模型的构建与应用

文章目录 前言0、论文摘要一、Introduction1.1目标问题1.2相关的尝试1.3本文贡献 二.相关工作三.本文方法四 实验效果4.1数据集4.2 对比模型4.3实施细节4.4评估指标4.5 实验结果4.6 细粒度分析 五 总结 前言 Construction and Application of a Data-Driven Abstract Extractio…...

虹科新闻丨LIBERO医药冷链PDF温度计完成2024年航空安全鉴定,可安全空运!

来源&#xff1a;虹科环境监测技术 虹科新闻丨LIBERO医药冷链PDF温度计完成2024年航空安全鉴定&#xff0c;可安全空运&#xff01; 原文链接&#xff1a;https://mp.weixin.qq.com/s/XHT4kU27opeKJneYO0WqrA 欢迎关注虹科&#xff0c;为您提供最新资讯&#xff01; 虹科LIBE…...

智能搬运机器人作为一种新型的物流技术

随着物流行业的快速发展&#xff0c;货物转运的效率和准确性成为了企业竞争的关键因素之一。智能搬运机器人作为一种新型的物流技术&#xff0c;已经在许多企业中得到了广泛应用。本文将介绍富唯智能智能搬运机器人在物流行业的应用和优势。 在实际应用中&#xff0c;智能搬运机…...

UI自动化测试工具对企业具有重要意义

随着软件行业的不断发展&#xff0c;企业对高质量、高效率的软件交付有着越来越高的要求。在这个背景下&#xff0c;UI自动化测试工具成为了企业不可或缺的一部分。以下是UI自动化测试工具对企业的重要作用&#xff1a; 1. 提高软件质量 UI自动化测试工具能够模拟用户的操作&am…...

Linux--进程状态与优先级

概念 进程指的是程序在执行过程中的活动。进程是操作系统进行资源分配和调度的基本单位。 进程可以看作是程序的一次执行实体&#xff0c;它包含了程序代码、数据以及相关的执行上下文信息。操作系统通过创建、调度和管理多个进程来实现对计算机系统资源的有效利用。 每个进程…...

如何实现无公网ip固定TCP端口地址远程连接Oracle数据库

文章目录 前言1. 数据库搭建2. 内网穿透2.1 安装cpolar内网穿透2.2 创建隧道映射 3. 公网远程访问4. 配置固定TCP端口地址4.1 保留一个固定的公网TCP端口地址4.2 配置固定公网TCP端口地址4.3 测试使用固定TCP端口地址远程Oracle 前言 Oracle&#xff0c;是甲骨文公司的一款关系…...

Orchestrator源码解读2-故障失败发现

目录 前言 核心流程函数调用路径 GetReplicationAnalysis 故障类型和对应的处理函数 ​编辑 拓扑结构警告类型 核心流程总结 与MHA相比 前言 Orchestrator另外一个重要的功能是监控集群&#xff0c;发现故障。根据从复制拓扑本身获得的信息&#xff0c;它可以识别各种故…...

REST2SQL是什么?它有什么功能和特性?它值不值得我们去学习?我们该如何去学习呢?

REST2SQL是一种将RESTful API转换为SQL查询的工具或技术。它可以将RESTful API中的请求转换为对数据库的SQL查询&#xff0c;以便从数据库中检索、更新或删除数据。 REST2SQL的工作原理是通过分析RESTful API的请求参数和路径&#xff0c;将其转换为相应的SQL查询语句。这样可…...

Android 实现获取集合中出现重复数据的值和数量

方法一&#xff1a;使用HashMap和HashSet 创建一个HashMap&#xff0c;用于存储集合中的元素及其出现次数。 Map<String, Integer> map new HashMap<>();遍历集合&#xff0c;将每个元素作为键&#xff0c;将其出现次数作为值添加到HashMap中。 for (String it…...

【QT学习十一】QThread

一、引言 在现代软件开发中&#xff0c;多线程编程变得越来越重要&#xff0c;尤其是对于需要处理并发任务的应用程序。Qt C 框架提供了强大的多线程支持&#xff0c;使得开发者能够轻松地创建和管理多线程应用。 在 Qt 中&#xff0c;多线程的实现主要基于 QThread 类。QThrea…...

Mybatis 39_使用MBG生成代码

此2个插件均未晚装成功!!!! 安装 MyBatipse插件 MyBatipse插件 - 开发MyBatis应用的Eclipse插件- 自动完成- 有效性验证- Mapper视图使用MBG MyBatis Generator (MBG):根据底层数据表来自动生成Mapper组件只要两步即可: (1) 提供一个简单的配置文件,告诉MBG连接数据…...

iOS 26 携众系统重磅更新,但“苹果智能”仍与国行无缘

美国西海岸的夏天&#xff0c;再次被苹果点燃。一年一度的全球开发者大会 WWDC25 如期而至&#xff0c;这不仅是开发者的盛宴&#xff0c;更是全球数亿苹果用户翘首以盼的科技春晚。今年&#xff0c;苹果依旧为我们带来了全家桶式的系统更新&#xff0c;包括 iOS 26、iPadOS 26…...

树莓派超全系列教程文档--(62)使用rpicam-app通过网络流式传输视频

使用rpicam-app通过网络流式传输视频 使用 rpicam-app 通过网络流式传输视频UDPTCPRTSPlibavGStreamerRTPlibcamerasrc GStreamer 元素 文章来源&#xff1a; http://raspberry.dns8844.cn/documentation 原文网址 使用 rpicam-app 通过网络流式传输视频 本节介绍来自 rpica…...

【CSS position 属性】static、relative、fixed、absolute 、sticky详细介绍,多层嵌套定位示例

文章目录 ★ position 的五种类型及基本用法 ★ 一、position 属性概述 二、position 的五种类型详解(初学者版) 1. static(默认值) 2. relative(相对定位) 3. absolute(绝对定位) 4. fixed(固定定位) 5. sticky(粘性定位) 三、定位元素的层级关系(z-i…...

【决胜公务员考试】求职OMG——见面课测验1

2025最新版&#xff01;&#xff01;&#xff01;6.8截至答题&#xff0c;大家注意呀&#xff01; 博主码字不易点个关注吧,祝期末顺利~~ 1.单选题(2分) 下列说法错误的是:&#xff08; B &#xff09; A.选调生属于公务员系统 B.公务员属于事业编 C.选调生有基层锻炼的要求 D…...

Linux-07 ubuntu 的 chrome 启动不了

文章目录 问题原因解决步骤一、卸载旧版chrome二、重新安装chorme三、启动不了&#xff0c;报错如下四、启动不了&#xff0c;解决如下 总结 问题原因 在应用中可以看到chrome&#xff0c;但是打不开(说明&#xff1a;原来的ubuntu系统出问题了&#xff0c;这个是备用的硬盘&a…...

【HTML-16】深入理解HTML中的块元素与行内元素

HTML元素根据其显示特性可以分为两大类&#xff1a;块元素(Block-level Elements)和行内元素(Inline Elements)。理解这两者的区别对于构建良好的网页布局至关重要。本文将全面解析这两种元素的特性、区别以及实际应用场景。 1. 块元素(Block-level Elements) 1.1 基本特性 …...

【C语言练习】080. 使用C语言实现简单的数据库操作

080. 使用C语言实现简单的数据库操作 080. 使用C语言实现简单的数据库操作使用原生APIODBC接口第三方库ORM框架文件模拟1. 安装SQLite2. 示例代码:使用SQLite创建数据库、表和插入数据3. 编译和运行4. 示例运行输出:5. 注意事项6. 总结080. 使用C语言实现简单的数据库操作 在…...

Redis数据倾斜问题解决

Redis 数据倾斜问题解析与解决方案 什么是 Redis 数据倾斜 Redis 数据倾斜指的是在 Redis 集群中&#xff0c;部分节点存储的数据量或访问量远高于其他节点&#xff0c;导致这些节点负载过高&#xff0c;影响整体性能。 数据倾斜的主要表现 部分节点内存使用率远高于其他节…...

Pinocchio 库详解及其在足式机器人上的应用

Pinocchio 库详解及其在足式机器人上的应用 Pinocchio (Pinocchio is not only a nose) 是一个开源的 C 库&#xff0c;专门用于快速计算机器人模型的正向运动学、逆向运动学、雅可比矩阵、动力学和动力学导数。它主要关注效率和准确性&#xff0c;并提供了一个通用的框架&…...

使用Matplotlib创建炫酷的3D散点图:数据可视化的新维度

文章目录 基础实现代码代码解析进阶技巧1. 自定义点的大小和颜色2. 添加图例和样式美化3. 真实数据应用示例实用技巧与注意事项完整示例(带样式)应用场景在数据科学和可视化领域,三维图形能为我们提供更丰富的数据洞察。本文将手把手教你如何使用Python的Matplotlib库创建引…...