You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

在Xamarin中使用SkiaSharp动态绘制线条时遇运行崩溃问题

问题分析与解决方案

这个问题我之前做SkiaSharp绘图时也踩过坑,核心原因是你错误地缓存了SKCanvasSKSurface对象,这是SkiaSharp新手很容易犯的错误。

为什么缓存会导致崩溃?

SkiaSharp的PaintSurface事件是由系统触发的,每次触发时都会生成全新的SKSurfaceSKCanvas实例——这些对象只在当前事件处理的生命周期内有效,事件执行完毕后,系统可能会回收或销毁它们。你把这些临时对象保存下来,后续调用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包裹SKPaintSKPath等实现了IDisposable的对象,避免内存泄漏
  • 如果需要绘制多条动态更新的线条,只需要维护一个List<LineInfo>集合,在PaintSurface里遍历集合绘制即可
  • 永远不要缓存SKCanvasSKSurfaceSKImageInfo,这些都是每次重绘的临时对象,没有复用价值

内容的提问来源于stack exchange,提问作者random

火山引擎 最新活动