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
相关产品推荐
相关产品推荐

