我什么时候应该使用 Android Jetpack Compose Surface 可组合?

Val*_*kov 11 android android-jetpack android-jetpack-compose

Jetpack Compose 中有Surface可组合,它表示材料表面。表面允许您设置诸如背景颜色或边框之类的内容,但似乎可以使用修饰符来完成相同的操作。我应该什么时候使用 Surface 可组合组件以及它给我带来的好处?

Thr*_*ian 23

Surface是一个Box具有 aModifier.surface()和材质颜色和标高的,它检查祖先的标高始终位于它们之上,并且仅在下面重载,阻止表面后面的触摸传播pointerInput(Unit) {}

@Composable
fun Surface(
    modifier: Modifier = Modifier,
    shape: Shape = RectangleShape,
    color: Color = MaterialTheme.colors.surface,
    contentColor: Color = contentColorFor(color),
    border: BorderStroke? = null,
    elevation: Dp = 0.dp,
    content: @Composable () -> Unit
) {
    val absoluteElevation = LocalAbsoluteElevation.current + elevation
    CompositionLocalProvider(
        LocalContentColor provides contentColor,
        LocalAbsoluteElevation provides absoluteElevation
    ) {
        Box(
            modifier = modifier
                .surface(
                    shape = shape,
                    backgroundColor = surfaceColorAtElevation(
                        color = color,
                        elevationOverlay = LocalElevationOverlay.current,
                        absoluteElevation = absoluteElevation
                    ),
                    border = border,
                    elevation = elevation
                )
                .semantics(mergeDescendants = false) {}
                .pointerInput(Unit) {},
            propagateMinConstraints = true
        ) {
            content()
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Modifier.surface()

private fun Modifier.surface(
    shape: Shape,
    backgroundColor: Color,
    border: BorderStroke?,
    elevation: Dp
) = this.shadow(elevation, shape, clip = false)
    .then(if (border != null) Modifier.border(border, shape) else Modifier)
    .background(color = backgroundColor, shape = shape)
    .clip(shape)
Run Code Online (Sandbox Code Playgroud)

另一个有趣的事情是它的Box参数propagateMinConstraints = true强制第一个后代具有相同的最小约束或尺寸

Surface(
    modifier = Modifier.size(200.dp),
    onClick = {}) {
    Column(
        modifier = Modifier
            .size(50.dp)
            .background(Color.Red, RoundedCornerShape(6.dp))
    ) {}
}

Spacer(modifier = Modifier.height(20.dp))

Surface(
    modifier = Modifier.size(200.dp),
    onClick = {}) {
    Column(
        modifier = Modifier
            .size(50.dp)
            .background(Color.Red, RoundedCornerShape(6.dp))
    ) {
        Box(
            modifier = Modifier
                .size(50.dp)
                .background(Color.Green, RoundedCornerShape(6.dp))
        )

    }
}

Spacer(modifier = Modifier.height(20.dp))

Box(
    modifier = Modifier.size(200.dp)
) {
    Column(
        modifier = Modifier
            .size(50.dp)
            .background(Color.Red, RoundedCornerShape(6.dp))
    ) {
        Box(
            modifier = Modifier
                .size(50.dp)
                .background(Color.Green, RoundedCornerShape(6.dp))
        )

    }
}
Run Code Online (Sandbox Code Playgroud)

在第一个示例中,尽管它具有.dp 大小,但仍Surface强制Column具有200.dpModifier.size(50.dp)大小。

在第二个示例中,Box内部的Column大小为 50.dp,因为它不是 的直接后代Surface

在第三个示例中,如果我们将SurfaceBox 替换为propagateMinConstraints true),则Box允许直接后代使用自己的约束或维度。

在此输入图像描述


Val*_*kov 16

Surface可组合使代码更容易,并明确指示代码使用材料表面。让我们看一个例子:

Surface(
    color = MaterialTheme.colors.primarySurface,
    border = BorderStroke(1.dp, MaterialTheme.colors.secondary),
    shape = RoundedCornerShape(8.dp),
    elevation = 8.dp
) {
    Text(
        text = "example",
        modifier = Modifier.padding(8.dp)
    )
}
Run Code Online (Sandbox Code Playgroud)

结果:

在此处输入图片说明

不使用 Surface 也可以获得相同的结果:

val shape = RoundedCornerShape(8.dp)
val shadowElevationPx = with(LocalDensity.current) { 2.dp.toPx() }
val backgroundColor = MaterialTheme.colors.primarySurface

Text(
    text = "example",
    color = contentColorFor(backgroundColor),
    modifier = Modifier
        .graphicsLayer(shape = shape, shadowElevation = shadowElevationPx)
        .background(backgroundColor, shape)
        .border(1.dp, MaterialTheme.colors.secondary, shape)
        .padding(8.dp)
)
Run Code Online (Sandbox Code Playgroud)

但它有一些缺点:

  • 修改器链相当大,它实现了一个材质表面并不明显
  • 我必须为该形状声明一个变量并将其传递给三个不同的修饰符
  • 它使用contentColorFor来确定内容颜色,而 Surface 在幕后进行。因此,它backgroundColor也用于两个地方。
  • 我必须以像素为单位计算高程
  • Surface根据材料设计调整高度的颜色(在深色主题的情况下)。如果您想要相同的行为,则应手动处理。

有关 Surface 功能的完整列表,最好查看文档

  • 与其他内容视图/可组合项相比,阻止触摸传播可能是 Surface 最重要的作用。 (10认同)

小智 10

Surface相当于CardView视图系统。
通过Surface,您可以设置视图的标高(注意这与 Modifier.shadow 不同)

  • 我认为 compose Card() 相当于 CardView (6认同)