我正在尝试使用 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
)
}
}
您上面发布的代码有很多问题。
首先,在 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
)
}
}
旋转有效,但您绘制的内容与问题中的图像不对称。
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())
)
}
}
结果
我知道现在回答这个问题有点晚了,但以防万一其他人也想知道同样的事情。 要镜像可组合项(例如图像),您可以使用修改器的 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
})
}
结果如下:
尺寸 375 是一个尝试和错误,因为我没有研究你是如何制作两半的,但它是有效的,如果你遵循@Thracian 的说明来制作它,那么我想它会更直接。