You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

将Xamarin Forms触摸追踪Effect迁移至.NET MAUI遇阻求解决方案

解决方案一:修复原Xamarin.Forms Effect适配.NET MAUI

核心修改点

  1. 移除UWP平台代码,改用WinUI3适配的Windows平台实现
  2. 更新Effect基类引用,使用Microsoft.Maui.Controls.Compatibility命名空间下的PlatformEffect
  3. 调整各平台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
相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 23:51:24