You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Xamarin.Forms+SkiaSharp单指绘制:解决MIUI优化下多指异常

问题与解决方案

问题描述

使用Xamarin.Forms结合SkiaSharp实现绘图功能时,出现间歇性异常:
System.ArgumentException: 'An item with the same key has already been added. Key: 1'
该异常在多指绘制时触发,且与设备开启的MIUI优化相关。需求是让页面仅识别单指触摸,确保开启MIUI优化时程序仍正常运行。

解决方案

核心是限制页面仅处理第一个按下的触摸点,后续触摸事件直接忽略,直到当前触摸点释放或取消。具体修改FingerPaintPage.xaml.cs文件:

1. 添加活跃触摸ID记录变量

在FingerPaintPage类中添加成员变量,用于记录当前正在处理的触摸ID:

// 记录当前活跃的单指触摸ID,null表示无活跃触摸
long? activeTouchId = null;

2. 修改触摸事件处理逻辑

更新OnTouchEffectAction方法,加入单指触摸限制逻辑,确保同一时间只处理一个触摸点:

void OnTouchEffectAction(object sender, TouchActionEventArgs args)
{
    // 单指触摸限制:仅处理当前活跃的触摸点,或第一个按下的触摸点
    switch (args.Type)
    {
        case TouchActionType.Pressed:
            // 已有活跃触摸时,忽略新的按下事件
            if (activeTouchId.HasValue)
                return;
            activeTouchId = args.Id;
            break;
        case TouchActionType.Released:
        case TouchActionType.Cancelled:
            // 释放或取消当前活跃触摸时,清空记录
            if (activeTouchId == args.Id)
                activeTouchId = null;
            break;
        default:
            // 非活跃触摸的移动事件直接忽略
            if (!activeTouchId.HasValue || activeTouchId != args.Id)
                return;
            break;
    }

    // 原有触摸处理逻辑保持不变
    switch (args.Type)
    {
        case TouchActionType.Pressed:
            if (!inProgressPolylines.ContainsKey(args.Id))
            {
                Color strokeColor = (Color)typeof(Color).GetRuntimeField(colorPicker.Items[colorPicker.SelectedIndex]).GetValue(null);
                float strokeWidth = ConvertToPixel(new float[] { 1, 2, 5, 10, 20 }[widthPicker.SelectedIndex]);
                colorPicker.BackgroundColor = strokeColor;
                widthPicker.BackgroundColor = strokeColor;

                FingerPaintPolyline polyline = new FingerPaintPolyline
                {
                    StrokeColor = strokeColor,
                    StrokeWidth = strokeWidth
                };
                polyline.Path.MoveTo(ConvertToPixel(args.Location));
                if(!inProgressPolylines.ContainsKey(args.Id))
                {
                    inProgressPolylines.Add(args.Id, polyline);
                    canvasView.InvalidateSurface();
                }
            }
            break;

        case TouchActionType.Moved:
            if (inProgressPolylines.ContainsKey(args.Id))
            {
                FingerPaintPolyline polyline = inProgressPolylines[args.Id];
                try
                {
                    polyline.Path.LineTo(ConvertToPixel(args.Location));
                    canvasView.InvalidateSurface();
                }
                catch { }
            }
            break;

        case TouchActionType.Released:
            if (inProgressPolylines.ContainsKey(args.Id))
            {
                if (!completedPolylines.Contains(inProgressPolylines[args.Id]))
                {
                    completedPolylines.Add(inProgressPolylines[args.Id]);
                }
                inProgressPolylines.Remove(args.Id);
                canvasView.InvalidateSurface();
            }
            break;

        case TouchActionType.Cancelled:
            if (inProgressPolylines.ContainsKey(args.Id))
            {
                inProgressPolylines.Remove(args.Id);
                canvasView.InvalidateSurface();
            }
            break;
    }
}

原理说明

  • 通过activeTouchId变量锁定当前唯一活跃的触摸点,避免多指触摸同时触发事件
  • 新的触摸按下事件会被忽略,直到当前活跃触摸释放或取消
  • 仅处理活跃触摸的移动事件,其他触摸的操作不会进入后续逻辑
  • 从根源上避免了inProgressPolylines字典因重复添加同一Key而抛出的异常,同时兼容MIUI优化开启的场景

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 04:06:25