如何用Modifier镜像canvas制作的可组合函数?

问题描述 投票:0回答:2

问题描述

我正在尝试使用 Compose 和 Canvas 在 android 上创建一个组件来模拟 7 段显示,如下所示:

为此,我采用了仅创建该组件的一半并向下镜像我创建的这部分的策略,这样我就可以拥有整个显示。

这是 7 段显示屏的顶部部分:

但问题是当“镜像”从上到下时。事实证明,当我添加

Modifier.rotate(180f)
时,图形会围绕画布的原点顺时针旋转,因此它不会出现在屏幕上(如果是逆时针方向则会出现)。

我不想使用字体来解决这个问题,我想通过画布来解决这个问题并自行组合。如果有一种更聪明的方法可以在画布上做到这一点,而不必需要镜子,我想知道。

我的代码

下面是我用来绘制这个的代码:

DisplayComponent.kt

@Composable
fun DisplayComponent(
    modifier: Modifier = Modifier,
    size: Int = 1000,
    color: Color = MaterialTheme.colors.primary,
) {
    Column(modifier = modifier) {
        HalfDisplayComponent(size, color)
        HalfDisplayComponent(
            modifier = Modifier.rotate(180f),
            size = size,
            color = color
        )
    }
}

@Composable
private fun HalfDisplayComponent(
    size: Int,
    color: Color,
    modifier: Modifier = Modifier,
) {
    Box(modifier = modifier) {
        LedModel.values().forEach {
            LedComponent(
                ledModel = it,
                size = size,
                color = color
            )
        }
    }
}

LedModel.kt

enum class LedModel(val coordinates: List<Pair<Float, Float>>) {
    HorizontalTop(
        listOf(
            Pair(0.04f, 0.03f), // Point A
            Pair(0.07f, 0f), // Point B
            Pair(0.37f, 0f), // Point C
            Pair(0.4f, 0.03f), // Point D
            Pair(0.34f, 0.08f), // Point E
            Pair(0.1f, 0.08f), // Point F
        )
    ),
    VerticalRight(
        listOf(
            Pair(0.41f, 0.04f), // Point A
            Pair(0.44f, 0.07f), // Point B
            Pair(0.44f, 0.37f), // Point C
            Pair(0.41f, 0.4f), // Point D
            Pair(0.35f, 0.35f), // Point E
            Pair(0.35f, 0.09f), // Point F
        )
    ),
    VerticalLeft(
        listOf(
            Pair(0.03f, 0.4f), // Point A
            Pair(0f, 0.37f), // Point B
            Pair(0f, 0.07f), // Point C
            Pair(0.03f, 0.04f), // Point D
            Pair(0.09f, 0.09f), // Point E
            Pair(0.09f, 0.35f), // Point F
        )
    ),
    HorizontalBottom(
        listOf(
            Pair(0.1f, 0.36f), // Point A
            Pair(0.34f, 0.36f), // Point B
            Pair(0.39f, 0.4f), // Point C
            Pair(0.05f, 0.4f), // Point D
        )
    ),
}

LedComponent.kt

@Composable
fun LedComponent(
    modifier: Modifier = Modifier,
    size: Int = 30,
    color: Color = MaterialTheme.colors.primary,
    ledModel: LedModel = LedModel.HorizontalTop
) = getPath(ledModel.coordinates).let { path ->
    Canvas(modifier = modifier.scale(size.toFloat())) {
        drawPath(path, color)
    }
}

private fun getPath(coordinates: List<Pair<Float, Float>>): Path = Path().apply {
    coordinates.map {
        transformPointCoordinate(it)
    }.forEachIndexed { index, point ->
        if (index == 0) moveTo(point.x, point.y) else lineTo(point.x, point.y)
    }
}

private fun transformPointCoordinate(point: Pair<Float, Float>) =
    Offset(point.first.dp.value, point.second.dp.value)

我失败的尝试

如前所述,我尝试通过旋转显示器的可组合项来添加

Modifier
,但它不起作用。我是这样做的:

@Composable
fun DisplayComponent(
    modifier: Modifier = Modifier,
    size: Int = 1000,
    color: Color = MaterialTheme.colors.primary,
) {
    Column(modifier = modifier) {
        DisplayFABGComponent(size, color)
        DisplayFABGComponent(
            modifier = Modifier.rotate(180f),
            size = size,
            color = color
        )
    }
}
android kotlin android-jetpack-compose android-canvas android-jetpack-compose-canvas
2个回答
2
投票

