将Xamarin Forms触摸追踪Effect迁移至.NET MAUI遇阻求解决方案
解决方案一:修复原Xamarin.Forms Effect适配.NET MAUI
核心修改点
- 移除UWP平台代码,改用WinUI3适配的Windows平台实现
- 更新Effect基类引用,使用
Microsoft.Maui.Controls.Compatibility命名空间下的PlatformEffect - 调整各平台API命名空间,替换Xamarin专属API为.NET MAUI对应实现
1. 共享项目TouchEffect.cs修改
using Microsoft.Maui.Controls.Compatibility; namespace YourAppNamespace { public class TouchEffect : RoutingEffect { public event TouchActionEventHandler TouchAction; public TouchEffect() : base("YourAppNamespace.TouchEffect") { } public bool Capture { get; set; } public void OnTouchAction(View view, TouchActionEventArgs args) { TouchAction?.Invoke(view, args); } } }
2. Android平台TouchEffect实现
using Android.Content; using Android.Views; using Microsoft.Maui.Controls.Compatibility; using Microsoft.Maui.Controls.Compatibility.Platform.Android; using YourAppNamespace; using YourAppNamespace.Platforms.Android; [assembly: ResolutionGroupName("YourAppNamespace")] [assembly: ExportEffect(typeof(TouchEffect), "TouchEffect")] namespace YourAppNamespace.Platforms.Android { public class TouchEffect : PlatformEffect { private TouchHandler _touchHandler; protected override void OnAttached() { try { _touchHandler = new TouchHandler(Control ?? Container, this); } catch (Exception ex) { Console.WriteLine($"Error attaching touch effect: {ex.Message}"); } } protected override void OnDetached() { _touchHandler?.Dispose(); } private class TouchHandler : Java.Lang.Object, View.IOnTouchListener { private readonly TouchEffect _effect; private readonly View _view; private int _id = -1; public TouchHandler(View view, TouchEffect effect) { _view = view; _effect = effect; view.SetOnTouchListener(this); } public bool OnTouch(View v, MotionEvent e) { switch (e.ActionMasked) { case MotionEventActions.Down: _id = e.GetPointerId(0); _effect.OnTouchAction(_effect.Element, new TouchActionEventArgs(_id, TouchActionType.Pressed, GetTouchLocation(e), true)); break; case MotionEventActions.Move: for (int i = 0; i < e.PointerCount; i++) { _id = e.GetPointerId(i); _effect.OnTouchAction(_effect.Element, new TouchActionEventArgs(_id, TouchActionType.Moved, GetTouchLocation(e), true)); } break; case MotionEventActions.Up: case MotionEventActions.Cancel: _effect.OnTouchAction(_effect.Element, new TouchActionEventArgs(_id, e.ActionMasked == MotionEventActions.Up ? TouchActionType.Released : TouchActionType.Cancelled, GetTouchLocation(e), false)); _id = -1; break; } return true; } private Point GetTouchLocation(MotionEvent e) { var rect = new Rect(); _view.GetGlobalVisibleRect(rect); return new Point(e.RawX - rect.Left, e.RawY - rect.Top); } public void Dispose() { _view.SetOnTouchListener(null); base.Dispose(); } } } }
3. iOS平台TouchEffect及TouchRecognizer实现
TouchEffect.cs
using Microsoft.Maui.Controls.Compatibility; using Microsoft.Maui.Controls.Compatibility.Platform.iOS; using UIKit; using YourAppNamespace; using YourAppNamespace.Platforms.iOS; [assembly: ResolutionGroupName("YourAppNamespace")] [assembly: ExportEffect(typeof(TouchEffect), "TouchEffect")] namespace YourAppNamespace.Platforms.iOS { public class TouchEffect : PlatformEffect { private TouchRecognizer _touchRecognizer; protected override void OnAttached() { try { _touchRecognizer = new TouchRecognizer(Element, this); Container.AddGestureRecognizer(_touchRecognizer); } catch (Exception ex) { Console.WriteLine($"Error attaching touch effect: {ex.Message}"); } } protected override void OnDetached() { if (_touchRecognizer != null) { Container.RemoveGestureRecognizer(_touchRecognizer); _touchRecognizer.Dispose(); } } public void OnTouchAction(Element element, TouchActionEventArgs args) { ((TouchEffect)element.Effects.FirstOrDefault(e => e is TouchEffect))?.OnTouchAction(element, args); } } }
TouchRecognizer.cs
using CoreGraphics; using Microsoft.Maui.Controls; using UIKit; using YourAppNamespace.Platforms.iOS; namespace YourAppNamespace.Platforms.iOS { public class TouchRecognizer : UIGestureRecognizer { private readonly Element _element; private readonly TouchEffect _effect; private nint _previousId; public TouchRecognizer(Element element, TouchEffect effect) { _element = element; _effect = effect; CancelsTouchesInView = false; } public override void TouchesBegan(NSSet touches, UIEvent evt) { base.TouchesBegan(touches, evt); foreach (UITouch touch in touches) { _previousId = touch.Handle; _effect.OnTouchAction(_element, new TouchActionEventArgs((int)_previousId, TouchActionType.Pressed, GetTouchLocation(touch), true)); } } public override void TouchesMoved(NSSet touches, UIEvent evt) { base.TouchesMoved(touches, evt); foreach (UITouch touch in touches) { if (touch.Handle == _previousId) { _effect.OnTouchAction(_element, new TouchActionEventArgs((int)_previousId, TouchActionType.Moved, GetTouchLocation(touch), true)); } } } public override void TouchesEnded(NSSet touches, UIEvent evt) { base.TouchesEnded(touches, evt); foreach (UITouch touch in touches) { if (touch.Handle == _previousId) { _effect.OnTouchAction(_element, new TouchActionEventArgs((int)_previousId, TouchActionType.Released, GetTouchLocation(touch), false)); _previousId = -1; } } } public override void TouchesCancelled(NSSet touches, UIEvent evt) { base.TouchesCancelled(touches, evt); foreach (UITouch touch in touches) { if (touch.Handle == _previousId) { _effect.OnTouchAction(_element, new TouchActionEventArgs((int)_previousId, TouchActionType.Cancelled, GetTouchLocation(touch), false)); _previousId = -1; } } } private Point GetTouchLocation(UITouch touch) { CGPoint location = touch.LocationInView(View); return new Point(location.X, location.Y); } } }
4. Windows平台TouchEffect实现
using Microsoft.Maui.Controls.Compatibility; using Microsoft.Maui.Controls.Compatibility.Platform.Windows; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Input; using YourAppNamespace; using YourAppNamespace.Platforms.Windows; [assembly: ResolutionGroupName("YourAppNamespace")] [assembly: ExportEffect(typeof(TouchEffect), "TouchEffect")] namespace YourAppNamespace.Platforms.Windows { public class TouchEffect : PlatformEffect { private PointerEventHandler _pointerPressedHandler; private PointerEventHandler _pointerMovedHandler; private PointerEventHandler _pointerReleasedHandler; private PointerEventHandler _pointerCanceledHandler; private int _pointerId; protected override void OnAttached() { try { var frameworkElement = Control ?? Container; _pointerPressedHandler = OnPointerPressed; _pointerMovedHandler = OnPointerMoved; _pointerReleasedHandler = OnPointerReleased; _pointerCanceledHandler = OnPointerCanceled; frameworkElement.PointerPressed += _pointerPressedHandler; frameworkElement.PointerMoved += _pointerMovedHandler; frameworkElement.PointerReleased += _pointerReleasedHandler; frameworkElement.PointerCanceled += _pointerCanceledHandler; } catch (Exception ex) { Console.WriteLine($"Error attaching touch effect: {ex.Message}"); } } protected override void OnDetached() { var frameworkElement = Control ?? Container; if (frameworkElement != null) { frameworkElement.PointerPressed -= _pointerPressedHandler; frameworkElement.PointerMoved -= _pointerMovedHandler; frameworkElement.PointerReleased -= _pointerReleasedHandler; frameworkElement.PointerCanceled -= _pointerCanceledHandler; } } private void OnPointerPressed(object sender, PointerRoutedEventArgs e) { var element = Element; if (element == null) return; _pointerId = e.Pointer.PointerId; var point = e.GetCurrentPoint((UIElement)sender).Position; ((TouchEffect)element.Effects.FirstOrDefault(eff => eff is TouchEffect))?.OnTouchAction(element, new TouchActionEventArgs(_pointerId, TouchActionType.Pressed, new Point(point.X, point.Y), true)); e.Handled = true; } private void OnPointerMoved(object sender, PointerRoutedEventArgs e) { var element = Element; if (element == null) return; foreach (var point in e.GetIntermediatePoints((UIElement)sender)) { if (point.PointerId == _pointerId) { ((TouchEffect)element.Effects.FirstOrDefault(eff => eff is TouchEffect))?.OnTouchAction(element, new TouchActionEventArgs(_pointerId, TouchActionType.Moved, new Point(point.Position.X, point.Position.Y), true)); break; } } e.Handled = true; } private void OnPointerReleased(object sender, PointerRoutedEventArgs e) { var element = Element; if (element == null) return; if (e.Pointer.PointerId == _pointerId) { var point = e.GetCurrentPoint((UIElement)sender).Position; ((TouchEffect)element.Effects.FirstOrDefault(eff => eff is TouchEffect))?.OnTouchAction(element, new TouchActionEventArgs(_pointerId, TouchActionType.Released, new Point(point.X, point.Y), false)); _pointerId = -1; } e.Handled = true; } private void OnPointerCanceled(object sender, PointerRoutedEventArgs e) { var element = Element; if (element == null) return; if (e.Pointer.PointerId == _pointerId) { var point = e.GetCurrentPoint((UIElement)sender).Position; ((TouchEffect)element.Effects.FirstOrDefault(eff => eff is TouchEffect))?.OnTouchAction(element, new TouchActionEventArgs(_pointerId, TouchActionType.Cancelled, new Point(point.X, point.Y), false)); _pointerId = -1; } e.Handled = true; } } }
注意事项
- 替换所有
YourAppNamespace为实际项目命名空间 - 保留原共享项目中的
TouchActionEventArgs、TouchActionEventHandler、TouchActionType类 - 清理项目后重新生成,避免缓存问题
解决方案二:使用.NET MAUI Handlers实现触摸追踪
1. 创建自定义触摸感知控件
using Microsoft.Maui.Controls; namespace YourAppNamespace { public class TouchAwareView : ContentView { public event TouchActionEventHandler TouchAction; public void RaiseTouchAction(TouchActionEventArgs args) { TouchAction?.Invoke(this, args); } } }
2. 注册Handler(MauiProgram.cs)
using Microsoft.Maui.Handlers; using YourAppNamespace; using YourAppNamespace.Platforms.Android; using YourAppNamespace.Platforms.iOS; using YourAppNamespace.Platforms.Windows; // ... builder.ConfigureMauiHandlers(handlers => { #if ANDROID handlers.AddHandler<TouchAwareView, TouchAwareViewHandler>(); #elif IOS handlers.AddHandler<TouchAwareView, TouchAwareViewHandler>(); #elif WINDOWS handlers.AddHandler<TouchAwareView, TouchAwareViewHandler>(); #endif });
3. Android平台Handler实现
using Android.Content; using Android.Views; using Microsoft.Maui.Handlers; using Microsoft.Maui.Platform; using YourAppNamespace; namespace YourAppNamespace.Platforms.Android { public class TouchAwareViewHandler : ContentViewHandler { private TouchHandler _touchHandler; protected override void ConnectHandler(MauiContentView platformView) { base.ConnectHandler(platformView); _touchHandler = new TouchHandler(platformView, VirtualView as TouchAwareView); } protected override void DisconnectHandler(MauiContentView platformView) { _touchHandler?.Dispose(); base.DisconnectHandler(platformView); } private class TouchHandler : Java.Lang.Object, View.IOnTouchListener { private readonly TouchAwareView _virtualView; private readonly View _platformView; private int _id = -1; public TouchHandler(View platformView, TouchAwareView virtualView) { _platformView = platformView; _virtualView = virtualView; platformView.SetOnTouchListener(this); } public bool OnTouch(View v, MotionEvent e) { switch (e.ActionMasked) { case MotionEventActions.Down: _id = e.GetPointerId(0); _virtualView.RaiseTouchAction(new TouchActionEventArgs(_id, TouchActionType.Pressed, GetTouchLocation(e), true)); break; case MotionEventActions.Move: for (int i = 0; i < e.PointerCount; i++) { _id = e.GetPointerId(i); _virtualView.RaiseTouchAction(new TouchActionEventArgs(_id, TouchActionType.Moved, GetTouchLocation(e), true)); } break; case MotionEventActions.Up: case MotionEventActions.Cancel: _virtualView.RaiseTouchAction(new TouchActionEventArgs(_id, e.ActionMasked == MotionEventActions.Up ? TouchActionType.Released : TouchActionType.Cancelled, GetTouchLocation(e), false)); _id = -1; break; } return true; } private Point GetTouchLocation(MotionEvent e) { var rect = new Rect(); _platformView.GetGlobalVisibleRect(rect); return new Point(e.RawX - rect.Left, e.RawY - rect.Top); } public void Dispose() { _platformView.SetOnTouchListener(null); base.Dispose(); } } } }
4. iOS平台Handler实现
using CoreGraphics; using Microsoft.Maui.Handlers; using Microsoft.Maui.Platform; using UIKit; using YourAppNamespace; namespace YourAppNamespace.Platforms.iOS { public class TouchAwareViewHandler : ContentViewHandler { private TouchRecognizer _touchRecognizer; protected override void ConnectHandler(MauiContentView platformView) { base.ConnectHandler(platformView); _touchRecognizer = new TouchRecognizer(VirtualView as TouchAwareView); platformView.AddGestureRecognizer(_touchRecognizer); } protected override void DisconnectHandler(MauiContentView platformView) { if (_touchRecognizer != null) { platformView.RemoveGestureRecognizer(_touchRecognizer); _touchRecognizer.Dispose(); } base.DisconnectHandler(platformView); } private class TouchRecognizer : UIGestureRecognizer { private readonly TouchAwareView _virtualView; private nint _previousId; public TouchRecognizer(TouchAwareView virtualView) { _virtualView = virtualView; CancelsTouchesInView = false; } public override void TouchesBegan(NSSet touches, UIEvent evt) { base.TouchesBegan(touches, evt); foreach (UITouch touch in touches) { _previous
相关产品推荐
相关产品推荐

