
本文详解如何在基于 onDraw() 的自定义 View(如网格绘制组件)中动态更新画笔颜色,重点解决调用 invalidate() 后颜色不生效的常见问题,并提供健壮、可复用的实现方案。
本文详解如何在基于 `ondraw()` 的自定义 view(如网格绘制组件)中动态更新画笔颜色,重点解决调用 `invalidate()` 后颜色不生效的常见问题,并提供健壮、可复用的实现方案。
在 Android 开发中,通过继承 View 并重写 onDraw() 实现自定义绘制(如相机辅助线网格)是常见需求。但许多开发者会遇到“颜色已设置、invalidate() 已调用,但界面未刷新”的问题——这通常并非 invalidate() 失效,而是颜色值传递逻辑或 Paint 状态管理存在隐性缺陷。
? 根本问题分析
从您提供的 DrawGrid.java 代码可见,setCurrentColor(int color) 使用了 switch-case 将传入参数映射为预设颜色(0→WHITE, 1→RED, 2→BLUE)。然而,在 ColorFragment 的点击事件中,您直接传入的是 Color.RED 或 Color.BLUE 这类系统常量(值为 0xFFFF0000、0xFF0000FF),远超 case 0/1/2 范围,导致 currentColor 始终保持初始值 Color.WHITE,paint.setColor() 实际未更新,invalidate() 自然无法触发视觉变化。
此外,当前设计将颜色逻辑耦合在 DrawGrid 内部判断,既降低灵活性,又增加调用方理解成本。
✅ 推荐解决方案:直接传递 @ColorInt 值
修改 DrawGrid 的 setCurrentColor 方法,移除硬编码 switch,直接接收并应用颜色整数值:
public void setCurrentColor(@ColorInt int color) {
this.currentColor = color;
paint.setColor(color);
invalidate(); // 确保触发重绘
Log.d("DrawGrid", "Color updated to: 0x" + Integer.toHexString(color));
}
✅ 优势:
- 调用方(如 ColorFragment)可直接使用 Color.RED、ContextCompat.getColor() 或自定义色值;
- 避免 magic number 映射错误;
- 符合 Android 官方注解规范(@ColorInt 提供编译期类型检查)。
?️ 同步更新调用端代码(ColorFragment)
确保按钮点击时传入真实颜色值,而非索引:
red.setOnClickListener(v -> {
drawGrid.setCurrentColor(Color.RED); // ✅ 直接传 Color.RED
hideSelf();
});
blue.setOnClickListener(v -> {
drawGrid.setCurrentColor(ContextCompat.getColor(requireContext(), R.color.my_blue)); // ✅ 支持资源色
hideSelf();
});
⚠️ 关键注意事项:
- 避免重复创建 DrawGrid 实例:您在 onCreateView() 中每次新建 DrawGrid 并 addView(),但未保存引用或清理旧实例,易导致内存泄漏与绘制冲突。建议在 GridMenuFragment 中初始化 DrawGrid 并全局持有,ColorFragment 仅通过接口或引用调用其方法;
- Paint 复用安全:当前 paint 在构造时初始化且只设置 setColor(),无需重建,性能安全;
- invalidate() 足够:对于简单重绘,invalidate() 即可触发 onDraw();若需局部刷新(如仅重绘某条线),可用 invalidate(Rect),但本例无需。
? 总结
颜色不更新的本质是传参值与处理逻辑不匹配,而非 invalidate() 失效。最佳实践是:
- 简化参数契约:setCurrentColor() 直接接收 @ColorInt int,由调用方负责颜色选择;
- 确保单例引用:DrawGrid 应作为唯一视图实例被多个 Fragment 共享操作;
- 验证日志定位:在 onDraw() 和 setCurrentColor() 中打印 currentColor,确认值是否真正传递成功。
遵循以上方案,网格颜色即可响应式实时更新,代码更清晰、健壮且易于扩展。











