要在可组合函数之间共享数据时,可以通过参数传递显式地调用,这通常是最简单和最好的方式。
但随着参数越来越多,组件也越来越多,并且有些数据还需要保持私有性,这时这种方式就会显得很繁琐臃肿,难以维护。 对于这些情况,Compose 提供了CompositionLocals
来作为一种隐式方式在组合函数之间传递数据。说白了就是在 Composable 树中提供的一种共享数据的方式(例如主题配置)。
MaterialTheme 是如何实现的
需要注意的是,此时传入的 content
参数其实是声明在 Theme
中的自定义布局系统,其类型是一个带有 Composable
注解的 lambda
。
我们所关注的 colors
被 remember
修饰后赋值为 rememberedColors
。如果 MaterialTheme
这个 Composable
发生 recompose
时便会检查 colors
是否发生了改变从而决定更新。
接下来使用 CompositionLocalProvider
方法,通过中缀表达式 providers
将 rememberedColors
提供给了 LocalColors
。让我们回到自己的 Composable
中,看看我们是如何通过 MaterialTheme
获取到当前主题配色的。
@Composable
fun MyCard(text: String) {
Box(
modifier = Modifier
.fillMaxWidth()
.height(100.dp)
) {
Text(text = text, color = MaterialTheme.colors.primary)
}
}
这里使用的 MaterialTheme.colors
实际上被定义在 MaterialTheme object
单例对象中:
object MaterialTheme {
val colors: Colors
@Composable
@ReadOnlyComposable
get() = LocalColors.current
val typography: Typography
@Composable
@ReadOnlyComposable
get() = LocalTypography.current
val shapes: Shapes
@Composable
@ReadOnlyComposable
get() = LocalShapes.current
}
MaterialTheme
类单例的 colors
属性,间接使用了 LocalColors
总的来说,我们在自定义 Theme
使用的是 MaterialTheme
函数为 LocalColors
赋值,而在获取时使用的是 MaterialTheme
类单例,间接从 LocalColors
中获取到值。那 LocalColors
又是什么呢?
internal val LocalColors = staticCompositionLocalOf {
lightColors() }
实际上它是一个 CompositionLocal
,其初始值是 lightColors()
返回的 colors 配置。
MaterialTheme
方法中通过 CompositionLocalProvider
方法为我们的自定义视图 Composable
提供了一些 CompositionLocal
,包含了所有的主题配置信息。
CompositionLocal 介绍
CompositionLocals
本质上是分层的。当CompositionLocal
的值需要被限定于组合的特定子层次结构时,它们是有意义的。
CompositionLocals
可以被限定在以某个 Composable
作为根结点的子树中,其默认会向下传递的,当然当前子树中的某个 Composable
可以对该 CompositionLocals
进行覆盖,从而使得新值会在这个 Composable
中继续向下传递。
总的来说,CompositionLocal 它有以下特性:
- 具备函数穿透功能的局部变量,不需要显示的传递的函数参数,
- 多用于提供:上下文/主题 等,方便透传
- 它的着重点在于提供某种上下文,如果是某个函数需要某个参数多数情况应该使用函数参数直接传
要使用 CompositionLocal
必须创建一个 CompositionLocal 实例,消费者可以静态地引用该实例。CompositionLocal 实例本身不保存任何数据,可以将其视为在组合树中向下传递的数据的类型安全标识符。
要创建一个 CompositionLocal 的实例通过调用 compositionLocalOf
方法来实现:
import androidx.compose.runtime.compositionLocalOf
var LocalString = compositionLocalOf {
"Jetpack Compose" }
compositionLocalOf
后面的 lambda 中返回的是一个默认值,这个 lambda 其实是一个工厂函数,其中还可以配置没有提供值的情况下的警告信息,以提醒使用者:
val LocalBackground = compositionLocalOf<Color> {
error("LocalBackground没有提供值") }
compositionLocalOf
的返回值是一个 ProvidableCompositionLocal
对象(它继承了 CompositionLocal
类)。
然后,在 Composable 树的某个地方,我们可以使用 CompositionLocalProvider
方法为 CompositionLocal
提供一个值。通常情况下位于 Composable
树的根部,但也可以位于任何位置,还可以在多个位置使用,以覆盖子树能够获取到的值。
val LocalUserName = compositionLocalOf<String> {
"" }
class CompositionLocalProviderActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
CompositionLocalProvider(LocalUserName provides "Jetpack Compose") {
User()
}
}
}
}
@Composable
fun User() {
Column {
Text(text = LocalUserName.current) // 通过current当前用户名
}
}
比如可以通过这种方式来为 Composable 提供当前 Activity 的实例:
val LocalActivity