【问题标题】:How to use Jetpack Compose Canvas with Layout with dynamic size如何使用带有动态大小的布局的 Jetpack Compose Canvas
【发布时间】:2022-07-18 16:01:49
【问题描述】:

我想在测量可测量对象并使用布局功能放置可放置对象后动态设置androidx.compose.foundation.Canvas 大小,但 Canvas 需要具有特定大小的修饰符,如何使用 Layout 和 Canvas 以有效的方式实现这一点。

我想要实现的是在图像中设置内容并添加一些空间和绘制 红色区域中的箭头用于创建对话气泡,如图here

图像中的第一个容器是正确的,其中 Modifier.layout 和 Modifier.drawbackground 为

@Composable
private fun MyComposable(modifier: Modifier = Modifier) {

    Row(
        modifier = Modifier

    ) {
        var totalSize = Size.Zero

        val layoutModifier = modifier
            .layout { measurable, constraints ->

                val placeable = measurable.measure(constraints)
                totalSize = Size(placeable.width + 50f, placeable.height.toFloat())

                println("MyComposable() LAYOUT placeable: $placeable")

                layout(totalSize.width.roundToInt(), placeable.height) {
                    placeable.place(0, 0)
                }

            }
            .drawBehind {
                println("MyComposable() drawBehind size: $size")
                drawRect(Color.Red, size = totalSize, style = Stroke(2f))
                drawRect(Color.Blue, size = size, style = Stroke(2f))
            }


        Column(modifier = layoutModifier) {
            Text("First Text")
            Text("Second Text")
        }

    }
}

还有日志

I: MyComposable() LAYOUT placeable width: 250, height: 124
I: MyComposable() drawBehind size: Size(250.0, 124.0)

带有布局和画布

@Composable
private fun CanvasLayout(modifier: Modifier = Modifier, content: @Composable () -> Unit) {

    var widthInDp by remember { mutableStateOf(0.dp) }
    var heightInDp by remember { mutableStateOf(0.dp) }

    Layout(content = content, modifier = modifier) { measurables, constraints ->

        val placeables = measurables.map { measurable ->
            measurable.measure(constraints)
        }

        val maxWidth = placeables.maxOf { it.width } + 50
        val height = placeables.sumOf { it.height }

        widthInDp = (maxWidth / density).toDp()
        heightInDp = (height / density).toDp()
        println("???? CanvasLayout() LAYOUT maxWidth: $maxWidth, height: $height placeables: ${placeables.size}")

        var yPos = 0
        layout(maxWidth, height) {
            placeables.forEach { placeable: Placeable ->
                placeable.placeRelative(0, yPos)
                yPos += placeable.height
            }
        }
    }

    androidx.compose.foundation.Canvas(
        modifier = Modifier.size(widthInDp, heightInDp),
        onDraw = {
            println("???? CanvasLayout() CANVAS size: $size")
            drawRect(Color.Blue, size = size, style = Stroke(2f))
        }
    )
}

如图所示,底部矩形绘制在文本组合下方而不是围绕它们并调用layout 两次

I: ???? CanvasLayout() LAYOUT maxWidth: 300, height: 124 placeables: 2
I: ???? CanvasLayout() CANVAS size: Size(0.0, 0.0)
I: ???? CanvasLayout() LAYOUT maxWidth: 300, height: 124 placeables: 2
I: ???? CanvasLayout() CANVAS size: Size(114.0, 47.0)

LayoutCanvas一起使用的正确高效方法是什么?

【问题讨论】:

  • 第一个例子有什么问题?对于第二个,您似乎在 Column 中调用它,所以这是预期的结果。在Canvas的源代码中可以看到,它只在修饰符上调用drawBehind,所以你也可以直接调用它。
  • 没有错。我只想拥有自己的Layout,而不是添加Column 来包装其他将在实际实现中的组合。是的,你是对的,因为外部可组合是 Column,它位于 Canvas 下方 Layout 之下。所以我使用了LayoutModifier.drawBehind并放弃了Canvas
  • 正如你所说,我检查了Canvas 源代码,正如你所说,它只不过是Spacer(modifier.drawBehind(onDraw))。我以为是Layout 和其他属性。也许我应该检查以imageBitmap 作为参数的androidx.compose.ui.graphics.Canvas

标签: android android-jetpack-compose android-jetpack-compose-canvas


【解决方案1】:

有时它需要动态大小,这需要您修改画布大小,如this question.

因为CanvasSpacer

@Composable
fun Canvas(modifier: Modifier, onDraw: DrawScope.() -> Unit) =
    Spacer(modifier.drawBehind(onDraw))

画在后面效果很好。

但有时您使用 Canvas 设计布局时,canvas 恰好位于底层的无状态 Composable 中,例如在默认 Slider 源代码中。

fun Slider() {
   // States rememberables
  SliderImpl()
}

fun SliderImpl() {
   Canvas()
   // Other Composables like thumb
}

当您需要将动态大小传递给 Canvas 时,选项很少

1- 在您想要获取尺寸的布局上使用 Modifier.onSizeChanged{},但不建议这样做,它可能会导致您设置尺寸并再次读取的无限循环

2- 使用 BoxWithConstraints 返回 min/maxWidth、min/maxHeight 但最大约束并不总是对应于布局的边界。当您使用 Modifier.fillMax 或您有固定大小时,当您的 Composable 填充整个布局时,它会起作用。

3- 其他选项是使用SubcomposeLayout 在其中创建主要(您想要测量)和依赖(您想要将测量传递给)可组合项。主要组件被子组合并将维度传递给依赖的组件,然后您对其进行设置。

ScaffoldTabRow 使用此逻辑来测量和空间基于其他的可组合。您可以查看此link about SubcomposeLayout 了解更多详情。 BoxWithConstraintsLazyList 也是 SubcomposeLayouts

4-LookAheadLayout,这也可能有效,我还没有尝试过,当我这样做时,我会用示例编辑这个答案

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-05
    • 1970-01-01
    相关资源
    最近更新 更多