Xamarin Forms(Android):基于Skiasharp的触摸测试实现方案
实现Xamarin.Forms + SkiaSharp触摸测试自动完成功能
嘿,我来帮你搞定这个自动完成的逻辑!既然已经用SkiaSharp搭好了触摸绘图界面,接下来只需要加一套「框填充状态跟踪+自动检测」的逻辑就行,下面是具体的实现思路和代码示例:
1. 先定义测试框的数据结构
首先得给每个测试框建个“档案”,存它的位置、大小,还有是否被填满的状态——甚至可以加个填充阈值,比如填到80%就算完成,比要求完全填满更灵活:
public class TestBox { public RectF Bounds { get; set; } // 框的位置和尺寸 public bool IsFilled { get; set; } = false; // 是否已填满 public float FillThreshold { get; set; } = 0.8f; // 填充达标阈值(0-1) }
2. 跟踪绘图区域,检测框的填充状态
你应该已经有SKCanvasView的触摸事件处理了,现在要做的是:每次绘图动作结束后,检查每个未填满的框是否被绘制内容覆盖足够面积,然后判断是否所有框都达标。
方案一:精确面积检测(推荐)
用SKRegion计算绘制路径和框的交集面积,判断填充率是否达标,精度很高:
// 假设你用这个列表存所有测试框 private List<TestBox> _testBoxes = new List<TestBox>(); // 假设你用这个路径存用户绘制的所有内容 private SKPath _drawnPath = new SKPath(); // 触摸结束时触发检测 private void OnCanvasViewTouch(object sender, SKTouchEventArgs e) { switch (e.ActionType) { case SKTouchAction.Released: UpdateBoxFillStatus(); CheckIfAllBoxesFilled(); break; // 你的原有绘图逻辑(比如记录路径点) case SKTouchAction.Moved: _drawnPath.LineTo(e.Location); ((SKCanvasView)sender).InvalidateSurface(); break; } } // 更新每个框的填充状态 private void UpdateBoxFillStatus() { foreach (var box in _testBoxes.Where(b => !b.IsFilled)) { // 创建框的区域 using var boxRegion = new SKRegion(); boxRegion.SetRect(box.Bounds.ToSKRectI()); // 创建已绘制内容的区域 using var drawnRegion = new SKRegion(); drawnRegion.SetPath(_drawnPath, new SKRegion(SKRectI.Infinite)); // 计算两者的交集(即被填充的部分) using var intersection = new SKRegion(); intersection.Intersect(boxRegion, drawnRegion); // 计算填充率,达标就标记为已填满 float fillRatio = (float)intersection.Bounds.Area / box.Bounds.Area; if (fillRatio >= box.FillThreshold) { box.IsFilled = true; } } } // 检查是否所有框都已填满,触发完成逻辑 private void CheckIfAllBoxesFilled() { bool allFilled = _testBoxes.All(box => box.IsFilled); if (allFilled) { // 回到主线程更新UI(因为SkiaSharp的触摸事件可能在后台线程) Device.BeginInvokeOnMainThread(() => { DisplayAlert("测试完成", "所有框都已填满啦!", "确定"); // 这里可以加你的后续操作,比如提交测试结果、跳转页面等 }); } }
方案二:简化点检测(适合轻量场景)
如果不需要太精确的面积计算,也可以统计框内的触摸点数量,达到一定数量就判定为填满,实现更简单:
private List<SKPoint> _touchPoints = new List<SKPoint>(); private void OnCanvasViewTouch(object sender, SKTouchEventArgs e) { switch (e.ActionType) { case SKTouchAction.Moved: _touchPoints.Add(e.Location); ((SKCanvasView)sender).InvalidateSurface(); break; case SKTouchAction.Released: UpdateBoxFillStatusWithPoints(); CheckIfAllBoxesFilled(); break; } } private void UpdateBoxFillStatusWithPoints() { foreach (var box in _testBoxes.Where(b => !b.IsFilled)) { // 统计框内的触摸点数量 int pointsInBox = _touchPoints.Count(p => box.Bounds.Contains(p.X, p.Y)); // 比如框内有15个点就算填满(可根据框的大小调整) if (pointsInBox >= 15) { box.IsFilled = true; } } }
3. 给用户可视化反馈(可选但友好)
可以在PaintSurface方法里给已填满的框加个标记,比如半透明的绿色背景,让用户清楚进度:
private void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs e) { var canvas = e.Surface.Canvas; canvas.Clear(SKColors.White); // 绘制测试框 var boxStrokePaint = new SKPaint { Color = SKColors.Gray, Style = SKPaintStyle.Stroke, StrokeWidth = 2 }; var filledBoxFillPaint = new SKPaint { Color = SKColors.LightGreen.WithAlpha(100), Style = SKPaintStyle.Fill }; foreach (var box in _testBoxes) { if (box.IsFilled) { canvas.DrawRect(box.Bounds, filledBoxFillPaint); } canvas.DrawRect(box.Bounds, boxStrokePaint); } // 绘制用户的笔迹 var drawPaint = new SKPaint { Color = SKColors.Black, Style = SKPaintStyle.Stroke, StrokeWidth = 6, StrokeCap = SKStrokeCap.Round }; canvas.DrawPath(_drawnPath, drawPaint); }
小提示
- 用
SKRegion的精确检测性能稍弱,如果测试框很多,建议只在触摸结束后批量检测,别每次触摸移动都跑一遍。 - 阈值可以根据需求调整,比如要求完全填满就把
FillThreshold设为1.0f。 - 记得在页面销毁时清理
SKPaint、SKRegion这些实现了IDisposable的对象,避免内存泄漏。
这样一套逻辑下来,就能实现“所有框填满后自动完成测试”的需求啦!
内容的提问来源于stack exchange,提问作者Ufuk Zimmerman
相关产品推荐
相关产品推荐