您上面发布的代码有很多问题。

首先,在 Jetpack Compose 中,即使您的 Canvas 大小为 0.dp,您仍然可以在任何地方绘制,这是您问题中的第一个问题。您的画布没有尺寸调节器,您可以通过打印

DrawScope.size
来验证,如下所示。

fun LedComponent(
    modifier: Modifier = Modifier,
    size: Int = 1000,
    color: Color = MaterialTheme.colorScheme.primary,
    ledModel: LedModel = LedModel.HorizontalTop
) = getPath(ledModel.coordinates).let { path ->
    Canvas(
        modifier = modifier.scale(size.toFloat())
    ) {

        println("CANVAS size: ${this.size}")
        drawPath(path, color)
    }
}

除了

Modifier.scale(0f)
之外,您输入的任何值都没有任何区别,而且这也不是您应该如何构建或缩放绘图的方式。

如果您设置画布尺寸,例如

@Composable
fun DisplayComponent(
    modifier: Modifier = Modifier,
    size: Int = 1000,
    color: Color = MaterialTheme.colorScheme.primary,
) {
    Column(modifier = modifier) {
        HalfDisplayComponent(
            size,
            color,
            Modifier
                .size(200.dp)
                .border(2.dp,Color.Red)
        )
        HalfDisplayComponent(
            modifier = Modifier
                .size(200.dp)
                .border(2.dp, Color.Cyan)
                .rotate(180f),
            size = size,
            color = color
        )
    }
}

旋转有效,但您绘制的内容与问题中的图像不对称。

enter image description here

point.first.dp.value
这个片段什么也没做。它的作用是将 dp 添加到 float 然后获得 float。这不是进行 float/dp 转换的方式,也没有必要。

您可以使用 one Canvas 或使用 Modifier.drawBehind{} 来实现您的目标。使用尺寸作为半组件的参考创建路径,然后再次绘制并旋转它或创建包含完整 LED 组件的路径。或者,如果您希望单独显示 LED 数字,您可以为每个部分设置路径。

这是一个简单的示例,仅构建一个菱形形状,然后平移并旋转它以使用半组件构建沙漏状形状。您可以使用此示例作为演示,了解如何使用大小作为参考来创建路径、平移和旋转。

fun getHalfPath(path: Path, size: Size) {
    path.apply {
        val width = size.width
        val height = size.height / 2
        moveTo(width * 0f, height * .5f)
        lineTo(width * .3f, height * 0.3f)
        lineTo(width * .7f, height * 0.3f)
        lineTo(width * 1f, height * .5f)
        lineTo(width * .5f, height * 1f)
        lineTo(width * 0f, height * .5f)
    }
}

您需要使用 1/2f 的纵横比才能进行对称绘图。绿色边框用于显示 Box 可组合项的边界。

val path = remember {
    Path()
}

Box(modifier = Modifier
    .border(3.dp, Color.Green)
    .fillMaxWidth(.4f)
    .aspectRatio(1 / 2f)
    .drawBehind {
        if (path.isEmpty) {
            getHalfPath(path, size)
        }

        drawPath(
            path = path,
            color = Color.Red,
            style = Stroke(2.dp.toPx())
        )

        withTransform(
            {
                translate(0f, size.height / 2f)
                rotate(
                    degrees = 180f,
                    pivot = Offset(center.x, center.y / 2)
                )
            }
        ) {
            drawPath(
                path = path,
                color = Color.Black,
                style = Stroke(2.dp.toPx())
            )
        }
    }

结果

enter image description here


0
投票

我知道现在回答这个问题有点晚了,但以防万一其他人也想知道同样的事情。 要镜像可组合项(例如图像),您可以使用修改器的 GraphicLayer 属性并将其绕 Y 轴旋转。

Column(Modifier.fillMaxWidth(),
        horizontalAlignment = Alignment.CenterHorizontally) {
        HalfDisplayComponent(375, Color.Red, Modifier.size(50.dp))
        HalfDisplayComponent(375,
            Color.Green,
            Modifier.size(50.dp).graphicsLayer {
                rotationX = 180f
            })
    }

结果如下:

enter image description here

尺寸 375 是一个尝试和错误,因为我没有研究你是如何制作两半的,但它是有效的,如果你遵循@Thracian 的说明来制作它,那么我想它会更直接。

© www.soinside.com 2019 - 2024. All rights reserved.