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; } = ; public SKColor Color { set; get; } = new SKColor(, , , ); 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, ) + Math.Pow(pt.Y - Center.Y, )) < (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[]; 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[] + motionEvent.GetX(pointerIndex),
twoIntArray[] + 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 = ; pointerIndex < motionEvent.PointerCount; pointerIndex++)
{
id = motionEvent.GetPointerId(pointerIndex); if (capture)
{
senderView.GetLocationOnScreen(twoIntArray); screenPointerCoords = new Point(twoIntArray[] + motionEvent.GetX(pointerIndex),
twoIntArray[] + 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[], twoIntArray[], 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[];
double y = pointerLocation.Y - twoIntArray[];
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>

最新文章

  1. spring+mybatis+oracle/mysql整合开发需要的jar包详解
  2. 基于ZigBee的家居控制系统的设计与应用
  3. Java基础(9):Java生成随机数一定范围内的数的一个典型例子
  4. linux内核-双向链表
  5. TCP/IP 子网掩码浅析
  6. C# 解决DrawImage绘制图片拉伸产生渐变
  7. pssh,pscp,pslurp使用实践
  8. Oracle Instanc Client安装命令工具
  9. 201521123068 《java程序设计》 第10周学习总结
  10. [洛谷]P3729 曼哈顿计划EX(最小割树/等价流树)
  11. scala_2
  12. winform checkedlistbox 设置行颜色
  13. Goland的使用
  14. IntelliJ IDEA使用心得
  15. SWIFT中函数返回值为Tuple
  16. linux把程序添加到全局环境变量
  17. TVD$XTAT在linux下安装使用详解
  18. sgu 103 Traffic Lights 解题报告及测试数据
  19. __getitem__
  20. JFinal自定义FreeMarker标签

热门文章

  1. svn: 命令行上传多个指定文件
  2. 阿牛的EOF牛肉串-记忆化搜索或动态规划
  3. UDEV SCSI Rules Configuration for ASM in Oracle Linux 5 and 6
  4. NIO框架之MINA源代码解析(二):mina核心引擎
  5. 基于TCP/UDP的socket编程
  6. School Personal Contest #1 (Codeforces Beta Round #38)---A. Army
  7. How to: Set Properties of Web Application Projects
  8. 【SWUST626】分数分解
  9. IDEA 中Spark SQL通过JDBC连接mysql数据库
  10. 43. ExtJs控件属性配置详细