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

基于Jetpack Compose Canvas实现绘图、撤销、重做、擦除及图像保存

基于Jetpack Compose Canvas的绘图功能补充方案

一、画笔颜色与大小调整实现

1. 状态管理与UI组件

首先用可持久化状态存储画笔参数,搭配Compose原生组件实现调整控件:

// 定义存储单条笔触的数据类
data class DrawingStroke(
    val path: Path,
    val color: Color,
    val size: Float
)

// 画笔状态(建议放到ViewModel中全局管理)
var brushColor by rememberSaveable { mutableStateOf(Color.Black) }
var brushSize by rememberSaveable { mutableStateOf(8f) }

// 颜色选择器(Material3示例)
ColorPicker(
    color = brushColor,
    onColorChanged = { brushColor = it },
    modifier = Modifier.fillMaxWidth().padding(16.dp)
)

// 画笔大小调节滑块
Slider(
    value = brushSize,
    onValueChange = { brushSize = it },
    valueRange = 2f..32f,
    modifier = Modifier.fillMaxWidth().padding(16.dp),
    trailingIcon = { Text("${brushSize.toInt()}dp") }
)

2. 关联绘图逻辑

在Canvas绘制时,将选中的颜色和大小应用到笔触:

Canvas(modifier = Modifier.fillMaxSize().pointerInput(Unit) {
    // 此处为已实现的手绘触摸逻辑,新增传递brushColor和brushSize到DrawingStroke
    detectDragGestures(
        onDragStart = { offset ->
            currentPath = Path().apply { moveTo(offset.x, offset.y) }
        },
        onDrag = { change, _ ->
            currentPath?.lineTo(change.position.x, change.position.y)
        },
        onDragEnd = {
            currentPath?.let {
                drawingHistory = drawingHistory + DrawingStroke(it, brushColor, brushSize)
                currentPath = null
            }
        }
    )
}) {
    // 绘制历史笔触
    drawingHistory.forEach { stroke ->
        drawPath(
            path = stroke.path,
            color = stroke.color,
            style = Stroke(width = stroke.size, cap = StrokeCap.Round, join = StrokeJoin.Round)
        )
    }
    // 绘制当前笔触
    currentPath?.let {
        drawPath(
            path = it,
            color = brushColor,
            style = Stroke(width = brushSize, cap = StrokeCap.Round, join = StrokeJoin.Round)
        )
    }
}

二、图像保存为PNG/JPG格式

1. Canvas内容转Bitmap

先将绘图历史渲染为Bitmap:

fun createDrawingBitmap(canvasSize: Size, drawingHistory: List<DrawingStroke>): Bitmap {
    val bitmap = Bitmap.createBitmap(
        canvasSize.width.toInt(),
        canvasSize.height.toInt(),
        Bitmap.Config.ARGB_8888
    )
    val androidCanvas = android.graphics.Canvas(bitmap)
    // 设置画布背景色(默认透明,可改为白色)
    androidCanvas.drawColor(Color.White.toArgb())

    drawingHistory.forEach { stroke ->
        val paint = android.graphics.Paint().apply {
            color = stroke.color.toArgb()
            style = android.graphics.Paint.Style.STROKE
            strokeWidth = stroke.size
            strokeCap = android.graphics.Paint.Cap.ROUND
            strokeJoin = android.graphics.Paint.Join.ROUND
        }
        androidCanvas.drawPath(stroke.path.asAndroidPath(), paint)
    }
    return bitmap
}

2. 保存到系统相册

适配Android 10+的MediaStore存储方式,无需额外权限:

suspend fun saveBitmapToGallery(context: Context, bitmap: Bitmap, format: Bitmap.CompressFormat): Uri? {
    return withContext(Dispatchers.IO) {
        val fileName = "drawing_${System.currentTimeMillis()}.${if (format == Bitmap.CompressFormat.PNG) "png" else "jpg"}"
        val mimeType = if (format == Bitmap.CompressFormat.PNG) "image/png" else "image/jpeg"

        val contentValues = ContentValues().apply {
            put(MediaStore.Images.Media.DISPLAY_NAME, fileName)
            put(MediaStore.Images.Media.MIME_TYPE, mimeType)
            put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES + "/MyDrawings")
        }

        val uri = context.contentResolver.insert(
            MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
            contentValues
        ) ?: return@withContext null

        context.contentResolver.openOutputStream(uri)?.use { outputStream ->
            bitmap.compress(format, 90, outputStream)
        }
        uri
    }
}

三、实用建议

  • 将画笔状态、绘图历史统一放到ViewModel中,避免横竖屏切换时状态丢失,同时简化撤销/重做逻辑的维护
  • Android 9及以下版本需在Manifest中声明WRITE_EXTERNAL_STORAGE权限,并动态申请
  • 可添加常用颜色预设按钮(如黑、红、蓝、黄),减少用户操作成本
  • 保存过程中显示CircularProgressIndicator,给用户明确的进度反馈
  • 保存完成后可发送本地通知,提示用户图片已存入相册

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 21:42:34