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

Jetpack Compose中棋子移动被单元格遮挡的动画问题排查

解决Jetpack Compose国际象棋棋子移动被单元格遮挡的问题

问题核心是棋子的渲染层级低于后续单元格:当前每个棋子都嵌套在对应位置的单元格Box内部,而Column从上到下依次渲染行、Row从左到右渲染列。黑棋向下移动时,目标行的单元格是后渲染的,层级比原棋子所在单元格更高,导致移动过程中棋子被遮挡。

解决方案:将棋子统一渲染在棋盘顶层

把所有棋子从单元格中抽离,放在整个棋盘的最上层,确保棋子的绘制层级高于所有单元格,彻底解决遮挡问题。

修改后的代码示例

@Composable
fun RenderChessBoard(
    chessBoardState: ChessBoard,
    composableScope: CoroutineScope = rememberCoroutineScope(),
    onPieceMove: (ChessPiece, Position) -> Unit
) {
    var cellSize by remember { mutableStateOf(IntSize(0, 0)) }
    val selectedPiece = remember { mutableStateOf<ChessPiece?>(null) }
    var pieceOffsets by remember { mutableStateOf<Map<ChessPiece, IntOffset>>(emptyMap()) }

    // 外层Box:先渲染棋盘单元格(底层),再渲染顶层棋子
    Box {
        // 棋盘单元格区域(仅渲染格子和可移动提示)
        Column {
            chessBoardState.cells.forEachIndexed { rowIndex, row ->
                Row {
                    row.forEachIndexed { columnIndex, _ ->
                        val currentPosition = Position(rowIndex, columnIndex)
                        val cellColor = determineCellColor(selectedPiece.value, currentPosition, chessBoardState)

                        Box(
                            modifier = Modifier
                                .weight(1f)
                                .onSizeChanged { if (cellSize.width == 0) cellSize = it }
                                .aspectRatio(1f)
                                .background(cellColor),
                            contentAlignment = Alignment.Center
                        ) {
                            DrawAvailableMoves(
                                selectedPiece = selectedPiece.value,
                                currentPosition = currentPosition,
                                chessBoard = chessBoardState,
                                onPieceMove = { chessPiece, targetPosition ->
                                    // 计算移动偏移量
                                    pieceOffsets = pieceOffsets + (chessPiece to IntOffset(
                                        (targetPosition.column - chessPiece.position.column) * cellSize.width,
                                        (targetPosition.row - chessPiece.position.row) * cellSize.height
                                    ))
                                    // 动画结束后更新棋盘状态并清理偏移记录
                                    composableScope.launch {
                                        delay(ANIMATION_DURATION_MS)
                                        onPieceMove.invoke(chessPiece, targetPosition)
                                        pieceOffsets = pieceOffsets - chessPiece
                                    }
                                    selectedPiece.value = null
                                }
                            )
                        }
                    }
                }
            }
        }

        // 顶层统一渲染所有棋子
        chessBoardState.cells.flatMap { row -> row.filterNotNull() }.forEach { currentPiece ->
            // 计算棋子在棋盘上的基础位置
            val baseOffset = IntOffset(
                currentPiece.position.column * cellSize.width,
                currentPiece.position.row * cellSize.height
            )
            // 动画偏移量
            val targetOffset = pieceOffsets[currentPiece] ?: IntOffset(0, 0)
            val animatedOffset by animateIntOffsetAsState(
                targetValue = targetOffset,
                animationSpec = tween(durationMillis = ANIMATION_DURATION_MS.toInt())
            )

            PieceView(
                modifier = Modifier
                    .offset { baseOffset + animatedOffset }
                    .size(cellSize.width.dp, cellSize.height.dp),
                piece = currentPiece
            )
        }
    }
}

关键修改点说明

  1. 层级重构:用外层Box包裹棋盘单元格和棋子,先渲染单元格(底层)再渲染棋子(顶层),确保棋子不会被任何单元格遮挡。
  2. 位置计算优化:通过baseOffset计算棋子在棋盘上的初始位置,叠加动画偏移量实现全局坐标定位,避免单元格内局部偏移的层级问题。
  3. 状态清理:动画结束后移除对应棋子的偏移记录,避免影响后续移动的初始位置计算。

其他可选方案(不推荐)

如果不想重构布局,可尝试给棋子添加zIndex = Float.MAX_VALUE修饰符,但zIndex仅在同级布局内生效,跨Column/Row的单元格时可能失效,可靠性远不如顶层渲染方案。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 07:15:36