You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Jetpack Compose Desktop中竖线无法与横线对齐的问题排查

问题描述

我正在学习Kotlin和Jetpack Compose Desktop,已经完成数独求解器开发,目前制作其图形界面。窗口尺寸设为800×800,四边均有20.dp内边距。横线绘制公式正常,但竖线出现偏移,且Canvas宽高小于窗口声明尺寸,调整列高度时画线符合预期。

数独界面
窗口属性

数独界面Composable代码

@Composable
fun displayPuzzle(answer: Array<Array<IntArray>>) {
    var list = mutableStateListOf<String>()
    for (x in answer[0]) list.addAll(x.map { it.toString() })

    var columnHeighty by remember { mutableStateOf(0F) }
    var columnWidthx by remember { mutableStateOf(0f) }
    var pad = 20

    LazyVerticalGrid(

        columns = GridCells.Fixed(9),
        contentPadding = PaddingValues(
            start = pad.dp,
            top = pad.dp,
            end = pad.dp,
            bottom = pad.dp
        )

    ) {
        items(list.size) { index ->
            Card(
                backgroundColor = Color.Red,
                modifier = Modifier
                    .fillMaxWidth()
                    .fillMaxHeight()
                    .onGloballyPositioned { coordinates ->
                        columnWidthx = coordinates.size.width.toFloat()
                        columnHeighty = coordinates.size.height.toFloat()
                    },

                border = BorderStroke(width = 1.dp, color = Color.White)
            ) {
                Text(
                    text = list[index],
                    fontWeight = FontWeight.Bold,
                    fontSize = 30.sp,
                    color = Color(0xFF000000),
                    textAlign = TextAlign.Center,
                    modifier = Modifier.padding(23.dp)
                )
            }

        }

    }

    Canvas(modifier = Modifier.fillMaxSize()) {
        val canvasWidth = size.width
        val canvasHeight = size.height
        val strokeWidth = 5.0F

        println("Canvas Width $canvasWidth")
        println("Canvas Height $canvasHeight")
        println("Column Width $columnWidthx")
        println("Column Height $columnHeighty")

        //Draw 1st vertical separator
        drawLine(
            start = Offset(x = columnWidthx * 3 + pad.toFloat(), y = pad.toFloat()),
            end = Offset(x = columnWidthx * 3 + pad.toFloat(), y = canvasHeight - pad.toFloat()),
            color = Color.Black,
            strokeWidth = strokeWidth
        )
        //Draw 2nd vertical separator
        drawLine(
            start = Offset(x = columnWidthx * 6 + pad.toFloat(), y = pad.toFloat()),
            end = Offset(x = columnWidthx * 6 + pad.toFloat(), y = canvasHeight - pad.toFloat()),
            color = Color.Black,
            strokeWidth = strokeWidth
        )
        //Draw 1st horizontal separator
        drawLine(
            start = Offset(x = pad.toFloat(), y = columnHeighty * 3 + pad.toFloat()),
            end = Offset(x = canvasWidth - pad.toFloat() , y = columnHeighty * 3 + pad.toFloat()),
            color = Color.Black,
            strokeWidth = strokeWidth
        )
        //Draw 2nd horizontal seperator
        drawLine(
            start = Offset(x = pad.toFloat(), y = columnHeighty * 6 + pad.toFloat()),
            end = Offset(x = canvasWidth - pad.toFloat() , y = columnHeighty * 6 + pad.toFloat()),
            color = Color.Black,
            strokeWidth = strokeWidth
        )
        //Draw top border
        drawLine(
            start = Offset(x = pad.toFloat(), y = pad.toFloat() + strokeWidth / 2),
            end = Offset(x = canvasWidth - pad.toFloat() , y = pad.toFloat() + strokeWidth / 2),
            color = Color.Black,
            strokeWidth = strokeWidth
        )
        //Draw bottom border
        drawLine(
            start = Offset(x = pad.toFloat(), y = canvasHeight - pad.toFloat() - strokeWidth / 2),
            end = Offset(x = canvasWidth - pad.toFloat() , y = canvasHeight - pad.toFloat() - strokeWidth / 2),
            color = Color.Black,
            strokeWidth = strokeWidth
        )
        //Draw left border
        drawLine(
            start = Offset(x = pad.toFloat(), y = pad.toFloat()),
            end = Offset(x = pad.toFloat() , y = canvasHeight - pad.toFloat()),
            color = Color.Black,
            strokeWidth = strokeWidth
        )
        //Draw right border
        drawLine(
            start = Offset(x = canvasWidth - pad.toFloat(), y = pad.toFloat()),
            end = Offset(x = canvasWidth - pad.toFloat() , y = canvasHeight - pad.toFloat()),
            color = Color.Black,
            strokeWidth = strokeWidth
        )

    }

}

