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

Jetpack Compose(Kotlin)如何用图片资源替换Canvas绘制的矩形

Jetpack Compose Canvas 绘制图片替换彩色矩形实现方案

实现步骤

  • 第一步:导入图片资源
    将所有游戏元素对应的.png格式素材放到项目res/drawable-nodpi目录下,按元素类型命名,例如墙体对应wall.png、地板对应floor.png、箱子对应box.png、目标点对应target.png、玩家对应player.png,素材原始尺寸建议和你设定的格子尺寸(当前为64dp)匹配,减少缩放带来的模糊问题。
  • 第二步:预加载图片建立映射
    不要在Canvas绘制循环里重复加载图片,会引发内存抖动、卡顿,要在Composable初始化阶段就加载所有素材,建立游戏元素字符和ImageBitmap的映射表。
  • 第三步:替换原有矩形绘制逻辑
    保留你原本已经写好的格子偏移、居中计算逻辑,把原来调用drawRect画彩色块的部分,替换为drawImage绘制对应素材即可。如果需要实现玩家/箱子站在目标点上的叠加效果,同一个坐标位置先画底层目标点素材,再叠加画上层的玩家/箱子素材就行。

修改后的完整代码参考

import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.res.imageResource
import androidx.compose.ui.unit.IntSize
import kotlin.math.roundToInt

class SokobanActivity : AppCompatActivity() {

    private lateinit var mGameMatrix: Array<Array<String>>

    ...

    @Composable
    fun GameCanvas() {
        // 预加载所有素材,建立元素-图片映射,放在循环外避免重复加载
        val elementImageMap = mapOf(
            // 墙体
            "#" to ImageBitmap.imageResource(id = R.drawable.wall),
            // 地板
            "0" to ImageBitmap.imageResource(id = R.drawable.floor),
            // 箱子
            "$" to ImageBitmap.imageResource(id = R.drawable.box),
            // 目标点
            "." to ImageBitmap.imageResource(id = R.drawable.target),
            // 玩家
            "@" to ImageBitmap.imageResource(id = R.drawable.player)
        )
        // 兜底默认图
        val defaultElementImage = ImageBitmap.imageResource(id = R.drawable.floor)

        Canvas(
            modifier = Modifier
                .fillMaxSize()
        ) {
            drawRect(color = Color.Black)
            val squareDim = 64.dp.value
            // 居中偏移量计算逻辑保持和原有逻辑一致
            val horizontalOffset = (size.width - (mGameMatrix.size * squareDim)) / 2
            val verticalOffset = (size.height - (mGameMatrix.size * squareDim)) / 2
            
            (mGameMatrix.indices).forEach { i ->
                (mGameMatrix[i].indices).forEach { j ->
                    val element = mGameMatrix[i][j]
                    drawGameElement(
                        image = elementImageMap[element] ?: defaultElementImage,
                        offset = Offset(
                            (i * squareDim) + horizontalOffset,
                            (j * squareDim) + verticalOffset
                        ),
                        size = Size(
                            width = squareDim,
                            height = squareDim
                        )
                    )
                }
            }
        }
    }

    private fun DrawScope.drawGameElement(
        image: ImageBitmap,
        offset: Offset,
        size: Size,
    ) {
        drawImage(
            image = image,
            topLeft = offset,
            // 指定绘制尺寸和格子大小一致,自动适配缩放
            dstSize = IntSize(
                width = size.width.roundToInt(),
                height = size.height.roundToInt()
            )
        )
    }
    ...
}

额外说明

  • 如果需要实现元素动画、点击交互,也可以不用Canvas,直接用LazyVerticalGrid或者Box堆叠每个格子的Image组件实现,代码可读性会更高,不过Canvas绘制的性能在格子数量多的时候会更好。
  • 素材放在drawable-nodpi目录可以避免系统根据设备屏幕密度自动缩放素材,导致格子尺寸计算错位。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 12:39:14