简介
Compose 以静态和可观察的方式提供数据:
compositionLocalOf
维护所提供对象的可变状态,更改该对象的值会导致提供 CompositionLocal
的整个 content lambda 被重组。
staticCompositionalLocalOf
创建的 CompositionLocal
对象不会观察值的变化,也不会在其值更改时触发重组。另一方面,staticCompositionalLocalOf 不会观察对象的变化,也不会在读取提供的值时触发重组。
注意:CompositionLocal
对象或常量通常带有 Local
前缀,以便在 IDE 中利用自动填充功能提高可检测性。
建议使用 CompositionLocal
的情况为:其可能会被任何(而非少数几个)后代使用。
CompositionLocalProvider
可组合项可将值绑定到给定层次结构的 CompositionLocal
实例,为 CompositionLocal 提供值。在 Composable
之间共享对象而不将它们作为函数参数传递。共享对象存储在 Composer 中,并且在 CompositionLocalProvider
包装的当前 ComposeScope
树中可用
staticCompositionalLocalOf
如果为 CompositionLocal
提供的值发生更改的可能很低或永远不会更改,使用 staticCompositionLocalOf
可提高性能。
val localIntent = staticCompositionLocalOf<Intent> { error("CompositionLocal intent not present") }
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
CompositionLocalProvider(LocalIntent provides intent) {
// TODO ...
}
}
}
}
通过 staticCompositionLocalOf<String> { error("CompositionLocal intent not present") }
限制它在 Provider 之外使用。通过上面这样给定默认值,如果在 Provider
之外访问则会抛出异常。
比如 Compose Material 中的 MaterialTheme
中的 colors
, typography
, shapes
都是通过 staticCompositionLocalOf
实现的。
如下是 androidx 源码中的实现:
// androidx 源码:MaterialTheme.kt
object MaterialTheme {
/**
* Retrieves the current [Colors] at the call site's position in the hierarchy.
*
* @sample androidx.compose.material.samples.ThemeColorSample
*/
val colors: Colors
@Composable
@ReadOnlyComposable
get() = LocalColors.current
/**
* Retrieves the current [Typography] at the call site's position in the hierarchy.
*
* @sample androidx.compose.material.samples.ThemeTextStyleSample
*/
val typography: Typography
@Composable
@ReadOnlyComposable
get() = LocalTypography.current
/**
* Retrieves the current [Shapes] at the call site's position in the hierarchy.
*/
val shapes: Shapes
@Composable
@ReadOnlyComposable
get() = LocalShapes.current
}
// androidx 源码:Colors.kt
internal val LocalColors = staticCompositionLocalOf { lightColors() }
// 自定义 Theme
val lightColors = lightColors(
//...
)
val darkColors = darkColors(
//...
)
@Composable
fun AppTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
content: @Composable () -> Unit
) {
MaterialTheme(
colors = if (darkTheme) darkColors else lightColors,
typography = typography,
shapes = shapes,
content = content
)
}
compositionalLocalOf
compositionLocalOf()
适合值可能会经常改变的场景。
比如 LocalContentColor
的实现
// androidx 源码:ContentColor.kt
package androidx.compose.material
import androidx.compose.runtime.compositionLocalOf
import androidx.compose.ui.graphics.Color
val LocalContentColor = compositionLocalOf { Color.Black }
Text
的文字颜色, Icon
的 tint 颜色, Tab
/TabRow
的 Divider 和 Indicator
, 的使用了 LocalContentColor.current
的值作为默认值。
// androidx 源码:Text.kt
@Composable
fun Text(
text: AnnotatedString,
modifier: Modifier = Modifier,
...
) {
val textColor = color.takeOrElse {
style.color.takeOrElse {
LocalContentColor.current.copy(alpha = LocalContentAlpha.current)
}
}
}
// androidx 源码:Icon.kt
@Composable
@NonRestartableComposable
fun Icon(
bitmap: ImageBitmap,
contentDescription: String?,
modifier: Modifier = Modifier,
tint: Color = LocalContentColor.current.copy(alpha = LocalContentAlpha.current)
) {
val painter = remember(bitmap) { BitmapPainter(bitmap) }
Icon(
painter = painter,
contentDescription = contentDescription,
modifier = modifier,
tint = tint
)
}
Surface
等 Composable 中使用 CompositionLocalProvider
保证其 ComposeScope
中 LocalContentColor
的值可用并在其值发送改变时重组。
// androidx 源码:Surface.kt
@Composable
fun Surface(
onClick: () -> Unit,
modifier: Modifier = Modifier,
...
) {
val absoluteElevation = LocalAbsoluteElevation.current + elevation
CompositionLocalProvider(
LocalContentColor provides contentColor,
LocalAbsoluteElevation provides absoluteElevation
) {
...
}
...
}
更多内容参见官方文档:
https://developer.android.com/jetpack/compose/compositionlocal