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>

Xamarin.Forms实现touch事件的更多相关文章

  1. 菜鸟的Xamarin.Forms前行之路——按钮的按下抬起事件的监控(可扩展至其他事件)

    提问:监控按钮的点击事件,可以通过按钮的Click事件,或者Command绑定,那么如何监控按钮的按下与抬起,或者移动,长按,双击等事件? 解决方法:各个平台自定义渲染依赖注入. 共享项目PCL: 1 ...

  2. Xamarin.Forms中的ListView的ItemTrapped事件与ItemSelected事件的区别

    今天对Xamarin.Forms中的ListView的两个事件(ItemTrapped和ItemSelected)做了小小的研究,发现有以下几点区别: 1.ItemTrapped事件会优先被触发. 2 ...

  3. Xamarin.Forms介绍

    On May 28, 2014, Xamarin introduced Xamarin.Forms, which allows you to write user-interface code tha ...

  4. Xamarin.Forms 开发资源集合(复制)

    复制:https://www.cnblogs.com/mschen/p/10199997.html 收集整理了下 Xamarin.Forms 的学习参考资料,分享给大家,稍后会不断补充: UI样式 S ...

  5. Xamarin.Forms 开发资源集合

    收集整理了下 Xamarin.Forms 的学习参考资料,分享给大家,稍后会不断补充: UI样式 Snppts: Xamarin Forms UI Snippets. Prebuilt Templat ...

  6. Xamarin.Forms 简介

    An Introduction to Xamarin.Forms 来源:http://developer.xamarin.com/guides/cross-platform/xamarin-forms ...

  7. Xamarin.Forms入门-使用 Xamarin.Forms 来创建跨平台的用户界面

    Xamarin.Forms 是一个跨平台的.基于原生控件的UI工具包,开发人员可以轻松的创建适用于 Android,iOS 以及 Windows Phone的用户界面.Xamarin.Forms 通过 ...

  8. 演练:使用Xamarin.Forms开发产品介绍性质的应用(VB版)

    概述 Xamarin这个使用mono和.net core的跨平台开发框架这几年在不断发展.被微软收购后的Xamarin为个人开发者提供了免费版的Xamarin for Visual Studio,吸引 ...

  9. Xamarin.Forms——WebView技术研究

    在Xamarin中有一些Forms原生不太好实现的内容可以考虑使用HTML.Javascript.CSS那一套前端技术来实现,使用WebView来承载显示本地或网络上的HTML文件.不像OpenUri ...

随机推荐

  1. 阿里云nginx创建多站点

    最近开始用阿里云的vps,用了它的一键安装包安装了php环境,nginx的.下面记录创建多站点的心得. 首先php安装好后会自带安装一个phpwind的站点. 文件目录存放在 /alidata/www ...

  2. Linux下Shell编程

    Linux的shell编程 1.什么是shell? 当一个用户登录Linux系统之后,系统初始化程序init就为每个用户执行一个称为shell(外壳)的程序. shell就是一个命令行解释器,它为用户 ...

  3. iOS中的成员变量,实例变量,属性变量

    在ios第一版中: 我们为输出口同时声明了属性和底层实例变量,那时,属性是oc语言的一个新的机制,并且要求你必须声明与之对应的实例变量,例如: 注意:(这个是以前的用法) @interface MyV ...

  4. 【转】Android 关闭多个视图Intent.FLAG_ACTIVITY_CLEAR_TOP用法

    如果已经启动了四个Activity:A,B,C和D.在D Activity里,我们要跳到B Activity,同时希望C finish掉, 可以在startActivity(intent)里的inte ...

  5. go语言笔记——append是内置的函数!!!new是一个函数!!!调试可以使用闭包,本质上是print调试,尼玛!

    内置函数 Go 语言拥有一些不需要进行导入操作就可以使用的内置函数.它们有时可以针对不同的类型进行操作,例如:len.cap 和 append,或必须用于系统级的操作,例如:panic.因此,它们需要 ...

  6. [Supervisor]supervisor监管gunicorn启动DjangoWeb时异常退出

    一开始配置 [program:django_web] command=gunicorn -w 4 -b 0.0.0.0:8080 superadmin.wsgi:application directo ...

  7. bzoj 2044 三维导弹拦截 —— 最小路径覆盖

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2044 第一问暴力 n^2 即可: 注意这道题对位置没要求!所以先按第一维排序一下即可: 然后 ...

  8. Python机器学习算法 — KNN分类

    KNN简介 K最近邻(k-Nearest Neighbor,KNN)分类算法,是一个理论上比较成熟的方法,也是最简单的机器学习算法之一.KNN分类算法属于监督学习. 最简单最初级的分类器是将全部的训练 ...

  9. js判断ie6的代码

    var isIE=!!window.ActiveXObject; var isIE6=isIE&&!window.XMLHttpRequest; var isIE8=isIE& ...

  10. P3379最近公共祖先(LCA)

    题目描述 如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先. 输入输出格式 输入格式: 第一行包含三个正整数N.M.S,分别表示树的结点个数.询问的个数和树根结点的序号. 接下来N-1行每 ...