使用CompositionLocal简化组合式函数参数

使用CompositionLocal简化组合式函数参数 (tommwq.work)

========================================================================================================================================================================================================================

目录

1. 组合式函数难以维护状态

和对象相比,组合式函数维护状态的能力比较弱。如果所有状态都通过参数列表显示传递,函数将难以维护。每次改动,调用树上所有的函数全部需要改动。如果使用一个全局对象包含所有的状态,一方面引入了全局依赖,一方面对象会变得非常复杂,难以维护一致性。为了解决这个问题,Jetpack通过CompositionLocal提供了一个带有层级结构的状态树,可以理解为OOP中把继承树中所有对象的行为移除之后的结果。

2. 通过CompositionLocal对象隐式传递状态

代码1 定义和使用CompositionLocal

kotlin 复制代码
import androidx.compose.runtime.compositionLocalOf

val ActiveUser = compositionLocalOf<User> { User("someone") }

@Composable
fun SomeScreen() {
    Text(ActiveUser.current.name) // 通过CompositionLocal的成员current访问具体对象。
}

代码2 提供/更换CompositionLocal

kotlin 复制代码
import androidx.compose.runtime.compositionLocalOf
import androidx.compose.runtime.CompositionLocalProvider


@Composable
fun App() {

    Screen() // show default user

    val anotherUser by remember { mutableStateOf(User("another")) }
    CompositionLocalProvider(ActiveUser provides anotherUser) {
        Screen() // show another user
    }

    Screen() // show default user again
}

从上面的例子可以看到,通过CompositionLocal可以隐式传递对象,并且传递给组合式函数对象可以由父函数控制,限制在特定范围内。因为我们可以把哪些与组合式函数自身逻辑无关,同时又会影响函数结果的对象放到CompositionLocal中,简化函数参数列表。

假设组合式函数Screen依赖于某个特性Feature,我们可以通过CompositionLocal隐含的传递Feature对象。

kotlin 复制代码
val LocalFeatureNullable = compositionLocalOf<Feature?> { null }
val LocalFeatureMustProvide = compositionLocalOf<Feature> { error("No Feature provided.") }

@Composable
fun Screen() {
    LocalFeatureNullable.current?.let { DisplayWithFeature() } :? DisplayWithoutFeature() 

    Text(LocalFeatureMustProvide.current.name)
}

3. 重组

有两个函数可以创建CompositionLocal对象:

compositionLocalOf

  1. 用于值频繁变化的场景。
  2. 重组时只有依赖该值的代码重组。

staticCompositionLocalOf

  1. 用于值极少变化的场景。
  2. 重组时CompositionLocalProvider范围内的代码都参与重组。

4. 示例

代码3 使用CompositionLocal简化导航

kotlin 复制代码
val LocalNavController = compositionLocalOf<NavHostController> { error("No NavController provided.") }


@Composable
fun Screen1() {
    Text(text = "Screen1")

    val navctl = LocalNavController.current

    Button(
        onClick = {
            navctl.navigate("screen2")
        },
        modifier = Modifier.wrapContentSize()
    ) {
        Text("打开屏幕2")
    }
}

@Composable
fun Screen2() {
    Text(text = "Screen2")

    val navctl = LocalNavController.current

    Button(
        onClick = {
            navctl.navigate("screen1")
        },
        modifier = Modifier.wrapContentSize()
    ) {
        Text("打开屏幕1")
    }
}

@Composable
fun MainScreen() {
    CompositionLocalProvider(LocalNavController provides rememberNavController()) {
        NavHost(LocalNavController.current, startDestination = "screen1") {
            composable("screen1") { Screen1() }
            composable("screen2") { Screen2() }
        }
    }
}

代码4 部分内置的CompositionLocal对象。文件:AndroidCompositionLocals.android.kt

scala 复制代码
package androidx.compose.ui.platform

...

/**
 * The Android [Configuration]. The [Configuration] is useful for determining how to organize the
 * UI.
 */
val LocalConfiguration = compositionLocalOf<Configuration> {
    noLocalProvidedFor("LocalConfiguration")
}

/**
 * Provides a [Context] that can be used by Android applications.
 */
val LocalContext = staticCompositionLocalOf<Context> {
    noLocalProvidedFor("LocalContext")
}

internal val LocalImageVectorCache = staticCompositionLocalOf<ImageVectorCache> {
    noLocalProvidedFor("LocalImageVectorCache")
}

/**
 * The CompositionLocal containing the current [LifecycleOwner].
 */
val LocalLifecycleOwner = staticCompositionLocalOf<LifecycleOwner> {
    noLocalProvidedFor("LocalLifecycleOwner")
}

/**
 * The CompositionLocal containing the current [SavedStateRegistryOwner].
 */
val LocalSavedStateRegistryOwner = staticCompositionLocalOf<SavedStateRegistryOwner> {
    noLocalProvidedFor("LocalSavedStateRegistryOwner")
}

/**
 * The CompositionLocal containing the current Compose [View].
 */
val LocalView = staticCompositionLocalOf<View> {
    noLocalProvidedFor("LocalView")
}

...

代码5 MaterialTheme使用CompositionLocal控制组件样式

less 复制代码
@Composable
fun MaterialTheme(
    colors: Colors = MaterialTheme.colors,
    typography: Typography = MaterialTheme.typography,
    shapes: Shapes = MaterialTheme.shapes,
    content: @Composable () -> Unit
) {
    ...
    CompositionLocalProvider(
        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)
        }
    }
}

5. 参考资料

  1. juejin.cn/post/709789...
相关推荐
阿巴斯甜5 小时前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker6 小时前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq95277 小时前
Andorid Google 登录接入文档
android
黄林晴8 小时前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab20 小时前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿1 天前
Android MediaPlayer 笔记
android
Jony_1 天前
Android 启动优化方案
android
阿巴斯甜1 天前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android
张小潇1 天前
AOSP15 Input专题InputReader源码分析
android
_小马快跑_1 天前
Kotlin | 协程调度器选择:何时用CoroutineScope配置,何时用launch指定?
android