窗口初始化代码

Window(onCloseRequest = ::exitApplication) {
    window.minimumSize = Dimension(800,800)
    displayPuzzle(finalAnswer[0])
}

解决方案

问题根源

  1. Canvas尺寸异常:LazyVerticalGrid与Canvas为同级Composable,默认会按垂直顺序排列,Canvas仅能占据Grid剩余空间,无法填满整个窗口,导致其宽高小于窗口声明尺寸。
  2. 竖线偏移:columnWidthx仅为单个Card的宽度,但LazyVerticalGrid默认存在列间距,计算竖线位置时未考虑该间距,导致偏移。

修复步骤

  1. 让Canvas覆盖整个窗口:用Box包裹LazyVerticalGrid和Canvas,使Canvas叠在Grid上方并填满整个Box(即窗口):
Box(modifier = Modifier.fillMaxSize()) {
    LazyVerticalGrid(/* 原Grid代码 */) { /* 原items代码 */ }
    Canvas(modifier = Modifier.fillMaxSize()) { /* 原绘制代码 */ }
}
  1. 修正竖线位置计算:

    • 禁用Grid默认间距:设置horizontalArrangement = Arrangement.spacedBy(0.dp)和verticalArrangement = Arrangement.spacedBy(0.dp),避免间距干扰宽度计算。
    • 获取Grid整体宽度:给Grid添加onGloballyPositioned获取整体宽度,计算每列实际宽度(含无间距的均匀分配):
      var gridWidth by remember { mutableStateOf(0f) }
      // ...
      LazyVerticalGrid(
          modifier = Modifier.onGloballyPositioned { gridWidth = it.size.width.toFloat() },
          horizontalArrangement = Arrangement.spacedBy(0.dp),
          verticalArrangement = Arrangement.spacedBy(0.dp),
          // 其他参数不变
      )
      val actualColumnWidth = gridWidth / 9
      
    • 用actualColumnWidth替代columnWidthx计算竖线位置。
  2. 统一单位转换:将pad.toFloat()替换为pad.dp.toPx(),确保dp与像素单位一致,避免内边距计算偏差。

完整修复后核心代码片段

@Composable
fun displayPuzzle(answer: Array<Array<IntArray>>) {
    var list = mutableStateListOf<String>()
    for (x in answer[0]) list.addAll(x.map { it.toString() })

    var columnHeighty by remember { mutableStateOf(0F) }
    var gridWidth by remember { mutableStateOf(0f) }
    val pad = 20
    val padPx = pad.dp.toPx()

    Box(modifier = Modifier.fillMaxSize()) {
        LazyVerticalGrid(
            columns = GridCells.Fixed(9),
            contentPadding = PaddingValues(pad.dp),
            horizontalArrangement = Arrangement.spacedBy(0.dp),
            verticalArrangement = Arrangement.spacedBy(0.dp),
            modifier = Modifier.onGloballyPositioned { gridWidth = it.size.width.toFloat() }
        ) {
            items(list.size) { index ->
                Card(
                    backgroundColor = Color.Red,
                    modifier = Modifier
                        .fillMaxWidth()
                        .fillMaxHeight()
                        .onGloballyPositioned { columnHeighty = it.size.height.toFloat() },
                    border = BorderStroke(width = 1.dp, color = Color.White)
                ) {
                    Text(
                        text = list[index],
                        fontWeight = FontWeight.Bold,
                        fontSize = 30.sp,
                        color = Color.Black,
                        textAlign = TextAlign.Center,
                        modifier = Modifier.padding(23.dp)
                    )
                }
            }
        }

        Canvas(modifier = Modifier.fillMaxSize()) {
            val canvasWidth = size.width
            val canvasHeight = size.height
            val strokeWidth = 5.0F
            val actualColumnWidth = gridWidth / 9

            // 绘制竖线
            drawLine(
                start = Offset(x = actualColumnWidth * 3 + padPx, y = padPx),
                end = Offset(x = actualColumnWidth * 3 + padPx, y = canvasHeight - padPx),
                color = Color.Black,
                strokeWidth = strokeWidth
            )
            drawLine(
                start = Offset(x = actualColumnWidth * 6 + padPx, y = padPx),
                end = Offset(x = actualColumnWidth * 6 + padPx, y = canvasHeight - padPx),
                color = Color.Black,
                strokeWidth = strokeWidth
            )

            // 其余横线、边框绘制按同样逻辑替换单位与宽度计算方式
        }
    }
}

内容的提问来源于stack exchange,提问作者JD74

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 13:31:02