Efektlerden Olayları Çağırma
Bir efekt bir olayı tanımlayıp çağırarak temel alınan yerel görünümdeki değişikliklere işaret edebilir. Bu makalede, düşük düzeyli çok dokunmalı parmak izlemenin nasıl uygulanacağı ve dokunma etkinliği sinyali veren olayların nasıl oluşturulacağı gösterilmektedir.
Bu makalede açıklanan etki, düşük düzeyli dokunma olaylarına erişim sağlar. Bu alt düzey olaylar mevcut GestureRecognizer
sınıflar aracılığıyla kullanılamaz, ancak bazı uygulama türleri için çok önemlidir. Örneğin, bir parmak boyası uygulamasının ekranda hareket eden tek tek parmaklarını izlemesi gerekir. Müzik klavyesinin tek tek tuşlardaki dokunmaları ve yayınları algılaması ve glissandoda bir tuştan diğerine kayan bir parmağı algılaması gerekir.
Bir efekt, herhangi bir Xamarin.Forms elemana bağlanabildiği için çok dokunmalı parmak izleme için idealdir.
Platform Dokunma Olayları
iOS, Android ve Evrensel Windows Platformu, uygulamaların dokunma etkinliğini algılamasına olanak tanıyan düşük düzeyli bir API içerir. Bu platformların tümü üç temel dokunma olayı türünü birbirinden ayırır:
- Basıldığında, bir parmak ekrana dokunduğunda
- Taşındı, ekrana dokunan bir parmak hareket ettiğinde
- Serbest bırakıldığında, parmak ekrandan serbest bırakıldığında
Çok dokunmalı bir ortamda, birden çok parmak ekrana aynı anda dokunabilir. Çeşitli platformlar, uygulamaların birden çok parmak arasında ayrım yapmak için kullanabileceği bir kimlik (KIMLIK) numarası içerir.
iOS'ta UIView
sınıfı, bu üç temel olaya karşılık gelen ve TouchesEnded
geçersiz kılınabilir üç yöntem TouchesBegan
TouchesMoved
tanımlar. Multi-Touch Finger Tracking makalesinde bu yöntemlerin nasıl kullanılacağı açıklanmaktadır. Ancak, bir iOS programının bu yöntemleri kullanmak için türetilen UIView
bir sınıfı geçersiz kılması gerekmez. iOS UIGestureRecognizer
aynı üç yöntemi de tanımlar ve öğesinden UIGestureRecognizer
türetilen bir sınıfın örneğini herhangi UIView
bir nesneye ekleyebilirsiniz.
Android'de sınıfı, View
tüm dokunma etkinliğini işlemek için adlı OnTouchEvent
geçersiz kılınabilir bir yöntem tanımlar. Dokunma etkinliğinin türü, Multi-Touch Parmak İzleme makalesinde açıklandığı gibi numaralandırma üyeleri Down
, PointerDown
Move
, , Up
ve PointerUp
tarafından tanımlanır. AndroidView
, bir olay işleyicisinin herhangi View
bir nesneye eklenmesini sağlayan adlı Touch
bir olayı da tanımlar.
Evrensel Windows Platformu (UWP) sınıfında UIElement
sınıfı , PointerMoved
ve PointerReleased
adlı PointerPressed
olayları tanımlar. Bunlar MSDN'deki İşaretçi Girişini İşleme makalesinde ve sınıfın API belgelerinde UIElement
açıklanmıştır.
Pointer
Evrensel Windows Platformu API'sinin amacı fare, dokunma ve kalem girişini birleştirmektir. Bu nedenle, PointerMoved
fare düğmesine basılmadığında bile fare bir öğe üzerinde hareket ettiğinde olay çağrılır. Bu PointerRoutedEventArgs
olaylara Pointer
eşlik eden nesne, bir fare düğmesine basıldığını veya parmağın ekranla temas halinde olduğunu belirten adlı IsInContact
bir özelliğe sahiptir.
Ayrıca UWP, ve PointerExited
adlı PointerEntered
iki olay daha tanımlar. Bunlar, bir farenin veya parmağın bir öğeden diğerine ne zaman hareket ettiğini gösterir. Örneğin, A ve B adlı iki bitişik öğeyi göz önünde bulundurun. her iki öğe de işaretçi olayları için işleyiciler yükledi. Bir parmak A'ya bastığında PointerPressed
olay çağrılır. Parmak hareket ettikçe, A olayları çağırır PointerMoved
. Parmak A'dan B'ye geçerse, A bir PointerExited
olayı çağırır ve B bir PointerEntered
olayı çağırır. Parmak serbest bırakılırsa, B bir PointerReleased
olayı çağırır.
iOS ve Android platformları UWP'den farklıdır: İlk çağrıyı TouchesBegan
alan veya OnTouchEvent
bir parmak dokunduğunda görünüme dokunan görünüm, parmak farklı görünümlere geçse bile tüm dokunma etkinliğini almaya devam eder. Uygulama işaretçiyi yakalarsa UWP benzer şekilde davranabilir: Olay işleyicisinde PointerEntered
öğe çağrılar CapturePointer
ve ardından tüm dokunma etkinliğini bu parmaktan alır.
UWP yaklaşımı, müzik klavyesi gibi bazı uygulama türleri için çok yararlı olduğunu kanıtlar. Her tuş, bu tuş için dokunma olaylarını işleyebilir ve ve PointerExited
olaylarını kullanarak PointerEntered
bir parmağın bir anahtardan diğerine ne zaman kaydırıldığını algılayabilir.
Bu nedenle, bu makalede açıklanan dokunma izleme etkisi UWP yaklaşımını uygular.
Dokunma İzleme Efekti API'si
Örnek, alt düzey dokunma izlemeyi uygulayan sınıfları (ve bir numaralandırmayı) içerir. Bu türler ad alanına TouchTracking
aittir ve sözcüğüyle Touch
başlar. TouchTrackingEffectDemos .NET Standard kitaplık projesi, dokunma olaylarının türüne yönelik numaralandırmayı içerirTouchActionType
:
public enum TouchActionType
{
Entered,
Pressed,
Moved,
Released,
Exited,
Cancelled
}
Tüm platformlar, dokunma olayının iptal edildiğini gösteren bir olay da içerir.
TouchEffect
.NET Standard kitaplığındaki sınıfı, adlı bir olaydan RoutingEffect
TouchAction
türetilir ve olayı çağıran TouchAction
adlı OnTouchAction
bir yöntemi tanımlar:
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);
}
}
Ayrıca özelliğine Capture
de dikkat edin. Dokunma olaylarını yakalamak için uygulamanın bu özelliği bir olaydan önce olarak true
ayarlaması Pressed
gerekir. Aksi takdirde, dokunma olayları Evrensel Windows Platformu gibi davranır.
TouchActionEventArgs
.NET Standard kitaplığındaki sınıfı, her olaya eşlik eden tüm bilgileri içerir:
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; }
}
Bir uygulama, tek tek parmaklarını Id
izlemek için özelliğini kullanabilir. özelliğine IsInContact
dikkat edin. Bu özellik her zaman true
olaylar ve Pressed
false
olaylar içindir Released
. Ayrıca her zaman true
iOS ve Android'de etkinlikler içindir Moved
. Özellik, IsInContact
program masaüstünde çalışırken ve fare işaretçisi düğmeye basılmadan hareket ettiğinde Evrensel Windows Platformu olaylar için Moved
olabilirfalse
.
kendi uygulamalarınızda sınıfınıTouchEffect
, dosyayı çözümün .NET Standard kitaplık projesine ekleyerek ve herhangi Xamarin.Forms bir öğenin koleksiyonuna Effects
bir örnek ekleyerek kullanabilirsiniz. Dokunma olaylarını TouchAction
almak için olaya bir işleyici ekleyin.
Kendi uygulamanızda kullanmak TouchEffect
için, TouchTrackingEffectDemos çözümüne dahil edilen platform uygulamalarına da ihtiyacınız olacaktır.
Dokunma İzleme Efekti Uygulamaları
uygulamasının TouchEffect
iOS, Android ve UWP uygulamaları aşağıda en basit uygulamadan (UWP) başlayıp iOS uygulamasıyla sona erer çünkü diğerlerinden daha yapısal olarak karmaşıktır.
UWP Uygulaması
UWP uygulaması TouchEffect
en basittir. Her zamanki gibi sınıfından türetilir PlatformEffect
ve iki derleme özniteliği içerir:
[assembly: ResolutionGroupName("XamarinDocs")]
[assembly: ExportEffect(typeof(TouchTracking.UWP.TouchEffect), "TouchEffect")]
namespace TouchTracking.UWP
{
public class TouchEffect : PlatformEffect
{
...
}
}
Geçersiz OnAttached
kılma bazı bilgileri alan olarak kaydeder ve tüm işaretçi olaylarına işleyiciler ekler:
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;
}
}
...
}
İşleyici, OnPointerPressed
yöntemindeki alanını CommonHandler
çağırarak onTouchAction
efekt olayını çağırır:
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
ayrıca .NET Standard kitaplığındaki efekt sınıfındaki özelliğinin değerini Capture
denetler ve ise true
çağırırCapturePointer
.
Diğer UWP olay işleyicileri daha da basittir:
public class TouchEffect : PlatformEffect
{
...
void OnPointerEntered(object sender, PointerRoutedEventArgs args)
{
CommonHandler(sender, TouchActionType.Entered, args);
}
...
}
Android Uygulaması
Bir parmak bir öğeden diğerine geçtiğinde ve olaylarını uygulaması gerektiğinden Exited
Android ve Entered
iOS uygulamaları mutlaka daha karmaşıktır. Her iki uygulama da benzer şekilde yapılandırılmıştır.
Android TouchEffect
sınıfı, olay için Touch
bir işleyici yükler:
view = Control == null ? Container : Control;
...
view.Touch += OnTouch;
sınıfı ayrıca iki statik sözlük tanımlar:
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>();
...
geçersiz viewDictionary
kılma her çağrıldığında OnAttached
yeni bir girdi alır:
viewDictionary.Add(view, this);
girdisi içindeki OnDetached
sözlükten kaldırılır. her örneği TouchEffect
, efektin eklendiği belirli bir görünümle ilişkilendirilir. Statik sözlük, tüm TouchEffect
örneklerin diğer tüm görünümleri ve ilgili TouchEffect
örneklerini listelemesine olanak tanır. Bu, olayların bir görünümden diğerine aktarılmasına izin vermek için gereklidir.
Android, bir uygulamanın tek tek parmaklarını izlemesine olanak tanıyan dokunma olaylarına bir kimlik kodu atar. bu idToEffectDictionary
kimlik kodunu bir TouchEffect
örnekle ilişkilendirir. İşleyici parmak basmak için çağrıldığında Touch
bu sözlüğe bir öğe eklenir:
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;
Öğe, parmak ekrandan serbest bırakıldığında öğesinden kaldırılır idToEffectDictionary
. FireEvent
yöntemi yalnızca yöntemini çağırmak için gereken tüm bilgileri biriktirirOnTouchAction
:
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));
}
Diğer tüm dokunma türleri iki farklı şekilde işlenir: özelliği ise Capture
true
, dokunma olayı bilgilere oldukça basit bir çeviridir TouchEffect
. Dokunma olaylarının false
bir görünümden diğerine taşınması gerekebileceği durumlarda Capture
daha karmaşık hale gelir. Bu, taşıma olayları sırasında çağrılan yönteminin sorumluluğudur CheckForBoundaryHop
. Bu yöntem her iki statik sözlükleri de kullanır. Parmağın viewDictionary
şu anda dokunduğu görünümü belirlemek için öğesini numaralandırır ve belirli bir kimlikle ilişkili geçerli TouchEffect
örneği (ve dolayısıyla geçerli görünümü) depolamak için kullanıridToEffectDictionary
:
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;
}
}
içinde idToEffectDictionary
bir değişiklik yapıldıysa yöntemi, FireEvent
bir görünümden diğerine aktarmak için Exited
ve Entered
çağrıları yapabilir. Bununla birlikte, parmak eklenmiş TouchEffect
olmayan bir görünümün kapladığı bir alana veya bu alandan efekt eklenmiş bir görünüme taşınmış olabilir.
Görünüme try
erişildiğinde ve catch
bloğuna dikkat edin. Bu sayfaya gidilen ve giriş sayfasına geri gidilen bir sayfada OnDetached
yöntem çağrılmaz ve öğeler içinde viewDictionary
kalır, ancak Android bunları atılmış olarak kabul eder.
iOS Uygulaması
iOS uygulaması Android uygulamasına benzer, ancak iOS TouchEffect
sınıfının bir türevini oluşturması UIGestureRecognizer
gerekir. Bu, iOS projesinde adlı TouchRecognizer
bir sınıftır. Bu sınıf, örnekleri depolayan TouchRecognizer
iki statik sözlük tutar:
static Dictionary<UIView, TouchRecognizer> viewDictionary =
new Dictionary<UIView, TouchRecognizer>();
static Dictionary<long, TouchRecognizer> idToTouchDictionary =
new Dictionary<long, TouchRecognizer>();
Bu TouchRecognizer
sınıfın yapısının çoğu Android TouchEffect
sınıfına benzer.
Önemli
uygulamasındaki görünümlerin UIKit
çoğunda dokunmatik ekran varsayılan olarak etkin değildir. Dokunma, iOS projesindeki OnAttached
TouchEffect
sınıftaki geçersiz kılmaya eklenerek view.UserInteractionEnabled = true;
etkinleştirilebilir. Bu, efektin UIView
eklendiği öğeye karşılık gelen elde edildikten sonra gerçekleşmelidir.
Dokunma Efektini Çalışır Duruma Getirmek
Örnek program, ortak görevler için dokunma izleme etkisini test eden beş sayfa içerir.
BoxView Sürükleme sayfası, öğesine öğe eklemenize BoxView
AbsoluteLayout
ve ardından bunları ekranda sürüklemenize olanak tanır. XAML dosyası, öğesine öğe AbsoluteLayout
eklemek BoxView
ve öğesini temizlemek için iki Button
görünümün örneğini AbsoluteLayout
oluşturur.
'a AbsoluteLayout
yeni bir ekleyen arka planda kod dosyasındakiyöntemi, öğesine bir TouchEffect
nesnesi BoxView
ekler ve efekte bir olay işleyicisi BoxView
ekler:
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);
}
Olay işleyicisi TouchAction
tüm BoxView
öğeler için tüm dokunma olaylarını işler, ancak biraz dikkatli olması gerekir: Program yalnızca sürükleme uyguladığından ve iki parmağın birbirine karışması nedeniyle tek bir BoxView
parmak üzerinde iki parmağın kullanılmasına izin vermez. Bu nedenle, sayfa şu anda izlenmekte olan her parmak için eklenmiş bir sınıf tanımlar:
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>();
şu dragDictionary
anda sürüklenen her BoxView
için bir girdi içerir.
Dokunma Pressed
eylemi bu sözlüğe bir öğe ekler ve eylem öğeyi Released
kaldırır. Mantığın Pressed
sözlüğe ait bir öğe olup olmadığını denetlemesi BoxView
gerekir. Öyleyse, zaten sürükleniyor BoxView
ve yeni olay aynı BoxView
üzerinde ikinci parmaktır. Moved
ve Released
eylemleri için, olay işleyicisi sözlüğün bunun BoxView
için bir girdisi olup olmadığını ve sürüklenen BoxView
sözlüğe ait touch Id
özelliğinin sözlük girdisindekiyle eşleşip eşleşmediğini denetlemelidir:
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;
}
}
Mantık, Pressed
nesnesinin Capture
TouchEffect
özelliğini olarak true
ayarlar. Bu, bu parmak için sonraki tüm olayları aynı olay işleyicisine teslim etme etkisine sahiptir.
Mantık, Moved
ekli özelliği değiştirerek LayoutBounds
öğesini taşırBoxView
. Location
Olay bağımsız değişkenlerinin özelliği her zaman sürüklenene BoxView
göredir ve BoxView
sabit bir hızda sürükleniyorsa ardışık Location
olayların özellikleri yaklaşık olarak aynı olur. Örneğin, bir parmak ortasındakine BoxView
basarsa, Pressed
eylem sonraki olaylar için aynı kalan ( PressPoint
50, 50) özelliğini depolar. BoxView
sabit bir hızda çapraz olarak sürüklenirse, eylem sırasındaki Moved
sonraki Location
özellikler değeri (55, 55) olabilir ve bu durumda Moved
mantık öğesinin yatay ve dikey konumuna BoxView
5 ekler. Bu, merkezi yeniden doğrudan parmağın altında olacak şekilde hareket eder BoxView
.
Farklı parmaklar kullanarak birden çok BoxView
öğeyi aynı anda taşıyabilirsiniz.
Görünümü Alt Sınıflama
Çoğu zaman, bir Xamarin.Forms öğenin kendi dokunma olaylarını işlemesi daha kolaydır. Sürüklenebilir BoxView Sürükleme sayfası, BoxView Sürükleme sayfasıyla aynı işlevi görür, ancak kullanıcının sürüklediği öğeler öğesinden BoxView
türetilen bir DraggableBoxView
sınıfın örnekleridir:
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;
}
}
}
Oluşturucu, öğesini oluşturup ekler TouchEffect
ve bu nesne ilk kez başlatıldığında özelliğini ayarlar Capture
. Sınıfın kendisi her parmakla ilişkili , pressPoint
ve touchId
değerlerini depoladığı isBeingDragged
için sözlük gerekmez. İşlemeMoved
, ve TranslationY
özelliklerini değiştirirTranslationX
, böylece mantığın üst öğesi DraggableBoxView
bir AbsoluteLayout
olmasa bile çalışır.
SkiaSharp ile tümleştirme
Sonraki iki gösterim için grafik gerekir ve bu amaçla SkiaSharp kullanılır. Bu örnekleri incelemeden önce SkiaSharp kullanma Xamarin.Forms hakkında bilgi edinmek isteyebilirsiniz. İlk iki makale ("SkiaSharp Çizim Temel Bilgileri" ve "SkiaSharp Çizgileri ve Yolları"), burada ihtiyacınız olacak her şeyi kapsar.
Üç Nokta Çizim sayfası, parmağınızı ekranda çekerek bir elips çizmenizi sağlar. Parmağınızı nasıl hareket ettirdiğinize bağlı olarak, elipsleri sol üst köşeden sağ alta veya başka bir köşeden karşı köşeye çizebilirsiniz. Üç nokta rastgele bir renk ve opaklık ile çizilir.
Daha sonra üç noktalardan birine dokunursanız, başka bir konuma sürükleyebilirsiniz. Bu, belirli bir noktada grafik nesnenin aranarak "isabet testi" olarak bilinen bir teknik gerektirir. SkiaSharp üç nokta öğeleri değildir Xamarin.Forms , bu nedenle kendi TouchEffect
işlemlerini gerçekleştiremezler. tüm TouchEffect
SKCanvasView
nesneye uygulanmalıdır.
EllipseDrawPage.xaml dosyası, tek hücreli Grid
içinde örneğini SKCanvasView
oluşturur. TouchEffect
nesnesi bu Grid
nesneye eklenir:
<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>
Android'de ve Evrensel Windows Platformu doğrudan TouchEffect
öğesine eklenebilirSKCanvasView
, ancak iOS'ta çalışmaz. özelliğinin Capture
olarak ayarlandığına true
dikkat edin.
SkiaSharp tarafından işlenen her üç nokta türündeki EllipseDrawingFigure
bir nesneyle temsil edilir:
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;
}
}
StartPoint
program dokunmatik girişi işlerken ve EndPoint
özellikleri kullanılır; Rectangle
özelliği üç nokta çizmek için kullanılır. LastFingerLocation
Üç nokta sürüklenirken özelliği devreye girer ve IsInEllipse
yöntem isabet testine yardımcı olur. Yöntem, nokta üç noktanın içindeyse döndürür true
.
Arka planda kod dosyası üç koleksiyon tutar:
Dictionary<long, EllipseDrawingFigure> inProgressFigures = new Dictionary<long, EllipseDrawingFigure>();
List<EllipseDrawingFigure> completedFigures = new List<EllipseDrawingFigure>();
Dictionary<long, EllipseDrawingFigure> draggingFigures = new Dictionary<long, EllipseDrawingFigure>();
Sözlük, draggingFigure
koleksiyonun completedFigures
bir alt kümesini içerir. SkiaSharp PaintSurface
olay işleyicisi yalnızca ve completedFigures
inProgressFigures
koleksiyonlarındaki nesneleri işler:
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);
}
}
Dokunma işleminin en karmaşık kısmı işlemedir Pressed
. Burada isabet testi gerçekleştirilir, ancak kod kullanıcının parmağı altında bir üç nokta algılarsa, bu üç nokta yalnızca şu anda başka bir parmak tarafından sürüklenmediyse sürüklenebilir. Kullanıcının parmağında üç nokta yoksa kod yeni bir üç nokta çizme işlemini başlatır:
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;
Diğer SkiaSharp örneği, Finger Paint sayfasıdır. İki Picker
görünümden bir vuruş rengi ve vuruş genişliği seçebilir ve ardından bir veya daha fazla parmakla çizim yapabilirsiniz:
Bu örnek, ekranda boyanan her çizgiyi temsil etmek için ayrı bir sınıf gerektirir:
class FingerPaintPolyline
{
public FingerPaintPolyline()
{
Path = new SKPath();
}
public SKPath Path { set; get; }
public Color StrokeColor { set; get; }
public float StrokeWidth { set; get; }
}
SKPath
Her satırı işlemek için bir nesne kullanılır. FingerPaint.xaml.cs dosyası, biri şu anda çizilmekte olan çok çizgili ve diğeri tamamlanmış çok çizgili nesneler için olmak üzere bu nesnelerin iki koleksiyonunu tutar:
Dictionary<long, FingerPaintPolyline> inProgressPolylines = new Dictionary<long, FingerPaintPolyline>();
List<FingerPaintPolyline> completedPolylines = new List<FingerPaintPolyline>();
İşleme, Pressed
ilk noktayı depolamak için yol nesnesi üzerinde yeni FingerPaintPolyline
bir oluşturur MoveTo
ve bu nesneyi inProgressPolylines
sözlüğe ekler. İşleme Moved
, yeni parmak konumuyla yol nesnesine çağrılar LineTo
ve Released
işlem tamamlanan çok çizgiliyi 'den'e inProgressPolylines
completedPolylines
aktarır. Bir kez daha, gerçek SkiaSharp çizim kodu nispeten basittir:
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);
}
}
Görünümden Görünüme Dokunmayı İzleme
Önceki örneklerin tümü, öğesinin Capture
TouchEffect
TouchEffect
true
özelliğini oluşturulduğunda veya olay oluştuğunda Pressed
olarak ayarlamıştır. Bu, aynı öğenin görünüme ilk basan parmakla ilişkili tüm olayları almasını sağlar. Son örnek olarak true
ayarlanmadıCapture
. Bu, ekranla temas eden bir parmak bir öğeden diğerine geçtiğinde farklı davranışlara neden olur. Parmağın hareket eden öğesi, özelliği ayarlanmış TouchActionType.Exited
bir Type
olay alır ve ikinci öğe ayarına TouchActionType.Entered
sahip bir Type
olay alır.
Bu tür dokunmatik işleme, bir müzik klavyesi için çok kullanışlıdır. Bir tuş basıldığında algılayabilmelidir, ancak bir parmak bir tuştan diğerine kaydığında da algılayabilmelidir.
Sessiz Klavye sayfası, öğesinden Key
türetilen küçük WhiteKey
ve BlackKey
sınıflarını BoxView
tanımlar.
sınıfı Key
, gerçek bir müzik programında kullanılmaya hazırdır. ve KeyNumber
adlı IsPressed
ortak özellikleri tanımlar. Bu, MIDI standardı tarafından oluşturulan anahtar koduna ayarlanması amaçlanmıştır. sınıfı, Key
özelliği değiştiğinde IsPressed
çağrılan adlı StatusChanged
bir olayı da tanımlar.
Her tuşa birden çok parmakla izin verilir. Bu nedenle, Key
sınıfı şu anda bu tuşa dokunan tüm parmaklarının dokunmatik kimlik numaralarından birini List
tutar:
List<long> ids = new List<long>();
Olay işleyicisiTouchAction
, hem olay türü hem de Pressed
türü Entered
için listeye bir kimlik ids
ekler, ancak yalnızca özelliği true
olay için Entered
olduğundaIsInContact
. Kimlik, bir Released
veya Exited
olayı için öğesinden List
kaldırılır:
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;
}
}
ve yöntemlerinin AddToList
her ikisi de öğesinin List
boş ile boş olmayan arasında değişip değişmediğini denetler ve değiştirildiğinde olayı çağırırStatusChanged
.RemoveFromList
Çeşitli WhiteKey
ve BlackKey
öğeler sayfanın XAML dosyasında düzenlenir ve telefon yatay modda tutulduğunda en iyi şekilde görünür:
Parmağınızı tuşlar boyunca süpürürseniz, dokunma olaylarının bir tuştan diğerine aktarıldığını renkli küçük değişikliklerle görürsünüz.
Özet
Bu makalede, bir efektteki olayların nasıl çağrıldığı ve düşük düzeyli çok dokunmalı işleme uygulayan bir efektin nasıl yazıldığı ve kullanılacağı gösterilmiştir.