M8Test Help

CompositionLocal

核心概念

CompositionLocal 是 Jetpack Compose 中一种在 Composable 组件树中隐式传递数据的机制。

想象一下,你有一些数据(比如主题颜色、字体大小、当前登录的用户信息)需要在很多个 Composable 中使用。如果没有 CompositionLocal ,你唯一的办法就是将这些数据作为参数,一层一层地从父组件传递到子组件,即使中间的很多组件本身并不需要这些数据。这个过程被称为 “属性逐层传递”(Prop Drilling) ,它非常繁琐且难以维护。

CompositionLocal 完美地解决了这个问题。它允许你在组件树的某个上层节点提供一个值,然后在这个节点下的任何子孙组件中,都可以直接获取到这个值,而无需通过参数显式传递。

可以把它理解为:

  • 作用域内的“全局”变量 :它的作用范围仅限于提供它的那个组件及其所有后代。

  • Compose 版的依赖注入 :它为 Composable 组件树提供了一种轻量级的依赖注入方式。

用法

CompositionLocal 的使用分为三个步骤: 创建提供消费

步骤 1:创建 CompositionLocal

首先,我们需要创建一个 CompositionLocal 实例来“持有”我们的数据。通常我们会把它定义成一个顶层的 val

创建 CompositionLocal 有两种主要方式:

  1. compositionLocalOf: 这是最常用的方式。

    • 特点 :当 CompositionLocal 的值发生变化时,Compose 只会重组(recompose)那些直接读取了这个值的 Composable 。这是一种智能的、高性能的优化。

    • 要求 :必须提供一个默认值,以防止在没有提供值的情况下使用它导致崩溃。

  2. staticCompositionLocalOf: 用于那些值几乎不会改变或永远不会改变的情况。

    • 特点 :如果它的值发生变化, 所有CompositionLocalProvider 范围内的 Composable(无论它们是否读取了该值)都会被重组。

    • 优势 :如果值是静态的,它的初始创建和读取开销比 compositionLocalOf 稍低。

    • 警告只有当你确定这个值几乎不会改变时才使用它 ,否则会造成严重的性能问题。

