Xamarin.iOS 中的多点触控手指跟踪
本文档演示如何跟踪多个手指的触摸事件
有时,多点触控应用程序需要跟踪各个手指在屏幕上同时移动的情况。 一种典型的应用是手指绘画程序。 你希望用户不仅能够使用单根手指进行绘画,而且还能同时使用多根手指进行绘画。 当程序处理多个触摸事件时,它需要区分这些手指。
当手指首次触摸屏幕时,iOS 会为该手指创建一个 UITouch
对象。 当手指在屏幕上移动然后从屏幕上抬起时,该对象保持不变,此时该对象被释放。 为了跟踪手指,程序应避免直接存储此 UITouch
对象。 它可以改用类型为 IntPtr
的 Handle
属性来唯一标识这些 UITouch
对象。
跟踪各个手指的程序几乎始终维护一个用于触摸跟踪的字典。 对于 iOS 程序,字典键是标识特定手指的 Handle
值。 字典值取决于应用程序。 在示例程序中,每个手指笔划(从触摸到释放)都与一个对象相关联,该对象包含渲染该手指绘制的线条所需的所有信息。 该程序为此定义了一个小型 FingerPaintPolyline
类:
class FingerPaintPolyline
{
public FingerPaintPolyline()
{
Path = new CGPath();
}
public CGColor Color { set; get; }
public float StrokeWidth { set; get; }
public CGPath Path { private set; get; }
}
每条折线都有颜色、笔划宽度和 iOS 图形 CGPath
对象,用于在绘制线条时累积和渲染线条的多个点。
下面所示的所有其余代码都包含在名为 FingerPaintCanvasView
的 UIView
派生类中。 当一根或多根手指主动绘制对象时,该类维护一个 FingerPaintPolyline
类型的对象字典:
Dictionary<IntPtr, FingerPaintPolyline> inProgressPolylines = new Dictionary<IntPtr, FingerPaintPolyline>();
该字典允许视图根据 UITouch
对象的 Handle
属性快速获取与每个手指关联的 FingerPaintPolyline
信息。
FingerPaintCanvasView
类还为已完成的折线维护一个 List
对象:
List<FingerPaintPolyline> completedPolylines = new List<FingerPaintPolyline>();
此 List
中的对象顺序与它们的绘制顺序相同。
FingerPaintCanvasView
重写 View
定义的五个方法:
各种 Touches
重写累积构成折线的点。
[Draw
] 重写先绘制已完成的折线,然后绘制正在完成的折线:
public override void Draw(CGRect rect)
{
base.Draw(rect);
using (CGContext context = UIGraphics.GetCurrentContext())
{
// Stroke settings
context.SetLineCap(CGLineCap.Round);
context.SetLineJoin(CGLineJoin.Round);
// Draw the completed polylines
foreach (FingerPaintPolyline polyline in completedPolylines)
{
context.SetStrokeColor(polyline.Color);
context.SetLineWidth(polyline.StrokeWidth);
context.AddPath(polyline.Path);
context.DrawPath(CGPathDrawingMode.Stroke);
}
// Draw the in-progress polylines
foreach (FingerPaintPolyline polyline in inProgressPolylines.Values)
{
context.SetStrokeColor(polyline.Color);
context.SetLineWidth(polyline.StrokeWidth);
context.AddPath(polyline.Path);
context.DrawPath(CGPathDrawingMode.Stroke);
}
}
}
每个 Touches
重写都可能报告多个手指的操作,由存储在方法的 touches
参数中的一个或多个 UITouch
对象指示。 TouchesBegan
重写循环访问这些对象。 对于每个 UITouch
对象,该方法都会创建并初始化一个新的 FingerPaintPolyline
对象,包括存储从 LocationInView
方法获取的手指的初始位置。 使用 UITouch
对象的 Handle
属性作为字典键,将此 FingerPaintPolyline
对象添加到 InProgressPolylines
字典中:
public override void TouchesBegan(NSSet touches, UIEvent evt)
{
base.TouchesBegan(touches, evt);
foreach (UITouch touch in touches.Cast<UITouch>())
{
// Create a FingerPaintPolyline, set the initial point, and store it
FingerPaintPolyline polyline = new FingerPaintPolyline
{
Color = StrokeColor,
StrokeWidth = StrokeWidth,
};
polyline.Path.MoveToPoint(touch.LocationInView(this));
inProgressPolylines.Add(touch.Handle, polyline);
}
SetNeedsDisplay();
}
该方法最后调用 SetNeedsDisplay
以生成对 Draw
重写的调用并更新屏幕。
当一根或多根手指在屏幕上移动时,View
会多次调用其 TouchesMoved
重写。 此重写以类似方式循环访问存储在 touches
参数中的 UITouch
对象,并将手指的当前位置添加到图形路径中:
public override void TouchesMoved(NSSet touches, UIEvent evt)
{
base.TouchesMoved(touches, evt);
foreach (UITouch touch in touches.Cast<UITouch>())
{
// Add point to path
inProgressPolylines[touch.Handle].Path.AddLineToPoint(touch.LocationInView(this));
}
SetNeedsDisplay();
}
touches
集合仅包含自上次调用 TouchesBegan
或 TouchesMoved
以来已移动的手指的 UITouch
对象。 如果需要与当前与屏幕接触的所有手指相对应的 UITouch
对象,可以通过该方法的 UIEvent
参数的 AllTouches
属性获取该信息。
TouchesEnded
重写有两个任务。 它必须将最后一个点添加到图形路径,并将 FingerPaintPolyline
对象从 inProgressPolylines
字典传输到 completedPolylines
列表:
public override void TouchesEnded(NSSet touches, UIEvent evt)
{
base.TouchesEnded(touches, evt);
foreach (UITouch touch in touches.Cast<UITouch>())
{
// Get polyline from dictionary and remove it from dictionary
FingerPaintPolyline polyline = inProgressPolylines[touch.Handle];
inProgressPolylines.Remove(touch.Handle);
// Add final point to path and save with completed polylines
polyline.Path.AddLineToPoint(touch.LocationInView(this));
completedPolylines.Add(polyline);
}
SetNeedsDisplay();
}
只需丢弃字典中的 FingerPaintPolyline
对象即可处理 TouchesCancelled
重写:
public override void TouchesCancelled(NSSet touches, UIEvent evt)
{
base.TouchesCancelled(touches, evt);
foreach (UITouch touch in touches.Cast<UITouch>())
{
inProgressPolylines.Remove(touch.Handle);
}
SetNeedsDisplay();
}
总而言之,此处理允许示例程序跟踪各个手指并将结果绘制在屏幕上:
现在,你已了解如何跟踪屏幕上的各个手指并区分它们。