Jetpack Compose 中的 CompositionLocal

本文详细介绍了Jetpack Compose中的CompositionLocal,它是一种在Composable函数间隐式传递数据的方式,常用于共享数据如主题配置。文章探讨了MaterialTheme的实现原理,compositionLocalOf与staticCompositionLocalOf的区别,以及如何在Composable中创建和使用CompositionLocal。同时,还讨论了CompositionLocal的替代方案,如显式参数和控制反转。
摘要由CSDN通过智能技术生成

要在可组合函数之间共享数据时,可以通过参数传递显式地调用,这通常是最简单和最好的方式。

但随着参数越来越多,组件也越来越多,并且有些数据还需要保持私有性,这时这种方式就会显得很繁琐臃肿,难以维护。 对于这些情况,Compose 提供了CompositionLocals来作为一种隐式方式在组合函数之间传递数据。说白了就是在 Composable 树中提供的一种共享数据的方式(例如主题配置)。

MaterialTheme 是如何实现的

在这里插入图片描述

需要注意的是,此时传入的 content 参数其实是声明在 Theme 中的自定义布局系统,其类型是一个带有 Composable 注解的 lambda

我们所关注的 colorsremember 修饰后赋值为 rememberedColors。如果 MaterialTheme 这个 Composable 发生 recompose 时便会检查 colors 是否发生了改变从而决定更新。

接下来使用 CompositionLocalProvider 方法,通过中缀表达式 providersrememberedColors 提供给了 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 
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

川峰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值