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

Android端数字填色点击路径按预设色填充实现问题咨询

Android 平台按数字填色功能实现方案

这类按号填色的逻辑和自由画笔填色本质不一样,别硬套floodFill的方案,不仅容易溢色,性能还差,核心思路是提前把所有带编号的闭合区域预处理好,运行时只做点击命中和状态切换,完全不需要实时算填充范围。

核心前置准备

你要先把填色素材拆成独立的区域单元,每个单元存三个核心属性:

  • 绑定的数字编号、对应固定填充色
  • 闭合Path路径、区域中心点坐标(用来画数字提示)
  • 填充状态标记(未填充/已填充)

额外生成一张和显示画布等大的掩码位图,每个像素直接存储对应位置所属的区域编号,边界线位置存无效值,这个用来做点击判断比走Path区域命中快很多,大分辨率下也不会卡。

交互逻辑

整个流程非常直接:

  • 页面初始加载时,只绘制所有区域的浅灰底色、黑色轮廓,在每个未填充区域的中心点绘制灰色的数字提示
  • 用户在底部功能面板选数字时,记录当前选中的数字编号,给选中的面板按钮加高亮态
  • 用户点击画布时:
    • 把点击坐标转成掩码位图对应的像素坐标,读取该位置存储的区域编号
    • 如果读取到的编号和当前选中的数字不匹配,直接触发个短震动反馈提示就行,不做任何填充操作
    • 如果编号匹配,把对应区域的填充状态改成已填充,触发重绘给该区域涂上绑定的固定颜色,填充完可以加个短渐显动画提升体验
    • 每次填充完检查当前数字绑定的所有区域是不是都填完了,填完就把面板上对应数字的按钮标记为已完成状态

为什么不推荐改floodFill适配

硬改自由填色的泛洪填充逻辑踩坑概率极高:

  • 数字填色的区域边界是1-2px的细线,用户点击位置稍微偏到边界上,泛洪算法很容易溢到相邻同色区域,或者漏填
  • 大尺寸画布上跑泛洪填充计算量大,中低端机上点一下卡几百毫秒很影响体验
  • 你还要额外加校验逻辑判断点到的区域是不是和选中数字匹配,多做了很多无用功

核心代码参考

掩码位图生成(提前在子线程做)

// 掩码图不用抗锯齿,避免边缘像素值混乱导致判断错误
val maskBitmap = Bitmap.createBitmap(canvasWidth, canvasHeight, Bitmap.Config.RGB_565)
val maskCanvas = Canvas(maskBitmap)
val maskPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
    style = Paint.Style.FILL
}
regionList.forEach { region ->
    // 直接把区域编号存在像素的R通道,读取的时候不用额外做映射
    maskPaint.color = Color.rgb(region.num, 0, 0)
    maskCanvas.drawPath(region.path, maskPaint)
}

点击事件处理

override fun onTouchEvent(event: MotionEvent): Boolean {
    if (event.action == MotionEvent.ACTION_UP && currentSelectedNum != -1) {
        // 坐标做边界保护,避免点到画布外面取像素崩溃
        val clickX = event.x.toInt().coerceIn(0, canvasWidth - 1)
        val clickY = event.y.toInt().coerceIn(0, canvasHeight - 1)
        // 取点击位置的像素值,从R通道读区域编号
        val targetNum = Color.red(maskBitmap.getPixel(clickX, clickY))
        if (targetNum == currentSelectedNum) {
            // 编号匹配,更新区域状态重绘
            regionList.find { it.num == targetNum }?.let {
                it.isFilled = true
                invalidate()
                // 检查当前数字的所有区域是否填充完成
                checkNumComplete(targetNum)
            }
        } else {
            // 不匹配给个轻反馈
            performHapticFeedback(HapticFeedbackConstants.CLOCK_TICK)
        }
    }
    return true
}

画布绘制逻辑

override fun onDraw(canvas: Canvas) {
    super.onDraw(canvas)
    // 先画区域填充色和数字提示
    regionList.forEach { region ->
        if (region.isFilled) {
            fillPaint.color = region.fillColor
            canvas.drawPath(region.path, fillPaint)
        } else {
            // 未填充区域画浅灰底
            canvas.drawPath(region.path, emptyBgPaint)
            // 区域中心画数字提示
            canvas.drawText(
                region.num.toString(),
                region.centerX,
                region.centerY,
                numberPaint
            )
        }
    }
    // 最后统一画轮廓线,避免填充色盖住边界
    regionList.forEach {
        canvas.drawPath(it.path, strokePaint)
    }
}

可选优化

  • 掩码图和区域Path可以提前在素材生产阶段生成打包进APK,不用APP运行时计算,打开页面速度更快
  • 超大尺寸填色图可以把掩码图分块加载,降低内存占用
  • 已填充区域上的数字提示记得隐藏,和常规数字填色的效果保持一致

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 21:15:36