在Xamarin中使用SkiaSharp动态绘制线条时遇运行崩溃问题
问题分析与解决方案
这个问题我之前做SkiaSharp绘图时也踩过坑,核心原因是你错误地缓存了SKCanvas和SKSurface对象,这是SkiaSharp新手很容易犯的错误。
为什么缓存会导致崩溃?
SkiaSharp的PaintSurface事件是由系统触发的,每次触发时都会生成全新的SKSurface和SKCanvas实例——这些对象只在当前事件处理的生命周期内有效,事件执行完毕后,系统可能会回收或销毁它们。你把这些临时对象保存下来,后续调用DrawNewLine时,实际上是在操作一个已经失效的对象,自然会导致程序崩溃。
正确的做法:维护绘图数据,触发重绘
SkiaSharp的正确绘图逻辑应该是:只维护需要绘制的数据(比如线条的坐标、颜色、宽度等),然后通过调用InvalidateSurface()触发PaintSurface事件,在事件内部使用最新的数据完成绘制。
下面是修改后的完整代码示例:
1. 定义线条数据模型
首先我们创建一个类来存储线条的所有参数,方便后续更新和绘制:
// 存储单条线条的信息 public class LineInfo { public float StartX { get; set; } public float StartY { get; set; } public float EndX { get; set; } public float EndY { get; set; } public SKColor Color { get; set; } public float StrokeWidth { get; set; } }
2. 页面类中维护绘图数据
在你的Xamarin页面类里,声明一个变量来存储当前要绘制的线条(如果需要绘制多条,可以用List<LineInfo>):
private LineInfo _currentLine;
3. 修改PaintSurface事件处理方法
在事件内部,根据当前的_currentLine数据来绘制,不要缓存任何临时对象:
void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args) { SKImageInfo info = args.Info; SKSurface surface = args.Surface; SKCanvas canvas = surface.Canvas; // 先清空画布 canvas.Clear(); // 如果有需要绘制的线条,就执行绘制 if (_currentLine != null) { // 使用using包裹SKPaint,避免内存泄漏 using (SKPaint linePaint = new SKPaint { Style = SKPaintStyle.Stroke, Color = _currentLine.Color, StrokeWidth = _currentLine.StrokeWidth }) { canvas.DrawLine( _currentLine.StartX, _currentLine.StartY, _currentLine.EndX, _currentLine.EndY, linePaint); } } }
4. 重写DrawNewLine方法
现在我们不再直接操作canvas,而是更新绘图数据,然后触发重绘:
public void DrawNewLine() { // 更新当前线条的数据 _currentLine = new LineInfo { StartX = 0, StartY = 0, EndX = 50, EndY = 50, Color = SKColors.Red, StrokeWidth = 16 }; // 触发PaintSurface事件,让系统重新绘制画布 CanvasView.InvalidateSurface(); }
额外注意事项
- 务必用
using包裹SKPaint、SKPath等实现了IDisposable的对象,避免内存泄漏 - 如果需要绘制多条动态更新的线条,只需要维护一个
List<LineInfo>集合,在PaintSurface里遍历集合绘制即可 - 永远不要缓存
SKCanvas、SKSurface或SKImageInfo,这些都是每次重绘的临时对象,没有复用价值
内容的提问来源于stack exchange,提问作者random




