mcpField
M
maui-graphics-drawing

maui-graphics-drawing

github.com/davidortinau开发17654 次安装16 周前更新
代码仓库

关于 maui-graphics-drawing

.NET MAUI Graphics Drawing 技能汇总 GraphicsView 绘制中的状态管理、重绘和性能注意事项,面向构建自定义图形界面的 .NET MAUI 开发者。内容还涵盖阴影、裁剪和命中测试等常见问题。

主要功能

  1. 01画布状态保存与恢复
  2. 02使用 Invalidate() 触发重绘
  3. 03预计算路径并减少分配
  4. 04管理阴影与裁剪状态
  5. 05选择静态或动画绘制方案

适用场景

  1. 01排查 GraphicsView 空白或状态泄漏
  2. 02实现动画及分层图形
  3. 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