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
相关产品推荐
相关产品推荐

