在SKCanvasView上同时使用Xamarin手势识别器与OnTouch方法的问题
解决SKCanvasView中OnTouch与Xamarin GestureRecognizers共存的问题
你遇到的核心矛盾是:Android平台下为了让SKTouchAction.Pressed后能触发Moved事件,必须设置e.Handled = true,但这会阻断Xamarin自带GestureRecognizers的事件传递。以下是几种可行的解决方案:
方案一:Android平台自定义Renderer控制事件分发
针对Android平台单独处理触摸事件的传递逻辑,让SKCanvasView的OnTouch和GestureRecognizers都能处理事件:
- 在Android项目中创建自定义Renderer:
[assembly: ExportRenderer(typeof(GestureContainer), typeof(GestureContainerRenderer))] namespace YourAppNamespace.Droid.Renderers { public class GestureContainerRenderer : SKCanvasViewRenderer { public GestureContainerRenderer(Context context) : base(context) { } public override bool OnTouchEvent(MotionEvent e) { // 先执行SKCanvasView的触摸处理逻辑 base.OnTouchEvent(e); // 将事件手动传递给GestureRecognizers if (Element is GestureContainer gestureView) { foreach (var recognizer in gestureView.GestureRecognizers) { if (recognizer is IGestureRecognizer androidRecognizer) { androidRecognizer.OnTouch(gestureView, e); } } } return true; } } }
- 修改
GestureContainer中的OnTouch方法,根据平台设置e.Handled:
protected override void OnTouch(SKTouchEventArgs e) { switch (e.ActionType) { case SKTouchAction.Moved: // 你的移动逻辑代码 break; case SKTouchAction.Pressed: // 你的按下逻辑代码 break; } // Android平台设置为true保证Moved事件触发,其他平台保持false让GestureRecognizers正常工作 e.Handled = Device.RuntimePlatform == Device.Android; InvalidateSurface(); }
方案二:完全基于SkiaSharp触摸事件实现手势逻辑
放弃Xamarin自带的GestureRecognizers,直接在OnTouch中处理双击、捏合等手势逻辑,从根源避免冲突:
public abstract class GestureContainer : SKCanvasView, IViewportable { private DateTime _lastTapTime; private float _initialPinchDistance; private bool _isPinching; public GestureContainer() { // 无需添加Xamarin GestureRecognizers } private void TapGesture_DoubleTapped(object sender, EventArgs e) { // 双击逻辑 } public void OnPinchUpdated(object sender, PinchGestureUpdatedEventArgs e) { // 捏合逻辑 } protected override void OnTouch(SKTouchEventArgs e) { switch (e.ActionType) { case SKTouchAction.Pressed: // 处理双击 var currentTime = DateTime.Now; if ((currentTime - _lastTapTime).TotalMilliseconds < 300) { TapGesture_DoubleTapped(this, EventArgs.Empty); } _lastTapTime = currentTime; // 处理捏合开始 if (e.InTouchPoints.Count >= 2) { var point1 = e.InTouchPoints[0].Location; var point2 = e.InTouchPoints[1].Location; _initialPinchDistance = SKPoint.Distance(point1, point2); _isPinching = true; } break; case SKTouchAction.Moved: // 你的移动逻辑 // 处理捏合更新 if (_isPinching && e.InTouchPoints.Count >= 2) { var currentDistance = SKPoint.Distance(e.InTouchPoints[0].Location, e.InTouchPoints[1].Location); var scaleFactor = currentDistance / _initialPinchDistance; OnPinchUpdated(this, new PinchGestureUpdatedEventArgs(GestureStatus.Running, scaleFactor, e.Location)); _initialPinchDistance = currentDistance; } break; case SKTouchAction.Released: _isPinching = false; break; } e.Handled = true; // 保证Android上Moved事件正常触发 InvalidateSurface(); } }
方案三:自定义GestureRecognizer允许同时识别
通过自定义GestureRecognizer,允许它和SKCanvasView的触摸事件同时处理,不过这个方法在Android平台的兼容性不如前两种:
public class CustomDoubleTapGestureRecognizer : TapGestureRecognizer { protected override bool ShouldRecognizeSimultaneously(IGestureRecognizer other) { return true; // 允许和其他触摸事件同时识别 } } public class CustomPinchGestureRecognizer : PinchGestureRecognizer { protected override bool ShouldRecognizeSimultaneously(IGestureRecognizer other) { return true; } }
然后在GestureContainer的构造函数中使用这些自定义的GestureRecognizer即可。
内容的提问来源于stack exchange,提问作者Santos
相关产品推荐
相关产品推荐

