Vyvolání událostí z efektů
Efekt může definovat a vyvolat událost a signalizovat změny v podkladovém nativním zobrazení. Tento článek ukazuje, jak implementovat sledování vícedotykového prstu nízké úrovně a jak generovat události, které signalizují aktivitu dotykového ovládání.
Efekt popsaný v tomto článku poskytuje přístup k událostem dotykového ovládání nízké úrovně. Tyto události nízké úrovně nejsou dostupné prostřednictvím existujících GestureRecognizer
tříd, ale jsou nezbytné pro některé typy aplikací. Například aplikace malování prstem musí sledovat jednotlivé prsty při pohybu na obrazovce. Hudební klávesnice potřebuje rozpoznat klepnutí a uvolnění jednotlivých kláves a také pohyb prstem z jedné klávesy do druhé v glissando.
Efekt je ideální pro sledování prstu s více dotyky, protože může být připojen k libovolnému Xamarin.Forms prvku.
Události dotykového ovládání platformy
Všechny systémy iOS, Android a Univerzální platforma Windows obsahují rozhraní API nízké úrovně, které umožňuje aplikacím detekovat dotykovou aktivitu. Všechny tyto platformy rozlišují mezi třemi základními typy dotykových událostí:
- Stisknuto, když se prst dotkne obrazovky
- Přesunuto, když se prstem dotknete obrazovky
- Vydáno, když je prst uvolněn z obrazovky
Ve vícedotykovém prostředí se několik prstů může současně dotýkat obrazovky. Mezi různé platformy patří identifikační číslo (ID), které můžou aplikace použít k rozlišení mezi více prsty.
V iOSu UIView
třída definuje tři přepisovatelné metody , TouchesBegan
TouchesMoved
a TouchesEnded
odpovídající těmto třem základním událostem. Článek Sledování vícedotykových prstů popisuje, jak tyto metody používat. Program pro iOS však nemusí přepsat třídu, která je odvozena od UIView
použití těchto metod. IOS UIGestureRecognizer
také definuje stejné tři metody a můžete připojit instanci třídy, která je odvozena od UIGestureRecognizer
jakéhokoli UIView
objektu.
V Androidu View
třída definuje přepisovatelnou metodu pojmenovanou OnTouchEvent
ke zpracování všech aktivit dotykového ovládání. Typ dotykové aktivity je definován pomocí členů Down
výčtu , PointerDown
, Move
, Up
a PointerUp
jak je popsáno v článku Sledování vícedotykového prstu. Android View
také definuje událost s názvem Touch
, která umožňuje, aby obslužná rutina události byla připojena k libovolnému View
objektu.
V Univerzální platforma Windows (UPW) UIElement
třída definuje události s názvem PointerPressed
, PointerMoved
a PointerReleased
. Jsou popsány v článku Zpracování vstupu ukazatele na WEBU MSDN a dokumentaci k rozhraní API pro UIElement
třídu.
Rozhraní Pointer
API v Univerzální platforma Windows je určeno ke sjednocení vstupu myši, dotykového ovládání a pera. Z tohoto důvodu je událost vyvolána, PointerMoved
když se myš pohybuje přes prvek, i když tlačítko myši není stisknuto. Objekt PointerRoutedEventArgs
, který doprovází tyto události, má vlastnost s názvem Pointer
, která má vlastnost s názvem IsInContact
, která označuje, zda je tlačítko myši stisknuto nebo prst je v kontaktu s obrazovkou.
Kromě toho UPW definuje dvě další události s názvem PointerEntered
a PointerExited
. Označují, kdy se myš nebo prst přesune z jednoho prvku do druhého. Představte si například dva sousední prvky s názvem A a B. Oba prvky mají nainstalované obslužné rutiny pro události ukazatele. Když prst stiskne na A, PointerPressed
vyvolá se událost. Při pohybu prstem vyvolá PointerMoved
A události. Pokud se prst přesune z A na B, vyvolá PointerExited
událost a B vyvolá PointerEntered
událost. Pokud se pak prst uvolní, vyvolá PointerReleased
B událost.
Platformy pro iOS a Android se liší od UPW: Zobrazení, na které se poprvé zavolá TouchesBegan
, nebo OnTouchEvent
když se prst do zobrazení dotkne, bude i nadále dostávat všechny dotykové aktivity, i když se prst přesune do různých zobrazení. UPW se může chovat podobně, pokud aplikace zaznamená ukazatel: V PointerEntered
obslužné rutině události volá CapturePointer
prvek a pak získá veškerou dotykovou aktivitu z prstu.
Přístup pro UPW ukazuje, že je velmi užitečný pro některé typy aplikací, jako je například hudební klávesnice. Každá klávesa dokáže zpracovat události dotykového ovládání pro danou klávesu a zjistit, kdy se prst vysune z jednoho klíče do druhého pomocí PointerEntered
událostí a PointerExited
událostí.
Z tohoto důvodu efekt dotykového sledování popsaný v tomto článku implementuje přístup UPW.
Rozhraní API efektu dotykového sledování
Ukázka obsahuje třídy (a výčet), které implementují sledování dotykového ovládání nízké úrovně. Tyto typy patří do oboru názvů TouchTracking
a začínají slovem Touch
. Projekt knihovny TouchTrackingEffectDemos .NET Standard obsahuje TouchActionType
výčet pro typ dotykových událostí:
public enum TouchActionType
{
Entered,
Pressed,
Moved,
Released,
Exited,
Cancelled
}
Všechny platformy také obsahují událost, která značí, že byla událost dotykového ovládání zrušena.
Třída TouchEffect
v knihovně .NET Standard je odvozena od RoutingEffect
a definuje událost pojmenovanou TouchAction
a metodu, OnTouchAction
která vyvolá TouchAction
událost:
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);
}
}
Všimněte si Capture
také vlastnosti. Chcete-li zachytit dotykové události, musí aplikace nastavit tuto vlastnost před true
událostí Pressed
. Jinak se dotykové události chovají jako události v Univerzální platforma Windows.
Třída TouchActionEventArgs
v knihovně .NET Standard obsahuje všechny informace, které doprovází každou událost:
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; }
}
Aplikace může vlastnost použít Id
ke sledování jednotlivých prstů. IsInContact
Všimněte si vlastnosti. Tato vlastnost je vždy true
určena pro Pressed
události a false
události Released
. Je to také vždy true
pro události v Moved
iOSu a Androidu. Vlastnost IsInContact
může být false
pro Moved
události na Univerzální platforma Windows, když program běží na ploše a ukazatel myši se přesune bez stisknutí tlačítka.
Třídu můžete použít TouchEffect
ve vlastních aplikacích zahrnutím souboru do projektu knihovny .NET Standard řešení a přidáním instance do Effects
kolekce libovolného Xamarin.Forms prvku. Připojte k události obslužnou rutinu TouchAction
pro získání dotykových událostí.
K použití TouchEffect
ve vlastní aplikaci budete potřebovat také implementace platformy, které jsou součástí řešení TouchTrackingEffectDemos .
Implementace efektu dotykového sledování
Níže popsané implementace TouchEffect
pro iOS, Android a UPW začínají nejjednodušší implementací (UPW) a končí implementací iOS, protože je strukturálně složitější než ostatní.
Implementace UPW
Implementace UPW TouchEffect
je nejjednodušší. Třída je odvozena od PlatformEffect
třídy a obsahuje dva atributy sestavení:
[assembly: ResolutionGroupName("XamarinDocs")]
[assembly: ExportEffect(typeof(TouchTracking.UWP.TouchEffect), "TouchEffect")]
namespace TouchTracking.UWP
{
public class TouchEffect : PlatformEffect
{
...
}
}
Přepsání OnAttached
uloží některé informace jako pole a připojí obslužné rutiny ke všem událostem ukazatele:
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;
}
}
...
}
Obslužná rutina OnPointerPressed
vyvolá událost efektu voláním onTouchAction
pole v CommonHandler
metodě:
public class TouchEffect : PlatformEffect
{
...
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 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));
}
}
OnPointerPressed
také zkontroluje hodnotu Capture
vlastnosti ve třídě efektu v knihovně .NET Standard a volá CapturePointer
, pokud je true
.
Další obslužné rutiny událostí UPW jsou ještě jednodušší:
public class TouchEffect : PlatformEffect
{
...
void OnPointerEntered(object sender, PointerRoutedEventArgs args)
{
CommonHandler(sender, TouchActionType.Entered, args);
}
...
}
Implementace Androidu
Implementace Androidu a iOSu jsou nutně složitější, protože musí implementovat a Entered
události, Exited
když se prst přesune z jednoho prvku do druhého. Obě implementace jsou strukturovány podobně.
Třída Androidu TouchEffect
nainstaluje obslužnou rutinu Touch
události:
view = Control == null ? Container : Control;
...
view.Touch += OnTouch;
Třída také definuje dva statické slovníky:
public class TouchEffect : PlatformEffect
{
...
static Dictionary<Android.Views.View, TouchEffect> viewDictionary =
new Dictionary<Android.Views.View, TouchEffect>();
static Dictionary<int, TouchEffect> idToEffectDictionary =
new Dictionary<int, TouchEffect>();
...
Získá viewDictionary
novou položku při OnAttached
každém zavolání přepsání:
viewDictionary.Add(view, this);
Položka je odebrána ze slovníku v .OnDetached
Každá instance TouchEffect
je přidružena k určitému zobrazení, ke kterému je efekt připojen. Statický slovník umožňuje, aby všechny TouchEffect
instance vyčíslily všechny ostatní zobrazení a jejich odpovídající TouchEffect
instance. To je nezbytné, aby bylo možné přenášet události z jednoho zobrazení do druhého.
Android přiřadí kód ID k dotykovým událostem, které aplikaci umožňují sledovat jednotlivé prsty. Tento idToEffectDictionary
kód ID přidruží k TouchEffect
instanci. Položka se přidá do tohoto slovníku, když Touch
je obslužná rutina volána pro stisknutí prstu:
void OnTouch(object sender, Android.Views.View.TouchEventArgs args)
{
...
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;
Položka se odebere z idToEffectDictionary
po uvolnění prstu z obrazovky. Metoda FireEvent
jednoduše shromažďuje všechny informace potřebné k volání OnTouchAction
metody:
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));
}
Všechny ostatní typy dotykového ovládání se zpracovávají dvěma různými způsoby: Pokud Capture
je true
vlastnost , dotyková událost je poměrně jednoduchý překlad na TouchEffect
informace. Je to složitější, když Capture
je to proto false
, že dotykové události může být potřeba přesunout z jednoho zobrazení do jiného. Je to zodpovědnost za metodu CheckForBoundaryHop
, která se volá během událostí přesunutí. Tato metoda využívá oba statické slovníky. Vytvoří výčet viewDictionary
pro určení zobrazení, které se právě dotkne prstu, a používá idToEffectDictionary
se k uložení aktuální TouchEffect
instance (a tedy aktuálního zobrazení) přidruženého k určitému ID:
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;
}
}
Pokud došlo ke změně idToEffectDictionary
, metoda potenciálně volá FireEvent
Exited
a Entered
přepojuje z jednoho zobrazení do jiného. Prst se však mohl přesunout do oblasti, která je obsazena zobrazením bez připojené TouchEffect
oblasti nebo z této oblasti do zobrazení s připojeným efektem.
try
Všimněte si a catch
zablokujte zobrazení při přístupu k zobrazení. Na stránce, na které přejdete a pak přejde zpět na domovskou stránku, OnDetached
metoda není volána a položky zůstanou v systému viewDictionary
, ale Android je považuje za uvolněné.
Implementace iOS
Implementace iOS je podobná implementaci Android s tím rozdílem, že třída iOS TouchEffect
musí vytvořit instanci derivátu UIGestureRecognizer
. Toto je třída v projektu iOS s názvem TouchRecognizer
. Tato třída udržuje dva statické slovníky, které ukládají TouchRecognizer
instance:
static Dictionary<UIView, TouchRecognizer> viewDictionary =
new Dictionary<UIView, TouchRecognizer>();
static Dictionary<long, TouchRecognizer> idToTouchDictionary =
new Dictionary<long, TouchRecognizer>();
Většina struktury této TouchRecognizer
třídy je podobná třídě Android TouchEffect
.
Důležité
Mnoho zobrazení ve UIKit
výchozím nastavení nemá povolené dotykové ovládání. Dotykové ovládání je možné povolit přidáním view.UserInteractionEnabled = true;
přepsání OnAttached
ve TouchEffect
třídě v projektu iOS. K tomu by mělo dojít po UIView
získání, který odpovídá prvku, ke kterému je efekt připojen.
Umístění dotykového efektu do práce
Ukázkový program obsahuje pět stránek, které testuje efekt dotykového sledování pro běžné úlohy.
Stránka BoxView Draging umožňuje přidat BoxView
prvky do a AbsoluteLayout
potom je přetáhnout po obrazovce. Soubor XAML vytvoří instanci dvou Button
zobrazení pro přidání BoxView
prvků do AbsoluteLayout
a vymazání AbsoluteLayout
souboru .
Metoda v souboru kódu, který přidá nový BoxView
do objektu AbsoluteLayout
také TouchEffect
do BoxView
obslužné rutiny události k efektu:
void AddBoxViewToLayout()
{
BoxView boxView = new BoxView
{
WidthRequest = 100,
HeightRequest = 100,
Color = new Color(random.NextDouble(),
random.NextDouble(),
random.NextDouble())
};
TouchEffect touchEffect = new TouchEffect();
touchEffect.TouchAction += OnTouchEffectAction;
boxView.Effects.Add(touchEffect);
absoluteLayout.Children.Add(boxView);
}
Obslužná rutina TouchAction
události zpracovává všechny dotykové události pro všechny BoxView
prvky, ale musí postupovat opatrně: Nemůže povolit dva prsty na jednom BoxView
, protože program implementuje pouze přetahování a dva prsty by navzájem kolidovaly. Z tohoto důvodu stránka definuje vloženou třídu pro každý aktuálně sledovaný prst:
class DragInfo
{
public DragInfo(long id, Point pressPoint)
{
Id = id;
PressPoint = pressPoint;
}
public long Id { private set; get; }
public Point PressPoint { private set; get; }
}
Dictionary<BoxView, DragInfo> dragDictionary = new Dictionary<BoxView, DragInfo>();
Obsahuje dragDictionary
položku pro každou BoxView
aktuálně přetahovanou položku.
Akce Pressed
dotykového ovládání přidá položku do tohoto slovníku a Released
akce ji odebere. Logika Pressed
musí zkontrolovat, jestli už ve slovníku BoxView
existuje položka . Pokud ano, je BoxView
již přetažen a nová událost je druhý prst na stejné BoxView
. U obslužné Released
Moved
rutiny události musí zkontrolovat, jestli slovník obsahuje položku pro tuto BoxView
položku a že dotyková Id
vlastnost pro přetaženou BoxView
položku odpovídá vlastnosti, která je v položce slovníku:
void OnTouchEffectAction(object sender, TouchActionEventArgs args)
{
BoxView boxView = sender as BoxView;
switch (args.Type)
{
case TouchActionType.Pressed:
// Don't allow a second touch on an already touched BoxView
if (!dragDictionary.ContainsKey(boxView))
{
dragDictionary.Add(boxView, new DragInfo(args.Id, args.Location));
// Set Capture property to true
TouchEffect touchEffect = (TouchEffect)boxView.Effects.FirstOrDefault(e => e is TouchEffect);
touchEffect.Capture = true;
}
break;
case TouchActionType.Moved:
if (dragDictionary.ContainsKey(boxView) && dragDictionary[boxView].Id == args.Id)
{
Rectangle rect = AbsoluteLayout.GetLayoutBounds(boxView);
Point initialLocation = dragDictionary[boxView].PressPoint;
rect.X += args.Location.X - initialLocation.X;
rect.Y += args.Location.Y - initialLocation.Y;
AbsoluteLayout.SetLayoutBounds(boxView, rect);
}
break;
case TouchActionType.Released:
if (dragDictionary.ContainsKey(boxView) && dragDictionary[boxView].Id == args.Id)
{
dragDictionary.Remove(boxView);
}
break;
}
}
Logika Pressed
nastaví Capture
vlastnost objektu TouchEffect
na true
. To má vliv na doručení všech následných událostí pro daný prst do stejné obslužné rutiny události.
Logika Moved
přesune BoxView
vlastnost změnou LayoutBounds
připojené vlastnosti. Location
Vlastnost argumentů události je vždy relativní vzhledem k BoxView
přetahování a pokud BoxView
je přetažena konstantní rychlostí, Location
vlastnosti po sobě jdoucích událostí budou přibližně stejné. Pokud například prst stiskne BoxView
v jeho středu, Pressed
akce uloží PressPoint
vlastnost (50, 50), která zůstane stejná pro následné události. BoxView
Pokud je přetažen diagonálně konstantní rychlostí, následné Location
vlastnosti během Moved
akce mohou být hodnoty (55, 55), v takovém případě Moved
logika přidá 5 na vodorovnou a svislou pozici BoxView
. Přesune BoxView
se tak, aby jeho střed byl opět přímo pod prstem.
Více prvků můžete současně přesouvat BoxView
různými prsty.
Podtřídy zobrazení
Často je pro prvek jednodušší Xamarin.Forms zpracovat vlastní dotykové události. Dragable BoxView Draging page funguje stejně jako BoxView Draging page, ale prvky, které uživatel přetahuje, jsou instance DraggableBoxView
třídy, která je odvozena z BoxView
:
class DraggableBoxView : BoxView
{
bool isBeingDragged;
long touchId;
Point pressPoint;
public DraggableBoxView()
{
TouchEffect touchEffect = new TouchEffect
{
Capture = true
};
touchEffect.TouchAction += OnTouchEffectAction;
Effects.Add(touchEffect);
}
void OnTouchEffectAction(object sender, TouchActionEventArgs args)
{
switch (args.Type)
{
case TouchActionType.Pressed:
if (!isBeingDragged)
{
isBeingDragged = true;
touchId = args.Id;
pressPoint = args.Location;
}
break;
case TouchActionType.Moved:
if (isBeingDragged && touchId == args.Id)
{
TranslationX += args.Location.X - pressPoint.X;
TranslationY += args.Location.Y - pressPoint.Y;
}
break;
case TouchActionType.Released:
if (isBeingDragged && touchId == args.Id)
{
isBeingDragged = false;
}
break;
}
}
}
Konstruktor vytvoří a připojí a nastaví TouchEffect
Capture
vlastnost při prvním vytvoření instance objektu. Není vyžadován žádný slovník, protože samotná třída ukládá isBeingDragged
, pressPoint
a touchId
hodnoty spojené s každým prstem. Zpracování Moved
změní vlastnosti TranslationX
a TranslationY
vlastnosti tak, aby logika fungovala i v případě, že nadřazený objekt DraggableBoxView
není .AbsoluteLayout
Integrace s SkiaSharpem
Další dvě ukázky vyžadují grafiku a pro tento účel používají SkiaSharp. Než se seznámíte s používáním SkiaSharpuXamarin.Forms, než se seznámíte s těmito příklady. První dva články ("SkiaSharp Drawing Basics" a "SkiaSharp Lines and Paths") pokrývají vše, co zde budete potřebovat.
Stránka Ellipse Drawing (Ellipse Drawing ) umožňuje kreslit tři tečky potažením prstu na obrazovce. V závislosti na tom, jak prst posunete, můžete nakreslit tři tečky z levého horního rohu do pravého dolního nebo jiného rohu do opačného rohu. Tři tečky jsou vykresleny náhodnou barvou a neprůhledností.
Pokud se pak dotknete jednoho ze tří teček, můžete ho přetáhnout na jiné místo. To vyžaduje techniku známou jako "hit-testing", která zahrnuje hledání grafického objektu v určitém okamžiku. Tři tečky SkiaSharp nejsou Xamarin.Forms prvky, takže nemohou provádět vlastní TouchEffect
zpracování. Musí TouchEffect
platit pro celý SKCanvasView
objekt.
Soubor EllipseDrawPage.xaml vytvoří SKCanvasView
instanci v jedné buňce Grid
. Objekt TouchEffect
je připojen k této Grid
:
<Grid x:Name="canvasViewGrid"
Grid.Row="1"
BackgroundColor="White">
<skia:SKCanvasView x:Name="canvasView"
PaintSurface="OnCanvasViewPaintSurface" />
<Grid.Effects>
<tt:TouchEffect Capture="True"
TouchAction="OnTouchEffectAction" />
</Grid.Effects>
</Grid>
V Androidu a Univerzální platforma Windows TouchEffect
je možné ho SKCanvasView
připojit přímo k systému , ale v iOSu, který nefunguje. Všimněte si, že Capture
vlastnost je nastavena na true
hodnotu .
Každé tři tečky, které SkiaSharp vykresluje, je reprezentován objektem typu EllipseDrawingFigure
:
class EllipseDrawingFigure
{
SKPoint pt1, pt2;
public EllipseDrawingFigure()
{
}
public SKColor Color { set; get; }
public SKPoint StartPoint
{
set
{
pt1 = value;
MakeRectangle();
}
}
public SKPoint EndPoint
{
set
{
pt2 = value;
MakeRectangle();
}
}
void MakeRectangle()
{
Rectangle = new SKRect(pt1.X, pt1.Y, pt2.X, pt2.Y).Standardized;
}
public SKRect Rectangle { set; get; }
// For dragging operations
public Point LastFingerLocation { set; get; }
// For the dragging hit-test
public bool IsInEllipse(SKPoint pt)
{
SKRect rect = Rectangle;
return (Math.Pow(pt.X - rect.MidX, 2) / Math.Pow(rect.Width / 2, 2) +
Math.Pow(pt.Y - rect.MidY, 2) / Math.Pow(rect.Height / 2, 2)) < 1;
}
}
Vlastnosti StartPoint
a EndPoint
vlastnosti se používají při zpracování dotykového vstupu programu. Rectangle
Vlastnost se používá pro kreslení tří teček. Vlastnost LastFingerLocation
přichází do hry při přetahování tří teček a IsInEllipse
metoda pomáhá při hit-testování. Metoda vrátí true
, pokud je bod uvnitř tří teček.
Soubor kódu za kódem uchovává tři kolekce:
Dictionary<long, EllipseDrawingFigure> inProgressFigures = new Dictionary<long, EllipseDrawingFigure>();
List<EllipseDrawingFigure> completedFigures = new List<EllipseDrawingFigure>();
Dictionary<long, EllipseDrawingFigure> draggingFigures = new Dictionary<long, EllipseDrawingFigure>();
Slovník draggingFigure
obsahuje podmnožinu completedFigures
kolekce. Obslužná rutina události SkiaSharp PaintSurface
jednoduše vykreslí objekty v těchto completedFigures
kolekcích inProgressFigures
:
SKPaint paint = new SKPaint
{
Style = SKPaintStyle.Fill
};
...
void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
SKCanvas canvas = args.Surface.Canvas;
canvas.Clear();
foreach (EllipseDrawingFigure figure in completedFigures)
{
paint.Color = figure.Color;
canvas.DrawOval(figure.Rectangle, paint);
}
foreach (EllipseDrawingFigure figure in inProgressFigures.Values)
{
paint.Color = figure.Color;
canvas.DrawOval(figure.Rectangle, paint);
}
}
Nejsná část zpracování dotykového ovládání je Pressed
zpracování. Tady se provádí test hit-testing, ale pokud kód zjistí tři tečky pod prstem uživatele, může být toto tři tečky přetaženo pouze v případě, že není aktuálně přetažen jiným prstem. Pokud není pod prstem uživatele žádné tři tečky, začne kód nakreslit nový tři tečky:
case TouchActionType.Pressed:
bool isDragOperation = false;
// Loop through the completed figures
foreach (EllipseDrawingFigure fig in completedFigures.Reverse<EllipseDrawingFigure>())
{
// Check if the finger is touching one of the ellipses
if (fig.IsInEllipse(ConvertToPixel(args.Location)))
{
// Tentatively assume this is a dragging operation
isDragOperation = true;
// Loop through all the figures currently being dragged
foreach (EllipseDrawingFigure draggedFigure in draggingFigures.Values)
{
// If there's a match, we'll need to dig deeper
if (fig == draggedFigure)
{
isDragOperation = false;
break;
}
}
if (isDragOperation)
{
fig.LastFingerLocation = args.Location;
draggingFigures.Add(args.Id, fig);
break;
}
}
}
if (isDragOperation)
{
// Move the dragged ellipse to the end of completedFigures so it's drawn on top
EllipseDrawingFigure fig = draggingFigures[args.Id];
completedFigures.Remove(fig);
completedFigures.Add(fig);
}
else // start making a new ellipse
{
// Random bytes for random color
byte[] buffer = new byte[4];
random.NextBytes(buffer);
EllipseDrawingFigure figure = new EllipseDrawingFigure
{
Color = new SKColor(buffer[0], buffer[1], buffer[2], buffer[3]),
StartPoint = ConvertToPixel(args.Location),
EndPoint = ConvertToPixel(args.Location)
};
inProgressFigures.Add(args.Id, figure);
}
canvasView.InvalidateSurface();
break;
Dalším příkladem SkiaSharp je stránka Prst Malování. Můžete vybrat barvu tahu a šířku tahu ze dvou Picker
zobrazení a pak kreslit jedním nebo více prsty:
Tento příklad také vyžaduje samostatnou třídu, která představuje každou čáru malovanou na obrazovce:
class FingerPaintPolyline
{
public FingerPaintPolyline()
{
Path = new SKPath();
}
public SKPath Path { set; get; }
public Color StrokeColor { set; get; }
public float StrokeWidth { set; get; }
}
Objekt SKPath
se používá k vykreslení každého řádku. Soubor Prst Malování.xaml.cs udržuje dvě kolekce těchto objektů, jednu pro tyto křivky, které jsou právě nakresleny, a druhý pro dokončené čáry:
Dictionary<long, FingerPaintPolyline> inProgressPolylines = new Dictionary<long, FingerPaintPolyline>();
List<FingerPaintPolyline> completedPolylines = new List<FingerPaintPolyline>();
Zpracování Pressed
vytvoří nové FingerPaintPolyline
volání MoveTo
objektu cesty k uložení počátečního bodu a přidá tento objekt do slovníku inProgressPolylines
. Zpracování Moved
volá LineTo
objekt cesty s novou polohou prstu a Released
zpracování přenese dokončenou křivku z inProgressPolylines
do completedPolylines
. Opět je skutečný kód výkresu SkiaSharp relativně jednoduchý:
SKPaint paint = new SKPaint
{
Style = SKPaintStyle.Stroke,
StrokeCap = SKStrokeCap.Round,
StrokeJoin = SKStrokeJoin.Round
};
...
void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
SKCanvas canvas = args.Surface.Canvas;
canvas.Clear();
foreach (FingerPaintPolyline polyline in completedPolylines)
{
paint.Color = polyline.StrokeColor.ToSKColor();
paint.StrokeWidth = polyline.StrokeWidth;
canvas.DrawPath(polyline.Path, paint);
}
foreach (FingerPaintPolyline polyline in inProgressPolylines.Values)
{
paint.Color = polyline.StrokeColor.ToSKColor();
paint.StrokeWidth = polyline.StrokeWidth;
canvas.DrawPath(polyline.Path, paint);
}
}
Dotykové ovládání sledování zobrazení na zobrazení
Všechny předchozí příklady nastavily Capture
vlastnost TouchEffect
na true
hodnotu , buď při TouchEffect
vytvoření, nebo při výskytu Pressed
události. Tím se zajistí, že stejný prvek obdrží všechny události spojené s prstem, které poprvé stiskly zobrazení. Konečný vzorek není nastaven Capture
na true
hodnotu . To způsobí jiné chování, když se prst v kontaktu s obrazovkou přesune z jednoho prvku do druhého. Prvek, ze kterého se prst pohybuje, obdrží událost s vlastností nastavenou Type
TouchActionType.Exited
na a druhý prvek obdrží událost s Type
nastavením TouchActionType.Entered
.
Tento typ dotykového zpracování je velmi užitečný pro hudební klávesnici. Klávesa by měla být schopná rozpoznat, kdy je stisknutá, ale také když se prstem posune z jedné klávesy do druhé.
Stránka Bezobslužná klávesnice definuje malé WhiteKey
a BlackKey
třídy, které jsou odvozeny od Key
BoxView
.
Třída Key
je připravena k použití ve skutečném hudebním programu. Definuje veřejné vlastnosti pojmenované IsPressed
a KeyNumber
, které mají být nastaveny na kód klíče vytvořený standardem MIDI. Třída Key
také definuje událost s názvem StatusChanged
, která je vyvolána při IsPressed
změně vlastnosti.
Na každém klíči je povoleno více prstů. Z tohoto důvodu Key
třída udržuje List
čísla touch ID všech prstů, které se aktuálně dotknou této klávesy:
List<long> ids = new List<long>();
Obslužná rutina TouchAction
události přidá ID do ids
seznamu pro Pressed
typ události i typ Entered
, ale pouze v případě, že IsInContact
je true
vlastnost události Entered
. ID se odebere List
Released
z události nebo Exited
události:
void OnTouchEffectAction(object sender, TouchActionEventArgs args)
{
switch (args.Type)
{
case TouchActionType.Pressed:
AddToList(args.Id);
break;
case TouchActionType.Entered:
if (args.IsInContact)
{
AddToList(args.Id);
}
break;
case TouchActionType.Moved:
break;
case TouchActionType.Released:
case TouchActionType.Exited:
RemoveFromList(args.Id);
break;
}
}
RemoveFromList
Obě AddToList
metody kontrolují, jestli došlo ke List
změně mezi prázdným a neprázdným, a pokud ano, vyvolá StatusChanged
událost.
Různé prvky a BlackKey
prvky WhiteKey
jsou uspořádány do souboru XAML stránky, který vypadá nejlépe, když se telefon nachází v režimu na šířku:
Pokud prst přetáhnete přes klávesy, uvidíte mírné změny barvy, že se události dotykového ovládání přenesou z jednoho klíče do druhého.
Shrnutí
Tento článek ukázal, jak vyvolat události v efektu a jak psát a používat efekt, který implementuje zpracování vícedotykového zpracování nízké úrovně.