当前位置: 首页 > 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连接数据…...

JavaSec-RCE

简介 RCE(Remote Code Execution)&#xff0c;可以分为:命令注入(Command Injection)、代码注入(Code Injection) 代码注入 1.漏洞场景&#xff1a;Groovy代码注入 Groovy是一种基于JVM的动态语言&#xff0c;语法简洁&#xff0c;支持闭包、动态类型和Java互操作性&#xff0c…...

React 第五十五节 Router 中 useAsyncError的使用详解

前言 useAsyncError 是 React Router v6.4 引入的一个钩子&#xff0c;用于处理异步操作&#xff08;如数据加载&#xff09;中的错误。下面我将详细解释其用途并提供代码示例。 一、useAsyncError 用途 处理异步错误&#xff1a;捕获在 loader 或 action 中发生的异步错误替…...

土地利用/土地覆盖遥感解译与基于CLUE模型未来变化情景预测;从基础到高级,涵盖ArcGIS数据处理、ENVI遥感解译与CLUE模型情景模拟等

&#x1f50d; 土地利用/土地覆盖数据是生态、环境和气象等诸多领域模型的关键输入参数。通过遥感影像解译技术&#xff0c;可以精准获取历史或当前任何一个区域的土地利用/土地覆盖情况。这些数据不仅能够用于评估区域生态环境的变化趋势&#xff0c;还能有效评价重大生态工程…...

【Java_EE】Spring MVC

目录 Spring Web MVC ​编辑注解 RestController RequestMapping RequestParam RequestParam RequestBody PathVariable RequestPart 参数传递 注意事项 ​编辑参数重命名 RequestParam ​编辑​编辑传递集合 RequestParam 传递JSON数据 ​编辑RequestBody ​…...

前端开发面试题总结-JavaScript篇(一)

文章目录 JavaScript高频问答一、作用域与闭包1.什么是闭包&#xff08;Closure&#xff09;&#xff1f;闭包有什么应用场景和潜在问题&#xff1f;2.解释 JavaScript 的作用域链&#xff08;Scope Chain&#xff09; 二、原型与继承3.原型链是什么&#xff1f;如何实现继承&a…...

Unsafe Fileupload篇补充-木马的详细教程与木马分享(中国蚁剑方式)

在之前的皮卡丘靶场第九期Unsafe Fileupload篇中我们学习了木马的原理并且学了一个简单的木马文件 本期内容是为了更好的为大家解释木马&#xff08;服务器方面的&#xff09;的原理&#xff0c;连接&#xff0c;以及各种木马及连接工具的分享 文件木马&#xff1a;https://w…...

莫兰迪高级灰总结计划简约商务通用PPT模版

莫兰迪高级灰总结计划简约商务通用PPT模版&#xff0c;莫兰迪调色板清新简约工作汇报PPT模版&#xff0c;莫兰迪时尚风极简设计PPT模版&#xff0c;大学生毕业论文答辩PPT模版&#xff0c;莫兰迪配色总结计划简约商务通用PPT模版&#xff0c;莫兰迪商务汇报PPT模版&#xff0c;…...

Webpack性能优化:构建速度与体积优化策略

一、构建速度优化 1、​​升级Webpack和Node.js​​ ​​优化效果​​&#xff1a;Webpack 4比Webpack 3构建时间降低60%-98%。​​原因​​&#xff1a; V8引擎优化&#xff08;for of替代forEach、Map/Set替代Object&#xff09;。默认使用更快的md4哈希算法。AST直接从Loa…...

破解路内监管盲区:免布线低位视频桩重塑停车管理新标准

城市路内停车管理常因行道树遮挡、高位设备盲区等问题&#xff0c;导致车牌识别率低、逃费率高&#xff0c;传统模式在复杂路段束手无策。免布线低位视频桩凭借超低视角部署与智能算法&#xff0c;正成为破局关键。该设备安装于车位侧方0.5-0.7米高度&#xff0c;直接规避树枝遮…...

抽象类和接口(全)

一、抽象类 1.概念&#xff1a;如果⼀个类中没有包含⾜够的信息来描绘⼀个具体的对象&#xff0c;这样的类就是抽象类。 像是没有实际⼯作的⽅法,我们可以把它设计成⼀个抽象⽅法&#xff0c;包含抽象⽅法的类我们称为抽象类。 2.语法 在Java中&#xff0c;⼀个类如果被 abs…...