使用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...
相关推荐
zhangphil3 小时前
Android绘图Path基于LinearGradient线性动画渐变,Kotlin(2)
android·kotlin
watl03 小时前
【Android】unzip aar删除冲突classes再zip
android·linux·运维
键盘上的蚂蚁-3 小时前
PHP爬虫类的并发与多线程处理技巧
android
喜欢猪猪4 小时前
Java技术专家视角解读:SQL优化与批处理在大数据处理中的应用及原理
android·python·adb
JasonYin~6 小时前
HarmonyOS NEXT 实战之元服务:静态案例效果---手机查看电量
android·华为·harmonyos
zhangphil6 小时前
Android adb查看某个进程的总线程数
android·adb
抛空6 小时前
Android14 - SystemServer进程的启动与工作流程分析
android
Gerry_Liang8 小时前
记一次 Android 高内存排查
android·性能优化·内存泄露·mat
天天打码9 小时前
ThinkPHP项目如何关闭runtime下Log日志文件记录
android·java·javascript
爱数学的程序猿12 小时前
Python入门:6.深入解析Python中的序列
android·服务器·python