import com.m8test.script.GlobalVariables.* fun side1Run() { // 创建一个 ComposeView, 可以通过声明式ui创建脚本界面 _composeView.create { // 插槽(Slot)是一种内容分发机制。它允许你在封装一个通用组件时,在组件内部预留一些“坑位”或“插槽”,然后让使用该组件的父组件来决定这些“坑位”里到底填充什么内容。 // 创建一个Column,Column是一种垂直布局,可以让其中的组件垂直排列 Column { // 设置 Column 中 content 插槽的内容 setContent { val state = mutableStateOf("InitValue") // 1. 创建 CompositionLocal 和 StaticCompositionLocal val localTestComposition = getCompositionLocals().compositionLocalOf("LocalTest") { state } val localTestStaticComposition = getCompositionLocals().staticCompositionLocalOf("LocalTestStatic") { state } CompositionLocalProvider { // 2. 通过 CompositionLocalProvider 为 CompositionLocal 提供值 setValues(_iterables.listOf(localTestComposition.provides(state))) setContent { // 在插槽中添加一个文本 Text { // 3. 消费值 setText(getCompositionLocalValue(localTestComposition)!!.getValue()) } // 在插槽中添加一个文本 Text { // 由于这个没有用到 CompositionLocal 的值,所以就算状态发生改变也不会重组, providerSlot 中用到 CompositionLocal 的值的 Composable 才会重组 setText("非静态${state.getValue()}") } } } CompositionLocalProvider { // 2. 通过 CompositionLocalProvider 为 CompositionLocal 提供值 setValues(_iterables.listOf(localTestStaticComposition.provides(state))) setContent { // 在插槽中添加一个文本 Text { // 3. 消费值 setText(getCompositionLocalValue(localTestStaticComposition)!!.getValue()) } // 在插槽中添加一个文本 Text { // 即使这个没有用到 CompositionLocal 的值但因为其使用了静态的 CompositionLocal,所以状态发生改变会重组, 因为整个 providerSlot 中的 Composable 都会重组 setText("静态${state.getValue()}") } } } // 添加一个按钮,这个按钮点击后会改变 state 的值 Button { setContent { Text { setText("点击我") } } // 改变状态值 setOnClick { state.setValue(state.getValue() + ".") } } } } } // 启动一个Activity用于显示脚本界面 _activity.start() } //-m8test-remove side1Run();
// 创建一个 ComposeView, 可以通过声明式ui创建脚本界面 $composeView.create { slot -> // 插槽(Slot)是一种内容分发机制。它允许你在封装一个通用组件时,在组件内部预留一些“坑位”或“插槽”,然后让使用该组件的父组件来决定这些“坑位”里到底填充什么内容。 // 创建一个Column,Column是一种垂直布局,可以让其中的组件垂直排列 slot.Column { column -> // 设置 Column 中 content 插槽的内容 column.setContent { columnSlot -> def state = columnSlot.mutableStateOf("InitValue") // 1. 创建 CompositionLocal 和 StaticCompositionLocal def localTestComposition = columnSlot.getCompositionLocals().compositionLocalOf("LocalTest") { state } def localTestStaticComposition = columnSlot.getCompositionLocals().staticCompositionLocalOf("LocalTestStatic") { state } columnSlot.CompositionLocalProvider { provider -> // 2. 通过 CompositionLocalProvider 为 CompositionLocal 提供值 provider.setValues($iterables.listOf(localTestComposition.provides(state))) provider.setContent { providerSlot -> // 在插槽中添加一个文本 providerSlot.Text { text -> // 3. 消费值 text.setText(text.getCompositionLocalValue(localTestComposition).getValue()) } // 在插槽中添加一个文本 providerSlot.Text { text -> // 由于这个没有用到 CompositionLocal 的值,所以就算状态发生改变也不会重组, providerSlot 中用到 CompositionLocal 的值的 Composable 才会重组 text.setText("非静态" + state.getValue()) } } } columnSlot.CompositionLocalProvider { provider -> // 2. 通过 CompositionLocalProvider 为 CompositionLocal 提供值 provider.setValues($iterables.listOf(localTestStaticComposition.provides(state))) provider.setContent { providerSlot -> // 在插槽中添加一个文本 providerSlot.Text { text -> // 3. 消费值 text.setText(text.getCompositionLocalValue(localTestStaticComposition).getValue()) } // 在插槽中添加一个文本 providerSlot.Text { text -> // 即使这个没有用到 CompositionLocal 的值但因为其使用了静态的 CompositionLocal,所以状态发生改变会重组, 因为整个 providerSlot 中的 Composable 都会重组 text.setText("静态" + state.getValue()) } } } // 添加一个按钮,这个按钮点击后会改变 state 的值 columnSlot.Button { button -> button.setContent { bs -> bs.Text { it.setText("点击我") } } // 改变状态值 button.setOnClick { state.setValue(state.getValue() + ".") } } } } } // 启动一个Activity用于显示脚本界面 $activity.start()
// 创建一个 ComposeView, 可以通过声明式ui创建脚本界面 $composeView.create(slot => { // 插槽(Slot)是一种内容分发机制。它允许你在封装一个通用组件时,在组件内部预留一些“坑位”或“插槽”,然后让使用该组件的父组件来决定这些“坑位”里到底填充什么内容。 // 创建一个Column,Column是一种垂直布局,可以让其中的组件垂直排列 slot.Column(column => { // 设置 Column 中 content 插槽的内容 column.setContent(columnSlot => { const state = columnSlot.mutableStateOf("InitValue"); // 1. 创建 CompositionLocal 和 StaticCompositionLocal const localTestComposition = columnSlot.getCompositionLocals().compositionLocalOf("LocalTest", () => state); const localTestStaticComposition = columnSlot.getCompositionLocals().staticCompositionLocalOf("LocalTestStatic", () => state); columnSlot.CompositionLocalProvider(provider => { // 2. 通过 CompositionLocalProvider 为 CompositionLocal 提供值 provider.setValues($iterables.listOf(localTestComposition.provides(state))); provider.setContent(providerSlot => { // 在插槽中添加一个文本 providerSlot.Text(text => { // 3. 消费值 text.setText(text.getCompositionLocalValue(localTestComposition).getValue()); }); // 在插槽中添加一个文本 providerSlot.Text(text => { // 由于这个没有用到 CompositionLocal 的值,所以就算状态发生改变也不会重组, providerSlot 中用到 CompositionLocal 的值的 Composable 才会重组 text.setText("非静态" + state.getValue()); }); }); }); columnSlot.CompositionLocalProvider(provider => { // 2. 通过 CompositionLocalProvider 为 CompositionLocal 提供值 provider.setValues($iterables.listOf(localTestStaticComposition.provides(state))); provider.setContent(providerSlot => { // 在插槽中添加一个文本 providerSlot.Text(text => { // 3. 消费值 text.setText(text.getCompositionLocalValue(localTestStaticComposition).getValue()); }); // 在插槽中添加一个文本 providerSlot.Text(text => { // 即使这个没有用到 CompositionLocal 的值但因为其使用了静态的 CompositionLocal,所以状态发生改变会重组, 因为整个 providerSlot 中的 Composable 都会重组 text.setText("静态" + state.getValue()); }); }); }); // 添加一个按钮,这个按钮点击后会改变 state 的值 columnSlot.Button(button => { button.setContent(bs => { bs.Text(it => { it.setText("点击我"); }); }); // 改变状态值 button.setOnClick(() => { state.setValue(state.getValue() + "."); }); }); }); }); }); // 启动一个Activity用于显示脚本界面 $activity.start();
-- 创建一个 ComposeView, 可以通过声明式ui创建脚本界面 _composeView:create(function(slot) -- 插槽(Slot)是一种内容分发机制。它允许你在封装一个通用组件时,在组件内部预留一些“坑位”或“插槽”,然后让使用该组件的父组件来决定这些“坑位”里到底填充什么内容。 -- 创建一个Column,Column是一种垂直布局,可以让其中的组件垂直排列 slot:Column(function(column) -- 设置 Column 中 content 插槽的内容 column:setContent(function(columnSlot) local state = columnSlot:mutableStateOf("InitValue") -- 1. 创建 CompositionLocal 和 StaticCompositionLocal local localTestComposition = columnSlot:getCompositionLocals():compositionLocalOf("LocalTest", function() return state end) local localTestStaticComposition = columnSlot:getCompositionLocals():staticCompositionLocalOf("LocalTestStatic", function() return state end) columnSlot:CompositionLocalProvider(function(provider) -- 2. 通过 CompositionLocalProvider 为 CompositionLocal 提供值 provider:setValues(_iterables:listOf(localTestComposition:provides(state))) provider:setContent(function(providerSlot) -- 在插槽中添加一个文本 providerSlot:Text(function(text) -- 3. 消费值 text:setText(tostring(text:getCompositionLocalValue(localTestComposition):getValue())) end) -- 在插槽中添加一个文本 providerSlot:Text(function(text) -- 由于这个没有用到 CompositionLocal 的值,所以就算状态发生改变也不会重组, providerSlot 中用到 CompositionLocal 的值的 Composable 才会重组 text:setText("非静态" .. tostring(state:getValue())) end) end) end) columnSlot:CompositionLocalProvider(function(provider) -- 2. 通过 CompositionLocalProvider 为 CompositionLocal 提供值 provider:setValues(_iterables:listOf(localTestStaticComposition:provides(state))) provider:setContent(function(providerSlot) -- 在插槽中添加一个文本 providerSlot:Text(function(text) -- 3. 消费值 text:setText(tostring(text:getCompositionLocalValue(localTestStaticComposition):getValue())) end) -- 在插槽中添加一个文本 providerSlot:Text(function(text) -- 即使这个没有用到 CompositionLocal 的值但因为其使用了静态的 CompositionLocal,所以状态发生改变会重组, 因为整个 providerSlot 中的 Composable 都会重组 text:setText("静态" .. tostring(state:getValue())) end) end) end) -- 添加一个按钮,这个按钮点击后会改变 state 的值 columnSlot:Button(function(button) button:setContent(function(bs) bs:Text(function(it) it:setText("点击我") end) end) -- 改变状态值 button:setOnClick(function() state:setValue(tostring(state:getValue()) .. ".") end) end) end) end) end) -- 启动一个Activity用于显示脚本界面 _activity:start()
<?php /** @var m8test_java\com\m8test\script\core\api\ui\compose\ComposeView $composeView */ global $composeView; /** @var m8test_java\com\m8test\script\core\api\ui\Activity $activity */ global $activity; /** @var m8test_java\com\m8test\script\core\api\collections\Iterables $iterables */ global $iterables; // 创建一个 ComposeView, 可以通过声明式ui创建脚本界面 $composeView->create(function ($slot) { // 插槽(Slot)是一种内容分发机制。它允许你在封装一个通用组件时,在组件内部预留一些“坑位”或“插槽”,然后让使用该组件的父组件来决定这些“坑位”里到底填充什么内容。 // 创建一个Column,Column是一种垂直布局,可以让其中的组件垂直排列 $slot->Column(function ($column) use ($slot) { // 设置 Column 中 content 插槽的内容 $column->setContent(function ($columnSlot) use ($slot) { global $iterables; $state = $columnSlot->mutableStateOf(javaString("InitValue")); // 1. 创建 CompositionLocal 和 StaticCompositionLocal $localTestComposition = $columnSlot->getCompositionLocals()->compositionLocalOf(javaString("LocalTest"), function () use ($state) { return $state; }); $localTestStaticComposition = $columnSlot->getCompositionLocals()->staticCompositionLocalOf(javaString("LocalTestStatic"), function () use ($state) { return $state; }); $columnSlot->CompositionLocalProvider(function ($provider) use ($localTestComposition, $state) { global $iterables; // 2. 通过 CompositionLocalProvider 为 CompositionLocal 提供值 $provider->setValues($iterables->listOf($localTestComposition->provides($state))); $provider->setContent(function ($providerSlot) use ($localTestComposition, $state) { // 在插槽中添加一个文本 $providerSlot->Text(function ($text) use ($localTestComposition) { // 3. 消费值 $text->setText($text->getCompositionLocalValue($localTestComposition)->getValue()); }); // 在插槽中添加一个文本 $providerSlot->Text(function ($text) use ($state) { // 由于这个没有用到 CompositionLocal 的值,所以就算状态发生改变也不会重组, providerSlot 中用到 CompositionLocal 的值的 Composable 才会重组 $text->setText(javaString("非静态") . $state->getValue()); }); }); }); $columnSlot->CompositionLocalProvider(function ($provider) use ($localTestStaticComposition, $state) { global $iterables; // 2. 通过 CompositionLocalProvider 为 CompositionLocal 提供值 $provider->setValues($iterables->listOf($localTestStaticComposition->provides($state))); $provider->setContent(function ($providerSlot) use ($localTestStaticComposition, $state) { // 在插槽中添加一个文本 $providerSlot->Text(function ($text) use ($localTestStaticComposition) { // 3. 消费值 $text->setText($text->getCompositionLocalValue($localTestStaticComposition)->getValue()); }); // 在插槽中添加一个文本 $providerSlot->Text(function ($text) use ($state) { // 即使这个没有用到 CompositionLocal 的值但因为其使用了静态的 CompositionLocal,所以状态发生改变会重组, 因为整个 providerSlot 中的 Composable 都会重组 $text->setText(javaString("静态") . $state->getValue()); }); }); }); // 添加一个按钮,这个按钮点击后会改变 state 的值 $columnSlot->Button(function ($button) use ($state) { $button->setContent(function ($bs) { $bs->Text(function ($it) { $it->setText(javaString("点击我")); }); }); // 改变状态值 $button->setOnClick(function () use ($state) { $state->setValue($state->getValue() . javaString(".")); }); }); }); }); }); // 启动一个Activity用于显示脚本界面 $activity->start();
from m8test_java.com.m8test.script.GlobalVariables import _activity from m8test_java.com.m8test.script.GlobalVariables import _composeView from m8test_java.com.m8test.script.GlobalVariables import _iterables # 创建一个 ComposeView, 可以通过声明式ui创建脚本界面 _composeView.create(lambda slot: ( # 插槽(Slot)是一种内容分发机制。它允许你在封装一个通用组件时,在组件内部预留一些“坑位”或“插槽”,然后让使用该组件的父组件来决定这些“坑位”里到底填充什么内容。 # 创建一个Column,Column是一种垂直布局,可以让其中的组件垂直排列 slot.Column(lambda column: ( # 设置 Column 中 content 插槽的内容 column.setContent(lambda columnSlot: ( # 使用海象表达式定义 state (state := columnSlot.mutableStateOf("InitValue")), # 1. 创建 CompositionLocal 和 StaticCompositionLocal # Groovy 的 { state } 转换为 lambda: state (localTestComposition := columnSlot.getCompositionLocals().compositionLocalOf("LocalTest", lambda: state)), (localTestStaticComposition := columnSlot.getCompositionLocals().staticCompositionLocalOf("LocalTestStatic", lambda: state)), columnSlot.CompositionLocalProvider(lambda provider: ( # 2. 通过 CompositionLocalProvider 为 CompositionLocal 提供值 provider.setValues(_iterables.listOf(localTestComposition.provides(state))), provider.setContent(lambda providerSlot: ( # 在插槽中添加一个文本 providerSlot.Text(lambda text: ( # 3. 消费值 text.setText(text.getCompositionLocalValue(localTestComposition).getValue()) )), # 在插槽中添加一个文本 providerSlot.Text(lambda text: ( # 由于这个没有用到 CompositionLocal 的值,所以就算状态发生改变也不会重组, providerSlot 中用到 CompositionLocal 的值的 Composable 才会重组 text.setText("非静态" + state.getValue()) )) )) )), columnSlot.CompositionLocalProvider(lambda provider: ( # 2. 通过 CompositionLocalProvider 为 CompositionLocal 提供值 provider.setValues(_iterables.listOf(localTestStaticComposition.provides(state))), provider.setContent(lambda providerSlot: ( # 在插槽中添加一个文本 providerSlot.Text(lambda text: ( # 3. 消费值 text.setText(text.getCompositionLocalValue(localTestStaticComposition).getValue()) )), # 在插槽中添加一个文本 providerSlot.Text(lambda text: ( # 即使这个没有用到 CompositionLocal 的值但因为其使用了静态的 CompositionLocal,所以状态发生改变会重组, 因为整个 providerSlot 中的 Composable 都会重组 text.setText("静态" + state.getValue()) )) )) )), # 添加一个按钮,这个按钮点击后会改变 state 的值 columnSlot.Button(lambda button: ( button.setContent(lambda bs: ( bs.Text(lambda it: ( it.setText("点击我") )) )), # 改变状态值 # setOnClick 通常是一个无参闭包 button.setOnClick(lambda: ( state.setValue(state.getValue() + ".") )) )) )) )) )) # 启动一个Activity用于显示脚本界面 _activity.start()
# encoding: utf-8 # 创建一个 ComposeView, 可以通过声明式ui创建脚本界面 $composeView.create do |slot| # 插槽(Slot)是一种内容分发机制。它允许你在封装一个通用组件时,在组件内部预留一些“坑位”或“插槽”,然后让使用该组件的父组件来决定这些“坑位”里到底填充什么内容。 # 创建一个Column,Column是一种垂直布局,可以让其中的组件垂直排列 slot.Column do |column| # 设置 Column 中 content 插槽的内容 column.setContent do |columnSlot| state = columnSlot.mutableStateOf("InitValue") # 1. 创建 CompositionLocal 和 StaticCompositionLocal localTestComposition = columnSlot.getCompositionLocals().compositionLocalOf("LocalTest") { state } localTestStaticComposition = columnSlot.getCompositionLocals().staticCompositionLocalOf("LocalTestStatic") { state } columnSlot.CompositionLocalProvider do |provider| # 2. 通过 CompositionLocalProvider 为 CompositionLocal 提供值 provider.setValues($iterables.listOf(localTestComposition.provides(state))) provider.setContent do |providerSlot| # 在插槽中添加一个文本 providerSlot.Text do |text| # 3. 消费值 text.setText(text.getCompositionLocalValue(localTestComposition).getValue()) end # 在插槽中添加一个文本 providerSlot.Text do |text| # 由于这个没有用到 CompositionLocal 的值,所以就算状态发生改变也不会重组, providerSlot 中用到 CompositionLocal 的值的 Composable 才会重组 text.setText("非静态" + state.getValue().to_s) end end end columnSlot.CompositionLocalProvider do |provider| # 2. 通过 CompositionLocalProvider 为 CompositionLocal 提供值 provider.setValues($iterables.listOf(localTestStaticComposition.provides(state))) provider.setContent do |providerSlot| # 在插槽中添加一个文本 providerSlot.Text do |text| # 3. 消费值 text.setText(text.getCompositionLocalValue(localTestStaticComposition).getValue()) end # 在插槽中添加一个文本 providerSlot.Text do |text| # 即使这个没有用到 CompositionLocal 的值但因为其使用了静态的 CompositionLocal,所以状态发生改变会重组, 因为整个 providerSlot 中的 Composable 都会重组 text.setText("静态" + state.getValue().to_s) end end end # 添加一个按钮,这个按钮点击后会改变 state 的值 columnSlot.Button do |button| button.setContent do |bs| bs.Text do |it| it.setText("点击我") end end # 改变状态值 button.setOnClick do state.setValue(state.getValue() + ".") end end end end end # 启动一个Activity用于显示脚本界面 $activity.start()

步骤 2:提供 (Provide) 值

使用 CompositionLocalProvider 组件,在组件树的某个节点上为 CompositionLocal 提供一个具体的值。所有在这个 CompositionLocalProvidercontent lambda 闭包内的 Composable 都可以访问到这个值。

步骤 3:消费 (Consume) 值

在任何子孙 Composable 中,通过访问 CompositionLocal 实例的 .current 属性来获取当前作用域内提供的那个值。

import com.m8test.script.GlobalVariables.* import com.m8test.script.core.api.ui.compose.compositionlocal.ProvidableCompositionLocal import com.m8test.script.core.api.ui.compose.slot.ColumnScopeSlot private fun Provider(columnSlot: ColumnScopeSlot) { columnSlot.CompositionLocalProvider { // 1. 根据名称获取之前创建的 CompositionLocal val localTestComposition: ProvidableCompositionLocal<String>? = getCompositionLocal("LocalTest") // 2. 提供值,通过 setValues 添加 provides 提供的值 setValues(_iterables.listOf(localTestComposition!!.provides("LocalTextChangeValue"))) // 设置受该 CompositionLocalProvider 影响的内容 setContent { // 在插槽中添加一个文本 Text { // 3. 消费值,这里就是 LocalTextChangeValue setText(getCompositionLocalValue<String>(localTestComposition)!!) } } } } fun side1Run() { // 创建一个 ComposeView, 可以通过声明式ui创建脚本界面 _composeView.create { // 插槽(Slot)是一种内容分发机制。它允许你在封装一个通用组件时,在组件内部预留一些“坑位”或“插槽”,然后让使用该组件的父组件来决定这些“坑位”里到底填充什么内容。 // 创建一个Column,Column是一种垂直布局,可以让其中的组件垂直排列 Column { // 设置 Column 中 content 插槽的内容 setContent { // 1. 创建 CompositionLocal val localTestComposition = getCompositionLocals().compositionLocalOf("LocalTest") { "LocalTestInitValue" } Provider(this) // 在插槽中添加一个文本 Text { // 3. 消费值,这里因为没有通过CompositionLocalProvider改变值,所以还是 LocalTextInitValue setText(getCompositionLocalValue(localTestComposition)!!) } } } } // 启动一个Activity用于显示脚本界面 _activity.start() } //-m8test-remove side1Run();
import com.m8test.script.core.api.ui.compose.compositionlocal.ProvidableCompositionLocal import com.m8test.script.core.api.ui.compose.slot.ColumnScopeSlot private def Provider(ColumnScopeSlot columnSlot) { columnSlot.CompositionLocalProvider { provider -> // 1. 根据名称获取之前创建的 CompositionLocal ProvidableCompositionLocal<String> localTestComposition = provider.getCompositionLocal("LocalTest") // 2. 提供值,通过 setValues 添加 provides 提供的值 provider.setValues($iterables.listOf(localTestComposition.provides("LocalTextChangeValue"))) // 设置受该 CompositionLocalProvider 影响的内容 provider.setContent { providerSlot -> // 在插槽中添加一个文本 providerSlot.Text { text -> // 3. 消费值,这里就是 LocalTextChangeValue text.setText(text.getCompositionLocalValue(localTestComposition)) } } } } // 创建一个 ComposeView, 可以通过声明式ui创建脚本界面 $composeView.create { slot -> // 插槽(Slot)是一种内容分发机制。它允许你在封装一个通用组件时,在组件内部预留一些“坑位”或“插槽”,然后让使用该组件的父组件来决定这些“坑位”里到底填充什么内容。 // 创建一个Column,Column是一种垂直布局,可以让其中的组件垂直排列 slot.Column { column -> // 设置 Column 中 content 插槽的内容 column.setContent { columnSlot -> // 1. 创建 CompositionLocal def localTestComposition = columnSlot.getCompositionLocals().compositionLocalOf("LocalTest") { "LocalTestInitValue" } Provider(columnSlot) // 在插槽中添加一个文本 columnSlot.Text { text -> // 3. 消费值,这里因为没有通过CompositionLocalProvider改变值,所以还是 LocalTextInitValue text.setText(text.getCompositionLocalValue(localTestComposition)) } } } } // 启动一个Activity用于显示脚本界面 $activity.start()
/** * @param {Packages.com.m8test.script.core.api.ui.compose.slot.ColumnScopeSlot} columnSlot */ function Provider(columnSlot) { columnSlot.CompositionLocalProvider(provider => { // 1. 根据名称获取之前创建的 CompositionLocal /** @type {Packages.com.m8test.script.core.api.ui.compose.compositionlocal.ProvidableCompositionLocal<String>} */ const localTestComposition = provider.getCompositionLocal("LocalTest"); // 2. 提供值,通过 setValues 添加 provides 提供的值 provider.setValues($iterables.listOf(localTestComposition.provides("LocalTextChangeValue"))); // 设置受该 CompositionLocalProvider 影响的内容 provider.setContent(providerSlot => { // 在插槽中添加一个文本 providerSlot.Text(text => { // 3. 消费值,这里就是 LocalTextChangeValue text.setText(text.getCompositionLocalValue(localTestComposition)); }); }); }); } // 创建一个 ComposeView, 可以通过声明式ui创建脚本界面 $composeView.create(slot => { // 插槽(Slot)是一种内容分发机制。它允许你在封装一个通用组件时,在组件内部预留一些“坑位”或“插槽”,然后让使用该组件的父组件来决定这些“坑位”里到底填充什么内容。 // 创建一个Column,Column是一种垂直布局,可以让其中的组件垂直排列 slot.Column(column => { // 设置 Column 中 content 插槽的内容 column.setContent(columnSlot => { // 1. 创建 CompositionLocal const localTestComposition = columnSlot.getCompositionLocals().compositionLocalOf("LocalTest", () => "LocalTestInitValue"); Provider(columnSlot); // 在插槽中添加一个文本 columnSlot.Text(text => { // 3. 消费值,这里因为没有通过CompositionLocalProvider改变值,所以还是 LocalTextInitValue text.setText(text.getCompositionLocalValue(localTestComposition)); }); }); }); }); // 启动一个Activity用于显示脚本界面 $activity.start();
-- 引入 java 类 local ProvidableCompositionLocal = require("m8test_java.com.m8test.script.core.api.ui.compose.compositionlocal.ProvidableCompositionLocal") local function Provider(columnSlot) columnSlot:CompositionLocalProvider(function(provider) -- 1. 根据名称获取之前创建的 CompositionLocal local localTestComposition = provider:getCompositionLocal("LocalTest") -- 2. 提供值,通过 setValues 添加 provides 提供的值 provider:setValues(_iterables:listOf(localTestComposition:provides("LocalTextChangeValue"))) -- 设置受该 CompositionLocalProvider 影响的内容 provider:setContent(function(providerSlot) -- 在插槽中添加一个文本 providerSlot:Text(function(text) -- 3. 消费值,这里就是 LocalTextChangeValue text:setText(text:getCompositionLocalValue(localTestComposition)) end) end) end) end -- 创建一个 ComposeView, 可以通过声明式ui创建脚本界面 _composeView:create(function(slot) -- 插槽(Slot)是一种内容分发机制。它允许你在封装一个通用组件时,在组件内部预留一些“坑位”或“插槽”,然后让使用该组件的父组件来决定这些“坑位”里到底填充什么内容。 -- 创建一个Column,Column是一种垂直布局,可以让其中的组件垂直排列 slot:Column(function(column) -- 设置 Column 中 content 插槽的内容 column:setContent(function(columnSlot) -- 1. 创建 CompositionLocal local localTestComposition = columnSlot:getCompositionLocals():compositionLocalOf("LocalTest", function() return "LocalTestInitValue" end) Provider(columnSlot) -- 在插槽中添加一个文本 columnSlot:Text(function(text) -- 3. 消费值,这里因为没有通过CompositionLocalProvider改变值,所以还是 LocalTextInitValue text:setText(text:getCompositionLocalValue(localTestComposition)) end) end) end) end) -- 启动一个Activity用于显示脚本界面 _activity:start()
<?php use m8test_java\com\m8test\script\core\api\ui\compose\compositionlocal\ProvidableCompositionLocal; use m8test_java\com\m8test\script\core\api\ui\compose\slot\ColumnScopeSlot; /** @var m8test_java\com\m8test\script\core\api\ui\compose\ComposeView $composeView */ global $composeView; /** @var m8test_java\com\m8test\script\core\api\ui\Activity $activity */ global $activity; /** @var m8test_java\com\m8test\script\core\api\collections\Iterables $iterables */ global $iterables; function Provider(ColumnScopeSlot $columnSlot) { $columnSlot->CompositionLocalProvider(function ($provider) { global $iterables; // 1. 根据名称获取之前创建的 CompositionLocal /** @var ProvidableCompositionLocal $localTestComposition */ $localTestComposition = $provider->getCompositionLocal(javaString("LocalTest")); // 2. 提供值,通过 setValues 添加 provides 提供的值 $provider->setValues($iterables->listOf($localTestComposition->provides(javaString("LocalTextChangeValue")))); // 设置受该 CompositionLocalProvider 影响的内容 $provider->setContent(function ($providerSlot) use ($localTestComposition) { // 在插槽中添加一个文本 $providerSlot->Text(function ($text) use ($localTestComposition) { // 3. 消费值,这里就是 LocalTextChangeValue $text->setText($text->getCompositionLocalValue($localTestComposition)); }); }); }); } // 创建一个 ComposeView, 可以通过声明式ui创建脚本界面 $composeView->create(function ($slot) { // 插槽(Slot)是一种内容分发机制。它允许你在封装一个通用组件时,在组件内部预留一些“坑位”或“插槽”,然后让使用该组件的父组件来决定这些“坑位”里到底填充什么内容。 // 创建一个Column,Column是一种垂直布局,可以让其中的组件垂直排列 $slot->Column(function ($column) { // 设置 Column 中 content 插槽的内容 $column->setContent(function ($columnSlot) { // 1. 创建 CompositionLocal $localTestComposition = $columnSlot->getCompositionLocals()->compositionLocalOf(javaString("LocalTest"), function () { return javaString("LocalTestInitValue"); }); Provider($columnSlot); // 在插槽中添加一个文本 $columnSlot->Text(function ($text) use ($localTestComposition) { // 3. 消费值,这里因为没有通过CompositionLocalProvider改变值,所以还是 LocalTestInitValue $text->setText($text->getCompositionLocalValue($localTestComposition)); }); }); }); }); // 启动一个Activity用于显示脚本界面 $activity->start();
# 导入所需的全局变量 from m8test_java.com.m8test.script.GlobalVariables import _activity from m8test_java.com.m8test.script.GlobalVariables import _composeView from m8test_java.com.m8test.script.GlobalVariables import _iterables # 导入所需的Java类 from m8test_java.com.m8test.script.core.api.ui.compose.slot.ColumnScopeSlot import ColumnScopeSlot def Provider(columnSlot: ColumnScopeSlot): columnSlot.CompositionLocalProvider(lambda provider: ( # 1. 根据名称获取之前创建的 CompositionLocal (localTestComposition := provider.getCompositionLocal("LocalTest")), # 2. 提供值,通过 setValues 添加 provides 提供的值 provider.setValues(_iterables.listOf(localTestComposition.provides("LocalTextChangeValue"))), # 设置受该 CompositionLocalProvider 影响的内容 provider.setContent(lambda providerSlot: ( # 在插槽中添加一个文本 providerSlot.Text(lambda text: ( # 3. 消费值,这里就是 LocalTextChangeValue text.setText(text.getCompositionLocalValue(localTestComposition)) )) )) )) # 创建一个 ComposeView, 可以通过声明式ui创建脚本界面 _composeView.create(lambda slot: ( # 插槽(Slot)是一种内容分发机制。它允许你在封装一个通用组件时,在组件内部预留一些“坑位”或“插槽”,然后让使用该组件的父组件来决定这些“坑位”里到底填充什么内容。 # 创建一个Column,Column是一种垂直布局,可以让其中的组件垂直排列 slot.Column(lambda column: ( # 设置 Column 中 content 插槽的内容 column.setContent(lambda columnSlot: ( # 1. 创建 CompositionLocal # Groovy的 { "LocalTestInitValue" } 是一个无参闭包,返回一个值,对应Python的 lambda: "LocalTestInitValue" (localTestComposition := columnSlot.getCompositionLocals().compositionLocalOf("LocalTest", lambda: "LocalTestInitValue")), Provider(columnSlot), # 在插槽中添加一个文本 columnSlot.Text(lambda text: ( # 3. 消费值,这里因为没有通过CompositionLocalProvider改变值,所以还是 LocalTestInitValue text.setText(text.getCompositionLocalValue(localTestComposition)) )) )) )) )) # 启动一个Activity用于显示脚本界面 _activity.start()
# encoding: utf-8 def Provider(columnSlot) columnSlot.CompositionLocalProvider do |provider| # 1. 根据名称获取之前创建的 CompositionLocal localTestComposition = provider.getCompositionLocal("LocalTest") # 2. 提供值,通过 setValues 添加 provides 提供的值 provider.setValues($iterables.listOf(localTestComposition.provides("LocalTextChangeValue"))) # 设置受该 CompositionLocalProvider 影响的内容 provider.setContent do |providerSlot| # 在插槽中添加一个文本 providerSlot.Text do |text| # 3. 消费值,这里就是 LocalTextChangeValue text.setText(text.getCompositionLocalValue(localTestComposition)) end end end end # 创建一个 ComposeView, 可以通过声明式ui创建脚本界面 $composeView.create do |slot| # 插槽(Slot)是一种内容分发机制。它允许你在封装一个通用组件时,在组件内部预留一些“坑位”或“插槽”,然后让使用该组件的父组件来决定这些“坑位”里到底填充什么内容。 # 创建一个Column,Column是一种垂直布局,可以让其中的组件垂直排列 slot.Column do |column| # 设置 Column 中 content 插槽的内容 column.setContent do |columnSlot| # 1. 创建 CompositionLocal localTestComposition = columnSlot.getCompositionLocals().compositionLocalOf("LocalTest") { "LocalTestInitValue" } Provider(columnSlot) # 在插槽中添加一个文本 columnSlot.Text do |text| # 3. 消费值,这里因为没有通过CompositionLocalProvider改变值,所以还是 LocalTextInitValue text.setText(text.getCompositionLocalValue(localTestComposition)) end end end end # 启动一个Activity用于显示脚本界面 $activity.start()

进阶用法

CompositionLocalProvider 是可以嵌套的。内层的 Provider 会覆盖外层 Provider 为同一个 CompositionLocal 提供的值,这个覆盖效果仅在内层 Provider 的作用域内有效。

这在某些场景下非常有用,比如在一个深色主题的 App 中,让某一个卡片强制使用浅色主题。

import com.m8test.script.GlobalVariables.* import com.m8test.script.core.api.ui.compose.compositionlocal.ProvidableCompositionLocal import com.m8test.script.core.api.ui.compose.slot.ColumnScopeSlot import com.m8test.script.core.api.ui.compose.slot.UnitScopeSlot private fun Provider2(providerSlot: UnitScopeSlot) { // 嵌套一个 CompositionLocalProvider providerSlot.CompositionLocalProvider { // 1. 根据名称获取之前创建的 CompositionLocal val localTestComposition: ProvidableCompositionLocal<String>? = getCompositionLocal("LocalTest") // 2. 提供值,通过 setValues 添加 provides 提供的值 setValues(_iterables.listOf(localTestComposition!!.provides("LocalTextChangeValue2"))) // 设置受嵌套 CompositionLocalProvider 影响的内容 setContent { // 在插槽中添加一个文本 Text { // 3. 消费值,这里就是 LocalTextChangeValue2 setText(getCompositionLocalValue(localTestComposition)!!) } } } } private fun Provider1(columnSlot: ColumnScopeSlot) { columnSlot.CompositionLocalProvider { // 1. 根据名称获取之前创建的 CompositionLocal val localTestComposition: ProvidableCompositionLocal<String>? = getCompositionLocal("LocalTest") // 2. 提供值,通过 setValues 添加 provides 提供的值 setValues(_iterables.listOf(localTestComposition!!.provides("LocalTextChangeValue"))) // 设置受该 CompositionLocalProvider 影响的内容 setContent { // 在插槽中添加一个文本 Text { // 3. 消费值,这里就是 LocalTextChangeValue setText(getCompositionLocalValue(localTestComposition)!!) } Provider2(this) } } } fun side1Run() { // 创建一个 ComposeView, 可以通过声明式ui创建脚本界面 _composeView.create { // 插槽(Slot)是一种内容分发机制。它允许你在封装一个通用组件时,在组件内部预留一些“坑位”或“插槽”,然后让使用该组件的父组件来决定这些“坑位”里到底填充什么内容。 // 创建一个Column,Column是一种垂直布局,可以让其中的组件垂直排列 Column { // 设置 Column 中 content 插槽的内容 setContent { // 1. 创建 CompositionLocal val localTestComposition = getCompositionLocals().compositionLocalOf("LocalTest") { "LocalTestInitValue" } Provider1(this) // 在插槽中添加一个文本 Text { // 3. 消费值,这里因为没有通过CompositionLocalProvider改变值,所以还是 LocalTextInitValue setText(getCompositionLocalValue(localTestComposition)!!) } } } } // 启动一个Activity用于显示脚本界面 _activity.start() } //-m8test-remove side1Run();
import com.m8test.script.core.api.ui.compose.compositionlocal.ProvidableCompositionLocal import com.m8test.script.core.api.ui.compose.slot.ColumnScopeSlot import com.m8test.script.core.api.ui.compose.slot.UnitScopeSlot private def Provider2(UnitScopeSlot providerSlot) { // 嵌套一个 CompositionLocalProvider providerSlot.CompositionLocalProvider { nestProvider -> // 1. 根据名称获取之前创建的 CompositionLocal ProvidableCompositionLocal<String> localTestComposition = nestProvider.getCompositionLocal("LocalTest") // 2. 提供值,通过 setValues 添加 provides 提供的值 nestProvider.setValues($iterables.listOf(localTestComposition.provides("LocalTextChangeValue2"))) // 设置受嵌套 CompositionLocalProvider 影响的内容 nestProvider.setContent { nestProviderSlot -> // 在插槽中添加一个文本 nestProviderSlot.Text { text -> // 3. 消费值,这里就是 LocalTextChangeValue2 text.setText(text.getCompositionLocalValue(localTestComposition)) } } } } private def Provider1(ColumnScopeSlot columnSlot) { columnSlot.CompositionLocalProvider { provider -> // 1. 根据名称获取之前创建的 CompositionLocal ProvidableCompositionLocal<String> localTestComposition = provider.getCompositionLocal("LocalTest") // 2. 提供值,通过 setValues 添加 provides 提供的值 provider.setValues($iterables.listOf(localTestComposition.provides("LocalTextChangeValue"))) // 设置受该 CompositionLocalProvider 影响的内容 provider.setContent { providerSlot -> // 在插槽中添加一个文本 providerSlot.Text { text -> // 3. 消费值,这里就是 LocalTextChangeValue text.setText(text.getCompositionLocalValue(localTestComposition)) } Provider2(providerSlot) } } } // 创建一个 ComposeView, 可以通过声明式ui创建脚本界面 $composeView.create { slot -> // 插槽(Slot)是一种内容分发机制。它允许你在封装一个通用组件时,在组件内部预留一些“坑位”或“插槽”,然后让使用该组件的父组件来决定这些“坑位”里到底填充什么内容。 // 创建一个Column,Column是一种垂直布局,可以让其中的组件垂直排列 slot.Column { column -> // 设置 Column 中 content 插槽的内容 column.setContent { columnSlot -> // 1. 创建 CompositionLocal def localTestComposition = columnSlot.getCompositionLocals().compositionLocalOf("LocalTest") { "LocalTestInitValue" } Provider1(columnSlot) // 在插槽中添加一个文本 columnSlot.Text { text -> // 3. 消费值,这里因为没有通过CompositionLocalProvider改变值,所以还是 LocalTextInitValue text.setText(text.getCompositionLocalValue(localTestComposition)) } } } } // 启动一个Activity用于显示脚本界面 $activity.start()
/** * @param {Packages.com.m8test.script.core.api.ui.compose.slot.UnitScopeSlot} providerSlot */ function Provider2(providerSlot) { // 嵌套一个 CompositionLocalProvider providerSlot.CompositionLocalProvider(nestProvider => { // 1. 根据名称获取之前创建的 CompositionLocal /** @type {Packages.com.m8test.script.core.api.ui.compose.compositionlocal.ProvidableCompositionLocal<String>} */ const localTestComposition = nestProvider.getCompositionLocal("LocalTest"); // 2. 提供值,通过 setValues 添加 provides 提供的值 nestProvider.setValues($iterables.listOf(localTestComposition.provides("LocalTextChangeValue2"))); // 设置受嵌套 CompositionLocalProvider 影响的内容 nestProvider.setContent(nestProviderSlot => { // 在插槽中添加一个文本 nestProviderSlot.Text(text => { // 3. 消费值,这里就是 LocalTextChangeValue2 text.setText(text.getCompositionLocalValue(localTestComposition)); }); }); }); } /** * @param {Packages.com.m8test.script.core.api.ui.compose.slot.ColumnScopeSlot} columnSlot */ function Provider1(columnSlot) { columnSlot.CompositionLocalProvider(provider => { // 1. 根据名称获取之前创建的 CompositionLocal /** @type {Packages.com.m8test.script.core.api.ui.compose.compositionlocal.ProvidableCompositionLocal<String>} */ const localTestComposition = provider.getCompositionLocal("LocalTest"); // 2. 提供值,通过 setValues 添加 provides 提供的值 provider.setValues($iterables.listOf(localTestComposition.provides("LocalTextChangeValue"))); // 设置受该 CompositionLocalProvider 影响的内容 provider.setContent(providerSlot => { // 在插槽中添加一个文本 providerSlot.Text(text => { // 3. 消费值,这里就是 LocalTextChangeValue text.setText(text.getCompositionLocalValue(localTestComposition)); }); Provider2(providerSlot); }); }); } // 创建一个 ComposeView, 可以通过声明式ui创建脚本界面 $composeView.create(slot => { // 插槽(Slot)是一种内容分发机制。它允许你在封装一个通用组件时,在组件内部预留一些“坑位”或“插槽”,然后让使用该组件的父组件来决定这些“坑位”里到底填充什么内容。 // 创建一个Column,Column是一种垂直布局,可以让其中的组件垂直排列 slot.Column(column => { // 设置 Column 中 content 插槽的内容 column.setContent(columnSlot => { // 1. 创建 CompositionLocal const localTestComposition = columnSlot.getCompositionLocals().compositionLocalOf("LocalTest", () => "LocalTestInitValue"); Provider1(columnSlot); // 在插槽中添加一个文本 columnSlot.Text(text => { // 3. 消费值,这里因为没有通过CompositionLocalProvider改变值,所以还是 LocalTextInitValue text.setText(text.getCompositionLocalValue(localTestComposition)); }); }); }); }); // 启动一个Activity用于显示脚本界面 $activity.start();
-- private def Provider2(UnitScopeSlot providerSlot) local function Provider2(providerSlot) -- 嵌套一个 CompositionLocalProvider providerSlot:CompositionLocalProvider(function(nestProvider) -- 1. 根据名称获取之前创建的 CompositionLocal local localTestComposition = nestProvider:getCompositionLocal("LocalTest") -- 2. 提供值,通过 setValues 添加 provides 提供的值 nestProvider:setValues(_iterables:listOf(localTestComposition:provides("LocalTextChangeValue2"))) -- 设置受嵌套 CompositionLocalProvider 影响的内容 nestProvider:setContent(function(nestProviderSlot) -- 在插槽中添加一个文本 nestProviderSlot:Text(function(text) -- 3. 消费值,这里就是 LocalTextChangeValue2 text:setText(text:getCompositionLocalValue(localTestComposition)) end) end) end) end -- private def Provider1(ColumnScopeSlot columnSlot) local function Provider1(columnSlot) columnSlot:CompositionLocalProvider(function(provider) -- 1. 根据名称获取之前创建的 CompositionLocal local localTestComposition = provider:getCompositionLocal("LocalTest") -- 2. 提供值,通过 setValues 添加 provides 提供的值 provider:setValues(_iterables:listOf(localTestComposition:provides("LocalTextChangeValue"))) -- 设置受该 CompositionLocalProvider 影响的内容 provider:setContent(function(providerSlot) -- 在插槽中添加一个文本 providerSlot:Text(function(text) -- 3. 消费值,这里就是 LocalTextChangeValue text:setText(text:getCompositionLocalValue(localTestComposition)) end) Provider2(providerSlot) end) end) end -- 创建一个 ComposeView, 可以通过声明式ui创建脚本界面 _composeView:create(function(slot) -- 插槽(Slot)是一种内容分发机制。它允许你在封装一个通用组件时,在组件内部预留一些“坑位”或“插槽”,然后让使用该组件的父组件来决定这些“坑位”里到底填充什么内容。 -- 创建一个Column,Column是一种垂直布局,可以让其中的组件垂直排列 slot:Column(function(column) -- 设置 Column 中 content 插槽的内容 column:setContent(function(columnSlot) -- 1. 创建 CompositionLocal local localTestComposition = columnSlot:getCompositionLocals():compositionLocalOf("LocalTest", function() return "LocalTestInitValue" end) Provider1(columnSlot) -- 在插槽中添加一个文本 columnSlot:Text(function(text) -- 3. 消费值,这里因为没有通过CompositionLocalProvider改变值,所以还是 LocalTestInitValue text:setText(text:getCompositionLocalValue(localTestComposition)) end) end) end) end) -- 启动一个Activity用于显示脚本界面 _activity:start()
<?php use m8test_java\com\m8test\script\core\api\ui\compose\compositionlocal\ProvidableCompositionLocal; use m8test_java\com\m8test\script\core\api\ui\compose\slot\ColumnScopeSlot; use m8test_java\com\m8test\script\core\api\ui\compose\slot\UnitScopeSlot; /** @var m8test_java\com\m8test\script\core\api\ui\compose\ComposeView $composeView */ global $composeView; /** @var m8test_java\com\m8test\script\core\api\ui\Activity $activity */ global $activity; /** @var m8test_java\com\m8test\script\core\api\collections\Iterables $iterables */ global $iterables; function Provider2(UnitScopeSlot $providerSlot) { // 嵌套一个 CompositionLocalProvider $providerSlot->CompositionLocalProvider(function ($nestProvider) { global $iterables; // 1. 根据名称获取之前创建的 CompositionLocal /** @var ProvidableCompositionLocal $localTestComposition */ $localTestComposition = $nestProvider->getCompositionLocal(javaString("LocalTest")); // 2. 提供值,通过 setValues 添加 provides 提供的值 $nestProvider->setValues($iterables->listOf($localTestComposition->provides(javaString("LocalTextChangeValue2")))); // 设置受嵌套 CompositionLocalProvider 影响的内容 $nestProvider->setContent(function ($nestProviderSlot) use ($localTestComposition) { // 在插槽中添加一个文本 $nestProviderSlot->Text(function ($text) use ($localTestComposition) { // 3. 消费值,这里就是 LocalTextChangeValue2 $text->setText($text->getCompositionLocalValue($localTestComposition)); }); }); }); } function Provider1(ColumnScopeSlot $columnSlot) { $columnSlot->CompositionLocalProvider(function ($provider) { global $iterables; // 1. 根据名称获取之前创建的 CompositionLocal /** @var ProvidableCompositionLocal $localTestComposition */ $localTestComposition = $provider->getCompositionLocal(javaString("LocalTest")); // 2. 提供值,通过 setValues 添加 provides 提供的值 $provider->setValues($iterables->listOf($localTestComposition->provides(javaString("LocalTextChangeValue")))); // 设置受该 CompositionLocalProvider 影响的内容 $provider->setContent(function ($providerSlot) use ($localTestComposition) { // 在插槽中添加一个文本 $providerSlot->Text(function ($text) use ($localTestComposition) { // 3. 消费值,这里就是 LocalTextChangeValue $text->setText($text->getCompositionLocalValue($localTestComposition)); }); Provider2($providerSlot); }); }); } // 创建一个 ComposeView, 可以通过声明式ui创建脚本界面 $composeView->create(function ($slot) { // 插槽(Slot)是一种内容分发机制。它允许你在封装一个通用组件时,在组件内部预留一些“坑位”或“插槽”,然后让使用该组件的父组件来决定这些“坑位”里到底填充什么内容。 // 创建一个Column,Column是一种垂直布局,可以让其中的组件垂直排列 $slot->Column(function ($column) { // 设置 Column 中 content 插槽的内容 $column->setContent(function ($columnSlot) { // 1. 创建 CompositionLocal $localTestComposition = $columnSlot->getCompositionLocals()->compositionLocalOf(javaString("LocalTest"), function () { return javaString("LocalTestInitValue"); }); Provider1($columnSlot); // 在插槽中添加一个文本 $columnSlot->Text(function ($text) use ($localTestComposition) { // 3. 消费值,这里因为没有通过CompositionLocalProvider改变值,所以还是 LocalTestInitValue $text->setText($text->getCompositionLocalValue($localTestComposition)); }); }); }); }); // 启动一个Activity用于显示脚本界面 $activity->start();
# 导入所需的全局变量 from m8test_java.com.m8test.script.GlobalVariables import _activity from m8test_java.com.m8test.script.GlobalVariables import _composeView from m8test_java.com.m8test.script.GlobalVariables import _iterables # 导入所需的Java类 from m8test_java.com.m8test.script.core.api.ui.compose.slot.ColumnScopeSlot import ColumnScopeSlot from m8test_java.com.m8test.script.core.api.ui.compose.slot.UnitScopeSlot import UnitScopeSlot def Provider2(providerSlot: UnitScopeSlot): # 嵌套一个 CompositionLocalProvider providerSlot.CompositionLocalProvider(lambda nestProvider: ( # 1. 根据名称获取之前创建的 CompositionLocal (localTestComposition := nestProvider.getCompositionLocal("LocalTest")), # 2. 提供值,通过 setValues 添加 provides 提供的值 nestProvider.setValues(_iterables.listOf(localTestComposition.provides("LocalTextChangeValue2"))), # 设置受嵌套 CompositionLocalProvider 影响的内容 nestProvider.setContent(lambda nestProviderSlot: ( # 在插槽中添加一个文本 nestProviderSlot.Text(lambda text: ( # 3. 消费值,这里就是 LocalTextChangeValue2 text.setText(text.getCompositionLocalValue(localTestComposition)) )) )) )) def Provider1(columnSlot: ColumnScopeSlot): columnSlot.CompositionLocalProvider(lambda provider: ( # 1. 根据名称获取之前创建的 CompositionLocal (localTestComposition := provider.getCompositionLocal("LocalTest")), # 2. 提供值,通过 setValues 添加 provides 提供的值 provider.setValues(_iterables.listOf(localTestComposition.provides("LocalTextChangeValue"))), # 设置受该 CompositionLocalProvider 影响的内容 provider.setContent(lambda providerSlot: ( # 在插槽中添加一个文本 providerSlot.Text(lambda text: ( # 3. 消费值,这里就是 LocalTextChangeValue text.setText(text.getCompositionLocalValue(localTestComposition)) )), Provider2(providerSlot) )) )) # 创建一个 ComposeView, 可以通过声明式ui创建脚本界面 _composeView.create(lambda slot: ( # 插槽(Slot)是一种内容分发机制。它允许你在封装一个通用组件时,在组件内部预留一些“坑位”或“插槽”,然后让使用该组件的父组件来决定这些“坑位”里到底填充什么内容。 # 创建一个Column,Column是一种垂直布局,可以让其中的组件垂直排列 slot.Column(lambda column: ( # 设置 Column 中 content 插槽的内容 column.setContent(lambda columnSlot: ( # 1. 创建 CompositionLocal (localTestComposition := columnSlot.getCompositionLocals().compositionLocalOf("LocalTest", lambda: "LocalTestInitValue")), Provider1(columnSlot), # 在插槽中添加一个文本 columnSlot.Text(lambda text: ( # 3. 消费值,这里因为没有通过CompositionLocalProvider改变值,所以还是 LocalTextInitValue text.setText(text.getCompositionLocalValue(localTestComposition)) )) )) )) )) # 启动一个Activity用于显示脚本界面 _activity.start()
# encoding: utf-8 def Provider2(providerSlot) # 嵌套一个 CompositionLocalProvider providerSlot.CompositionLocalProvider do |nestProvider| # 1. 根据名称获取之前创建的 CompositionLocal localTestComposition = nestProvider.getCompositionLocal("LocalTest") # 2. 提供值,通过 setValues 添加 provides 提供的值 nestProvider.setValues($iterables.listOf(localTestComposition.provides("LocalTextChangeValue2"))) # 设置受嵌套 CompositionLocalProvider 影响的内容 nestProvider.setContent do |nestProviderSlot| # 在插槽中添加一个文本 nestProviderSlot.Text do |text| # 3. 消费值,这里就是 LocalTextChangeValue2 text.setText(text.getCompositionLocalValue(localTestComposition)) end end end end def Provider1(columnSlot) columnSlot.CompositionLocalProvider do |provider| # 1. 根据名称获取之前创建的 CompositionLocal localTestComposition = provider.getCompositionLocal("LocalTest") # 2. 提供值,通过 setValues 添加 provides 提供的值 provider.setValues($iterables.listOf(localTestComposition.provides("LocalTextChangeValue"))) # 设置受该 CompositionLocalProvider 影响的内容 provider.setContent do |providerSlot| # 在插槽中添加一个文本 providerSlot.Text do |text| # 3. 消费值,这里就是 LocalTextChangeValue text.setText(text.getCompositionLocalValue(localTestComposition)) end Provider2(providerSlot) end end end # 创建一个 ComposeView, 可以通过声明式ui创建脚本界面 $composeView.create do |slot| # 插槽(Slot)是一种内容分发机制。它允许你在封装一个通用组件时,在组件内部预留一些“坑位”或“插槽”,然后让使用该组件的父组件来决定这些“坑位”里到底填充什么内容。 # 创建一个Column,Column是一种垂直布局,可以让其中的组件垂直排列 slot.Column do |column| # 设置 Column 中 content 插槽的内容 column.setContent do |columnSlot| # 1. 创建 CompositionLocal localTestComposition = columnSlot.getCompositionLocals().compositionLocalOf("LocalTest") { "LocalTestInitValue" } Provider1(columnSlot) # 在插槽中添加一个文本 columnSlot.Text do |text| # 3. 消费值,这里因为没有通过CompositionLocalProvider改变值,所以还是 LocalTextInitValue text.setText(text.getCompositionLocalValue(localTestComposition)) end end end end # 启动一个Activity用于显示脚本界面 $activity.start()

总结与最佳实践

优点

  • 解耦 :避免了无关组件的参数污染,使组件签名更简洁。

  • 易于维护 :当需要给深层组件添加新数据时,无需修改中间所有组件。

  • 功能强大 :适用于实现主题、本地化等贯穿整个应用的功能。

注意事项和最佳实践

  1. 不要滥用CompositionLocal 会让数据流变得不那么明确。对于组件的核心逻辑数据, 显式参数传递仍然是首选 ,因为它能让组件的依赖关系一目了然,更易于测试和复用。

  2. 何时使用 :当数据是“环境性的”(ambient),并且在组件树的很多地方都可能需要时,才是使用 CompositionLocal 的好时机。例如:主题、用户偏好、语言设置。

  3. 提供默认值 :总是为 compositionLocalOf 提供一个有意义的默认值,以避免在忘记提供值时应用崩溃。

  4. 明智选择 static vs non-static :仔细考虑你的数据是否会改变。绝大多数情况下, compositionLocalOf 是更安全、更合适的选择。只有在数据真正是静态的情况下才考虑 staticCompositionLocalOf 以获得微小的性能提升。

  5. 命名约定 :通常以 Local 开头命名你的 CompositionLocal 实例,例如 LocalUserLocalTheme ,这是一种社区约定,有助于代码的可读性。

CompositionLocal 是 Jetpack Compose 工具箱中一个非常强大且必不可少的工具,正确地使用它可以极大地提升代码的整洁度和可维护性。

09 December 2025