• Xamarin.Forms实现touch事件


    Xamarin.Forms的View没有touch事件,只能自己实现

    首先,在共享项目里面,放入这几个类,结构大概是这样的:

    using System;
    using Xamarin.Forms;
    
    namespace TouchTracking
    {
        public class TouchActionEventArgs : EventArgs
        {
            public TouchActionEventArgs(long id, TouchActionType type, Point location, bool isInContact)
            {
                Id = id;
                Type = type;
                Location = location;
                IsInContact = isInContact;
            }
    
            public long Id { private set; get; }
    
            public TouchActionType Type { private set; get; }
    
            public Point Location { private set; get; }
    
            public bool IsInContact { private set; get; }
        }
    }
    namespace TouchTracking
    {
        public delegate void TouchActionEventHandler(object sender, TouchActionEventArgs args);
    }
    namespace TouchTracking
    {
        public enum TouchActionType
        {
            Entered,
            Pressed,
            Moved,
            Released,
            Exited,
            Cancelled
        }
    }
    using Xamarin.Forms;
    
    namespace TouchTracking
    {
        public class TouchEffect : RoutingEffect
        {
            public event TouchActionEventHandler TouchAction;
    
            public TouchEffect() : base("XamarinDocs.TouchEffect")
            {
            }
    
            public bool Capture { set; get; }
    
            public void OnTouchAction(Element element, TouchActionEventArgs args)
            {
                TouchAction?.Invoke(element, args);
            }
        }
    }
    using System;
    using SkiaSharp;
    using TouchTracking;
    
    namespace SkiaSharpFormsDemos
    {
        public class TouchPoint
        {
            // For painting
            SKPaint paint = new SKPaint
            {
                Style = SKPaintStyle.Fill
            };
    
            // For dragging
            bool isBeingDragged;
            long touchId;
            SKPoint previousPoint;
    
            public TouchPoint()
            {
            }
    
            public TouchPoint(float x, float y)
            {
                Center = new SKPoint(x, y);
            }
    
            public SKPoint Center { set; get; }
    
            public float Radius { set; get; } = 75;
    
            public SKColor Color { set; get; } = new SKColor(0, 0, 255, 64);
    
            public void Paint(SKCanvas canvas)
            {
                paint.Color = Color;
                canvas.DrawCircle(Center.X, Center.Y, Radius, paint);
            }
    
            public bool ProcessTouchEvent(long id, TouchActionType type, SKPoint location)
            {
                bool centerMoved = false;
    
                // Assumes Capture property of TouchEffect is true!
                switch (type)
                {
                    case TouchActionType.Pressed:
                        if (!isBeingDragged && PointInCircle(location))
                        {
                            isBeingDragged = true;
                            touchId = id;
                            previousPoint = location;
                            centerMoved = false;
                        }
                        break;
    
                    case TouchActionType.Moved:
                        if (isBeingDragged && touchId == id)
                        {
                            Center += location - previousPoint;
                            previousPoint = location;
                            centerMoved = true;
                        }
                        break;
    
                    case TouchActionType.Released:
                        if (isBeingDragged && touchId == id)
                        {
                            Center += location - previousPoint;
                            isBeingDragged = false;
                            centerMoved = true;
                        }
                        break;
    
                    case TouchActionType.Cancelled:
                        isBeingDragged = false;
                        break;
                }
                return centerMoved;
            }
    
            bool PointInCircle(SKPoint pt)
            {
                return (Math.Pow(pt.X - Center.X, 2) + Math.Pow(pt.Y - Center.Y, 2)) < (Radius * Radius);
            }
        }
    }

    然后,在android的项目里面,加入这个类

    using System;
    using System.Collections.Generic;
    using System.Linq;
    
    using Xamarin.Forms;
    using Xamarin.Forms.Platform.Android;
    
    using Android.Views;
    
    [assembly: ResolutionGroupName("XamarinDocs")]
    [assembly: ExportEffect(typeof(TouchTracking.Droid.TouchEffect), "TouchEffect")]
    
    namespace TouchTracking.Droid
    {
        public class TouchEffect : PlatformEffect
        {
            Android.Views.View view;
            Element formsElement;
            TouchTracking.TouchEffect libTouchEffect;
            bool capture;
            Func<double, double> fromPixels;
            int[] twoIntArray = new int[2];
    
            static Dictionary<Android.Views.View, TouchEffect> viewDictionary = 
                new Dictionary<Android.Views.View, TouchEffect>();
    
            static Dictionary<int, TouchEffect> idToEffectDictionary = 
                new Dictionary<int, TouchEffect>();
    
            protected override void OnAttached()
            {
                // Get the Android View corresponding to the Element that the effect is attached to
                view = Control == null ? Container : Control;
    
                // Get access to the TouchEffect class in the .NET Standard library
                TouchTracking.TouchEffect touchEffect = 
                    (TouchTracking.TouchEffect)Element.Effects.
                        FirstOrDefault(e => e is TouchTracking.TouchEffect);
    
                if (touchEffect != null && view != null)
                {
                    viewDictionary.Add(view, this);
    
                    formsElement = Element;
    
                    libTouchEffect = touchEffect;
    
                    // Save fromPixels function
                    fromPixels = view.Context.FromPixels;
    
                    // Set event handler on View
                    view.Touch += OnTouch;
                }
            }
    
            protected override void OnDetached()
            {
                if (viewDictionary.ContainsKey(view))
                {
                    viewDictionary.Remove(view);
                    view.Touch -= OnTouch;
                }
            }
    
            void OnTouch(object sender, Android.Views.View.TouchEventArgs args)
            {
                // Two object common to all the events
                Android.Views.View senderView = sender as Android.Views.View;
                MotionEvent motionEvent = args.Event;
    
                // Get the pointer index
                int pointerIndex = motionEvent.ActionIndex;
    
                // Get the id that identifies a finger over the course of its progress
                int id = motionEvent.GetPointerId(pointerIndex);
    
    
                senderView.GetLocationOnScreen(twoIntArray);
                Point screenPointerCoords = new Point(twoIntArray[0] + motionEvent.GetX(pointerIndex),
                                                      twoIntArray[1] + motionEvent.GetY(pointerIndex));
    
    
                // Use ActionMasked here rather than Action to reduce the number of possibilities
                switch (args.Event.ActionMasked)
                {
                    case MotionEventActions.Down:
                    case MotionEventActions.PointerDown:
                        FireEvent(this, id, TouchActionType.Pressed, screenPointerCoords, true);
    
                        idToEffectDictionary.Add(id, this);
    
                        capture = libTouchEffect.Capture;
                        break;
    
                    case MotionEventActions.Move:
                        // Multiple Move events are bundled, so handle them in a loop
                        for (pointerIndex = 0; pointerIndex < motionEvent.PointerCount; pointerIndex++)
                        {
                            id = motionEvent.GetPointerId(pointerIndex);
    
                            if (capture)
                            {
                                senderView.GetLocationOnScreen(twoIntArray);
    
                                screenPointerCoords = new Point(twoIntArray[0] + motionEvent.GetX(pointerIndex),
                                                                twoIntArray[1] + motionEvent.GetY(pointerIndex));
    
                                FireEvent(this, id, TouchActionType.Moved, screenPointerCoords, true);
                            }
                            else
                            {
                                CheckForBoundaryHop(id, screenPointerCoords);
    
                                if (idToEffectDictionary[id] != null)
                                {
                                    FireEvent(idToEffectDictionary[id], id, TouchActionType.Moved, screenPointerCoords, true);
                                }
                            }
                        }
                        break;
    
                    case MotionEventActions.Up:
                    case MotionEventActions.Pointer1Up:
                        if (capture)
                        {
                            FireEvent(this, id, TouchActionType.Released, screenPointerCoords, false);
                        }
                        else
                        {
                            CheckForBoundaryHop(id, screenPointerCoords);
    
                            if (idToEffectDictionary[id] != null)
                            {
                                FireEvent(idToEffectDictionary[id], id, TouchActionType.Released, screenPointerCoords, false);
                            }
                        }
                        idToEffectDictionary.Remove(id);
                        break;
    
                    case MotionEventActions.Cancel:
                        if (capture)
                        {
                            FireEvent(this, id, TouchActionType.Cancelled, screenPointerCoords, false);
                        }
                        else
                        {
                            if (idToEffectDictionary[id] != null)
                            {
                                FireEvent(idToEffectDictionary[id], id, TouchActionType.Cancelled, screenPointerCoords, false);
                            }
                        }
                        idToEffectDictionary.Remove(id);
                        break;
                }
            }
    
            void CheckForBoundaryHop(int id, Point pointerLocation)
            { 
                TouchEffect touchEffectHit = null;
    
                foreach (Android.Views.View view in viewDictionary.Keys)
                {
                    // Get the view rectangle
                    try
                    {
                        view.GetLocationOnScreen(twoIntArray);
                    }
                    catch // System.ObjectDisposedException: Cannot access a disposed object.
                    {
                        continue;
                    }
                    Rectangle viewRect = new Rectangle(twoIntArray[0], twoIntArray[1], view.Width, view.Height);
    
                    if (viewRect.Contains(pointerLocation))
                    {
                        touchEffectHit = viewDictionary[view];
                    }
                }
    
                if (touchEffectHit != idToEffectDictionary[id])
                {
                    if (idToEffectDictionary[id] != null)
                    {
                        FireEvent(idToEffectDictionary[id], id, TouchActionType.Exited, pointerLocation, true);
                    }
                    if (touchEffectHit != null)
                    {
                        FireEvent(touchEffectHit, id, TouchActionType.Entered, pointerLocation, true);
                    }
                    idToEffectDictionary[id] = touchEffectHit;
                }
            }
    
            void FireEvent(TouchEffect touchEffect, int id, TouchActionType actionType, Point pointerLocation, bool isInContact)
            {
                // Get the method to call for firing events
                Action<Element, TouchActionEventArgs> onTouchAction = touchEffect.libTouchEffect.OnTouchAction;
    
                // Get the location of the pointer within the view
                touchEffect.view.GetLocationOnScreen(twoIntArray);
                double x = pointerLocation.X - twoIntArray[0];
                double y = pointerLocation.Y - twoIntArray[1];
                Point point = new Point(fromPixels(x), fromPixels(y));
    
                // Call the method
                onTouchAction(touchEffect.formsElement,
                    new TouchActionEventArgs(id, actionType, point, isInContact));
            }
        }
    }

    然后,在ios的项目里面,加入这个类

    using System;
    using System.Linq;
    
    using Xamarin.Forms;
    using Xamarin.Forms.Platform.iOS;
    
    using System.Collections.Generic;
    
    using CoreGraphics;
    using Foundation;
    using UIKit;
    
    [assembly: ResolutionGroupName("XamarinDocs")]
    [assembly: ExportEffect(typeof(TouchTracking.iOS.TouchEffect), "TouchEffect")]
    
    namespace TouchTracking.iOS
    {
        public class TouchEffect : PlatformEffect
        {
            UIView view;
            TouchRecognizer touchRecognizer;
    
            protected override void OnAttached()
            {
                // Get the iOS UIView corresponding to the Element that the effect is attached to
                view = Control == null ? Container : Control;
    
                // Get access to the TouchEffect class in the .NET Standard library
                TouchTracking.TouchEffect effect = (TouchTracking.TouchEffect)Element.Effects.FirstOrDefault(e => e is TouchTracking.TouchEffect);
    
                if (effect != null && view != null)
                {
                    // Create a TouchRecognizer for this UIView
                    touchRecognizer = new TouchRecognizer(Element, view, effect); 
                    view.AddGestureRecognizer(touchRecognizer);
                }
            }
    
            protected override void OnDetached()
            {
                if (touchRecognizer != null)
                {
                    // Clean up the TouchRecognizer object
                    touchRecognizer.Detach();
    
                    // Remove the TouchRecognizer from the UIView
                    view.RemoveGestureRecognizer(touchRecognizer);
                }
            }
        }
    
        class TouchRecognizer : UIGestureRecognizer
        {
            Element element;        // Forms element for firing events
            UIView view;            // iOS UIView 
            TouchTracking.TouchEffect touchEffect;
            bool capture;
    
            static Dictionary<UIView, TouchRecognizer> viewDictionary =
                new Dictionary<UIView, TouchRecognizer>();
    
            static Dictionary<long, TouchRecognizer> idToTouchDictionary =
                new Dictionary<long, TouchRecognizer>();
    
            public TouchRecognizer(Element element, UIView view, TouchTracking.TouchEffect touchEffect)
            {
                this.element = element;
                this.view = view;
                this.touchEffect = touchEffect;
    
                viewDictionary.Add(view, this);
            }
    
            public void Detach()
            {
                viewDictionary.Remove(view);
            }
    
            // touches = touches of interest; evt = all touches of type UITouch
            public override void TouchesBegan(NSSet touches, UIEvent evt)
            {
                base.TouchesBegan(touches, evt);
    
                foreach (UITouch touch in touches.Cast<UITouch>())
                {
                    long id = touch.Handle.ToInt64();
                    FireEvent(this, id, TouchActionType.Pressed, touch, true);
    
                    if (!idToTouchDictionary.ContainsKey(id))
                    {
                        idToTouchDictionary.Add(id, this);
                    }
                }
    
                // Save the setting of the Capture property
                capture = touchEffect.Capture;
            }
    
            public override void TouchesMoved(NSSet touches, UIEvent evt)
            {
                base.TouchesMoved(touches, evt);
    
                foreach (UITouch touch in touches.Cast<UITouch>())
                {
                    long id = touch.Handle.ToInt64();
    
                    if (capture)
                    {
                        FireEvent(this, id, TouchActionType.Moved, touch, true);
                    }
                    else
                    {
                        CheckForBoundaryHop(touch);
    
                        if (idToTouchDictionary[id] != null)
                        {
                            FireEvent(idToTouchDictionary[id], id, TouchActionType.Moved, touch, true);
                        }
                    }
                }
            }
    
            public override void TouchesEnded(NSSet touches, UIEvent evt)
            {
                base.TouchesEnded(touches, evt);
    
                foreach (UITouch touch in touches.Cast<UITouch>())
                {
                    long id = touch.Handle.ToInt64();
    
                    if (capture)
                    {
                        FireEvent(this, id, TouchActionType.Released, touch, false);
                    }
                    else
                    {
                        CheckForBoundaryHop(touch);
    
                        if (idToTouchDictionary[id] != null)
                        {
                            FireEvent(idToTouchDictionary[id], id, TouchActionType.Released, touch, false);
                        }
                    }
                    idToTouchDictionary.Remove(id);
                }
            }
    
            public override void TouchesCancelled(NSSet touches, UIEvent evt)
            {
                base.TouchesCancelled(touches, evt);
    
                foreach (UITouch touch in touches.Cast<UITouch>())
                {
                    long id = touch.Handle.ToInt64();
    
                    if (capture)
                    {
                        FireEvent(this, id, TouchActionType.Cancelled, touch, false);
                    }
                    else if (idToTouchDictionary[id] != null)
                    {
                        FireEvent(idToTouchDictionary[id], id, TouchActionType.Cancelled, touch, false);
                    }
                    idToTouchDictionary.Remove(id);
                }
            }
    
            void CheckForBoundaryHop(UITouch touch)
            {
                long id = touch.Handle.ToInt64();
    
                // TODO: Might require converting to a List for multiple hits
                TouchRecognizer recognizerHit = null;
    
                foreach (UIView view in viewDictionary.Keys)
                {
                    CGPoint location = touch.LocationInView(view);
    
                    if (new CGRect(new CGPoint(), view.Frame.Size).Contains(location))
                    {
                        recognizerHit = viewDictionary[view];
                    }
                }
                if (recognizerHit != idToTouchDictionary[id])
                {
                    if (idToTouchDictionary[id] != null)
                    {
                        FireEvent(idToTouchDictionary[id], id, TouchActionType.Exited, touch, true);
                    }
                    if (recognizerHit != null)
                    {
                        FireEvent(recognizerHit, id, TouchActionType.Entered, touch, true);
                    }
                    idToTouchDictionary[id] = recognizerHit;
                }
            }
    
            void FireEvent(TouchRecognizer recognizer, long id, TouchActionType actionType, UITouch touch, bool isInContact)
            {
                // Convert touch location to Xamarin.Forms Point value
                CGPoint cgPoint = touch.LocationInView(recognizer.View);
                Point xfPoint = new Point(cgPoint.X, cgPoint.Y);
    
                // Get the method to call for firing events
                Action<Element, TouchActionEventArgs> onTouchAction = recognizer.touchEffect.OnTouchAction;
    
                // Call that method
                onTouchAction(recognizer.element,
                    new TouchActionEventArgs(id, actionType, xfPoint, isInContact));
            }
        }
    }

    然后,在UWP的项目里面,加入这个类

    using System;
    using System.Linq;
    using Windows.UI.Input;
    using Windows.UI.Xaml;
    using Windows.UI.Xaml.Input;
    using Xamarin.Forms;
    using Xamarin.Forms.Platform.UWP;
    
    [assembly: ResolutionGroupName("XamarinDocs")]
    [assembly: ExportEffect(typeof(TouchTracking.UWP.TouchEffect), "TouchEffect")]
    
    namespace TouchTracking.UWP
    {
        public class TouchEffect : PlatformEffect
        {
            FrameworkElement frameworkElement;
            TouchTracking.TouchEffect effect;
            Action<Element, TouchActionEventArgs> onTouchAction;
    
            protected override void OnAttached()
            {
                // Get the Windows FrameworkElement corresponding to the Element that the effect is attached to
                frameworkElement = Control == null ? Container : Control;
    
                // Get access to the TouchEffect class in the .NET Standard library
                effect = (TouchTracking.TouchEffect)Element.Effects.
                            FirstOrDefault(e => e is TouchTracking.TouchEffect);
    
                if (effect != null && frameworkElement != null)
                {
                    // Save the method to call on touch events
                    onTouchAction = effect.OnTouchAction;
    
                    // Set event handlers on FrameworkElement
                    frameworkElement.PointerEntered += OnPointerEntered;
                    frameworkElement.PointerPressed += OnPointerPressed;
                    frameworkElement.PointerMoved += OnPointerMoved;
                    frameworkElement.PointerReleased += OnPointerReleased;
                    frameworkElement.PointerExited += OnPointerExited;
                    frameworkElement.PointerCanceled += OnPointerCancelled;
                }
            }
    
            protected override void OnDetached()
            {
                if (onTouchAction != null)
                {
                    // Release event handlers on FrameworkElement
                    frameworkElement.PointerEntered -= OnPointerEntered;
                    frameworkElement.PointerPressed -= OnPointerPressed;
                    frameworkElement.PointerMoved -= OnPointerMoved;
                    frameworkElement.PointerReleased -= OnPointerReleased;
                    frameworkElement.PointerExited -= OnPointerEntered;
                    frameworkElement.PointerCanceled -= OnPointerCancelled;
                }
            }
    
            void OnPointerEntered(object sender, PointerRoutedEventArgs args)
            {
                CommonHandler(sender, TouchActionType.Entered, args);
            }
    
            void OnPointerPressed(object sender, PointerRoutedEventArgs args)
            {
                CommonHandler(sender, TouchActionType.Pressed, args);
    
                // Check setting of Capture property
                if (effect.Capture)
                {
                    (sender as FrameworkElement).CapturePointer(args.Pointer);
                }
            }
    
            void OnPointerMoved(object sender, PointerRoutedEventArgs args)
            {
                CommonHandler(sender, TouchActionType.Moved, args);
            }
    
            void OnPointerReleased(object sender, PointerRoutedEventArgs args)
            {
                CommonHandler(sender, TouchActionType.Released, args);
            }
    
            void OnPointerExited(object sender, PointerRoutedEventArgs args)
            {
                CommonHandler(sender, TouchActionType.Exited, args);
            }
    
            void OnPointerCancelled(object sender, PointerRoutedEventArgs args)
            {
                CommonHandler(sender, TouchActionType.Cancelled, args);
            }
    
            void CommonHandler(object sender, TouchActionType touchActionType, PointerRoutedEventArgs args)
            {
                PointerPoint pointerPoint = args.GetCurrentPoint(sender as UIElement);
                Windows.Foundation.Point windowsPoint = pointerPoint.Position;  
    
                onTouchAction(Element, new TouchActionEventArgs(args.Pointer.PointerId,
                                                                touchActionType,
                                                                new Point(windowsPoint.X, windowsPoint.Y),
                                                                args.Pointer.IsInContact));
            }
        }
    }

    这样,自定义的touch事件就完成了

    在共享项目的xaml里面,类似这样即可使用:

    <?xml version="1.0" encoding="utf-8" ?>
    <ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
                 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                 xmlns:local="clr-namespace:App1"
                  xmlns:tt="clr-namespace:TouchTracking"
                 x:Class="App1.MainPage">
    
        <Grid x:Name="p1" BackgroundColor="Aquamarine">
            <Grid.Effects>
                <tt:TouchEffect Capture="True" TouchAction="OnTouchEffectAction" />
            </Grid.Effects>
           
        </Grid>
    
    </ContentPage>
  • 相关阅读:
    转:单链表有环判断及其起始位置定位
    转:C++经典排序算法总结
    转:堆排序
    转载:C++快速排序
    转载:平衡二叉树(AVL Tree)
    设计模式原则
    适配器模式
    单例模式
    工厂模式
    Head First设计模式 装饰者模式
  • 原文地址:https://www.cnblogs.com/IWings/p/9300172.html
Copyright © 2020-2023  润新知