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

