关于 maui-graphics-drawing
.NET MAUI Graphics Drawing 技能汇总 GraphicsView 绘制中的状态管理、重绘和性能注意事项,面向构建自定义图形界面的 .NET MAUI 开发者。内容还涵盖阴影、裁剪和命中测试等常见问题。
主要功能
- 01画布状态保存与恢复
- 02使用 Invalidate() 触发重绘
- 03预计算路径并减少分配
- 04管理阴影与裁剪状态
- 05选择静态或动画绘制方案
适用场景
- 01排查 GraphicsView 空白或状态泄漏
- 02实现动画及分层图形
- 03优化逐帧绘制性能
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ davidortinau/ maui-skills --skill maui-graphics-drawing安装后在 Claude Code 中输入/maui-graphics-drawing确认已加载。
附带文件references/graphics-drawing-api.md
详细介绍
.NET MAUI 图形绘制
常见问题
| 问题 | 解决方法 |
|---|---|
| 屏幕上没有任何内容 | 确保 Drawable 已设置到 GraphicsView,且控件的 HeightRequest/WidthRequest 非零 |
| 图形之间的状态相互影响 | 使用成对的 SaveState() / RestoreState() 包裹需要隔离的部分 |
| 阴影影响后续绘制 | 绘制带阴影的元素后,调用 canvas.SetShadow(SizeF.Zero, 0, null) |
| 裁剪状态无法重置 | 每帧的裁剪会累积——使用 SaveState/RestoreState 包裹裁剪区域 |
| 绘制时 UI 卡顿 | 切勿在 Draw() 中执行 I/O、网络操作或大量计算——它在 UI 线程上运行 |
画布状态——始终成对调用 Save/Restore
⚠️ 未配对的 SaveState/RestoreState 会导致状态泄漏到后续绘制调用中。
// ✅ Correct — isolated state
canvas.SaveState();
canvas.StrokeColor = Colors.Red;
canvas.StrokeSize = 6;
canvas.DrawRectangle(10, 10, 80, 80);
canvas.RestoreState();
// Stroke reverts to previous values
// ❌ Wrong — state leaks to everything drawn after
canvas.StrokeColor = Colors.Red;
canvas.StrokeSize = 6;
canvas.DrawRectangle(10, 10, 80, 80);
// Every subsequent shape is now red with size 6
保存/还原的状态:描边、填充、字体、阴影、裁剪和变换。可嵌套调用以隔离多层状态。
触发重绘
// ✅ Correct — queue a redraw
graphicsView.Invalidate();
// ❌ Wrong — never call Draw() directly
myDrawable.Draw(canvas, rect);
Invalidate()会将重绘加入队列;框架会在下一帧调用IDrawable.Draw。- ⚠️ 避免在紧密循环中调用
Invalidate()——批量更改状态,然后只调用一次。
性能提示
- 让
Draw()保持快速——在绘制方法外预先计算路径和数据;每一帧都会调用Draw()。 - 复用
PathF对象——创建一次并存储为字段,然后重复绘制。 - 采用类似
MeasureFirstItem的思路——如果要绘制许多相同的项目,只计算一次尺寸。 - 尽量减少分配——如果路径不会改变,避免在
Draw()中调用new PathF()。
// ✅ Pre-computed path (field)
private readonly PathF _starPath = BuildStarPath();
public void Draw(ICanvas canvas, RectF dirtyRect)
{
canvas.FillPath(_starPath);
}
// ❌ Allocating every frame
public void Draw(ICanvas canvas, RectF dirtyRect)
{
var path = new PathF();
// ...build path every frame...
canvas.FillPath(path);
}
阴影和裁剪——易残留的状态问题
阴影会应用于所有后续绘制,直到显式移除。裁剪会累积,且只能通过 RestoreState() 撤销:
// ✅ Shadow: remove after use
canvas.SetShadow(new SizeF(5, 5), 4, Colors.Gray);
canvas.FillRectangle(20, 20, 100, 60);
canvas.SetShadow(SizeF.Zero, 0, null); // ← must remove
// ✅ Clip: isolate with SaveState/RestoreState
canvas.SaveState();
canvas.ClipRectangle(20, 20, 100, 100);
canvas.FillRectangle(0, 0, 200, 200); // clipped
canvas.RestoreState(); // clip removed
// ❌ Clip persists — everything after is also clipped
canvas.ClipRectangle(20, 20, 100, 100);
canvas.FillEllipse(150, 150, 50, 50); // unintentionally clipped!
在绘制调用之前设置属性
// ✅ Properties then draw
canvas.StrokeColor = Colors.Blue;
canvas.DrawRectangle(10, 10, 100, 50);
// ❌ Setting after draw has no effect on previous shape
canvas.DrawRectangle(10, 10, 100, 50);
canvas.StrokeColor = Colors.Blue;
决策框架
| 需求 | 方案 |
|---|---|
| 简单图形/静态图形 | 使用单个 IDrawable,在 Draw() 中绘制 |
| 动画图形 | 在外部更新状态,通过计时器/动画调用 Invalidate() |
| 复杂的分层场景 | 使用多个 SaveState/RestoreState 块,或分离绘图对象 |
| 对绘制的元素进行命中测试 | 手动跟踪图形边界——GraphicsView 不内置对已绘制内容的命中测试 |
| 平台特定渲染 | 使用处理器/平台代码;Microsoft.Maui.Graphics 仅支持跨平台 |
快速检查清单
-
GraphicsView已设置Drawable,且尺寸非零 -
Draw()执行快速——无 I/O,无大量内存分配 - 每个
SaveState()都有对应的RestoreState() - 使用后通过
SetShadow(SizeF.Zero, 0, null)移除阴影 - 使用
SaveState/RestoreState块包裹裁剪 - 在适用的绘制调用之前设置属性
- 使用
Invalidate(),而不是直接调用Draw()
评论
支持 Markdown