Śledzenie palcem wielodotykowym
W tym temacie pokazano, jak śledzić zdarzenia dotykowe z wielu palców
Czasami aplikacja wielodotykowa musi śledzić poszczególne palce, gdy poruszają się jednocześnie na ekranie. Jedną z typowych aplikacji jest program malowania palcami. Chcesz, aby użytkownik mógł rysować za pomocą pojedynczego palca, ale także rysować z wieloma palcami jednocześnie. Ponieważ program przetwarza wiele zdarzeń dotykowych, musi odróżnić zdarzenia odpowiadające każdemu palcem. W tym celu system Android dostarcza kod identyfikatora, ale uzyskanie i obsługa tego kodu może być nieco trudne.
Dla wszystkich zdarzeń skojarzonych z określonym palcem kod identyfikatora pozostaje taki sam. Kod identyfikatora jest przypisywany, gdy palec po raz pierwszy dotyka ekranu i staje się nieprawidłowy po naciśnięciu palca z ekranu. Te kody identyfikatorów są zazwyczaj bardzo małymi liczbami całkowitymi, a system Android ponownie używa ich w przypadku późniejszych zdarzeń dotykowych.
Prawie zawsze, program, który śledzi poszczególne palce utrzymuje słownik do śledzenia dotyku. Klucz słownika to kod identyfikatora, który identyfikuje konkretny palec. Wartość słownika zależy od aplikacji. W przykładzie FingerPaint każdy pociągnięcie palca (od dotyku do zwolnienia) jest skojarzone z obiektem zawierającym wszystkie informacje niezbędne do renderowania linii narysowanej palcem. Program definiuje małą FingerPaintPolyline
klasę w tym celu:
class FingerPaintPolyline
{
public FingerPaintPolyline()
{
Path = new Path();
}
public Color Color { set; get; }
public float StrokeWidth { set; get; }
public Path Path { private set; get; }
}
Każda wielolinia ma kolor, szerokość pociągnięcia i obiekt graficzny systemu Path
Android do gromadzenia i renderowania wielu punktów linii podczas rysowania.
Pozostała część kodu pokazanego poniżej znajduje się w pochodnej View
nazwie FingerPaintCanvasView
. Ta klasa utrzymuje słownik obiektów typu FingerPaintPolyline
w czasie, gdy są aktywnie rysowane przez co najmniej jeden palec:
Dictionary<int, FingerPaintPolyline> inProgressPolylines = new Dictionary<int, FingerPaintPolyline>();
Ten słownik umożliwia szybkie uzyskanie FingerPaintPolyline
informacji skojarzonych z konkretnym palcem.
Klasa FingerPaintCanvasView
obsługuje List
również obiekt dla linii polilinii, które zostały ukończone:
List<FingerPaintPolyline> completedPolylines = new List<FingerPaintPolyline>();
Obiekty w tej List
kolejności znajdują się w tej samej kolejności, w której zostały narysowane.
FingerPaintCanvasView
zastępuje dwie metody zdefiniowane przez View
: OnDraw
i OnTouchEvent
.
W jego OnDraw
przesłonięć widok rysuje ukończone linie wieloliniowe, a następnie rysuje wielolinie w toku.
Zastąpienie OnTouchEvent
metody rozpoczyna się od uzyskania pointerIndex
wartości z ActionIndex
właściwości . Ta ActionIndex
wartość rozróżnia wiele palców, ale nie jest spójna w wielu zdarzeniach. Z tego powodu użyj polecenia , pointerIndex
aby uzyskać wartość wskaźnika id
z GetPointerId
metody . Ten identyfikator jest spójny w wielu zdarzeniach:
public override bool OnTouchEvent(MotionEvent args)
{
// Get the pointer index
int pointerIndex = args.ActionIndex;
// Get the id to identify a finger over the course of its progress
int id = args.GetPointerId(pointerIndex);
// Use ActionMasked here rather than Action to reduce the number of possibilities
switch (args.ActionMasked)
{
// ...
}
// Invalidate to update the view
Invalidate();
// Request continued touch input
return true;
}
Zwróć uwagę, że przesłonięcia używają ActionMasked
właściwości w instrukcji switch
, a nie Action
właściwości . Poniżej przedstawiono przyczyny:
Gdy masz do czynienia z wieloma dotykami, Action
właściwość ma wartość MotionEventsAction.Down
dla pierwszego palca, aby dotknąć ekranu, a następnie wartości Pointer2Down
i Pointer3Down
jako drugie i trzecie palce również dotknąć ekranu. Jako czwarte i piąte palce nawiązać kontakt, Action
właściwość ma wartości liczbowe, które nawet nie odpowiadają członkom MotionEventsAction
wyliczenia! Należy zbadać wartości flag bitowych w wartościach, aby zinterpretować, co oznaczają.
Podobnie, jak palce pozostawiają kontakt z ekranem, Action
właściwość ma wartości Pointer2Up
i Pointer3Up
dla drugiego i trzeciego palca, a Up
dla pierwszego palca.
Właściwość ActionMasked
przyjmuje mniejszą liczbę wartości, ponieważ ma być używana w połączeniu z właściwością ActionIndex
, aby odróżnić wiele palców. Gdy palce dotykają ekranu, właściwość może być równa MotionEventActions.Down
tylko dla pierwszego palca i PointerDown
kolejnych palców. Gdy palce opuszczają ekran, ActionMasked
ma wartości Pointer1Up
dla kolejnych palców i Up
pierwszego palca.
W przypadku używania ActionMasked
parametru ActionIndex
wyróżnia się między kolejnymi palcami, aby dotknąć i opuścić ekran, ale zwykle nie trzeba używać tej wartości, z wyjątkiem argumentu dla innych metod w MotionEvent
obiekcie. W przypadku wielodotyku jedna z najważniejszych z tych metod jest GetPointerId
wywoływana w powyższym kodzie. Ta metoda zwraca wartość, której można użyć dla klucza słownika, aby skojarzyć określone zdarzenia z palcami.
Przesłonięcia OnTouchEvent
w przykładzie przetwarza zdarzenia MotionEventActions.Down
i PointerDown
identycznie, tworząc nowy FingerPaintPolyline
obiekt i dodając go do słownika:
public override bool OnTouchEvent(MotionEvent args)
{
// Get the pointer index
int pointerIndex = args.ActionIndex;
// Get the id to identify a finger over the course of its progress
int id = args.GetPointerId(pointerIndex);
// Use ActionMasked here rather than Action to reduce the number of possibilities
switch (args.ActionMasked)
{
case MotionEventActions.Down:
case MotionEventActions.PointerDown:
// Create a Polyline, set the initial point, and store it
FingerPaintPolyline polyline = new FingerPaintPolyline
{
Color = StrokeColor,
StrokeWidth = StrokeWidth
};
polyline.Path.MoveTo(args.GetX(pointerIndex),
args.GetY(pointerIndex));
inProgressPolylines.Add(id, polyline);
break;
// ...
}
// ...
}
Zwróć uwagę, że pointerIndex
element jest również używany do uzyskania położenia palca w widoku. Wszystkie informacje dotykowe są skojarzone z wartością pointerIndex
. Element id
jednoznacznie identyfikuje palce w wielu komunikatach, dzięki czemu jest używany do tworzenia wpisu słownika.
OnTouchEvent
Podobnie przesłonięcia obsługują również metodę MotionEventActions.Up
i Pointer1Up
identycznie, przenosząc ukończoną wielolinię do completedPolylines
kolekcji, aby można je było narysować podczas OnDraw
przesłonięcia. Kod usuwa id
również wpis ze słownika:
public override bool OnTouchEvent(MotionEvent args)
{
// ...
switch (args.ActionMasked)
{
// ...
case MotionEventActions.Up:
case MotionEventActions.Pointer1Up:
inProgressPolylines[id].Path.LineTo(args.GetX(pointerIndex),
args.GetY(pointerIndex));
// Transfer the in-progress polyline to a completed polyline
completedPolylines.Add(inProgressPolylines[id]);
inProgressPolylines.Remove(id);
break;
case MotionEventActions.Cancel:
inProgressPolylines.Remove(id);
break;
}
// ...
}
Teraz na trudną część.
Między zdarzeniami w dół i w górę zazwyczaj istnieje wiele MotionEventActions.Move
zdarzeń. Są one umieszczane w jednym wywołaniu metody OnTouchEvent
i muszą być obsługiwane inaczej niż zdarzenia Down
i Up
. Wartość pointerIndex
uzyskana wcześniej z ActionIndex
właściwości musi być ignorowana. Zamiast tego metoda musi uzyskać wiele pointerIndex
wartości przez pętlę z zakresu od 0 do PointerCount
właściwości, a następnie uzyskać dla id
każdej z tych pointerIndex
wartości:
public override bool OnTouchEvent(MotionEvent args)
{
// ...
switch (args.ActionMasked)
{
// ...
case MotionEventActions.Move:
// Multiple Move events are bundled, so handle them differently
for (pointerIndex = 0; pointerIndex < args.PointerCount; pointerIndex++)
{
id = args.GetPointerId(pointerIndex);
inProgressPolylines[id].Path.LineTo(args.GetX(pointerIndex),
args.GetY(pointerIndex));
}
break;
// ...
}
// ...
}
Ten typ przetwarzania umożliwia przykładowi śledzenie poszczególnych palców i rysowanie wyników na ekranie:
Wiesz już, jak można śledzić poszczególne palce na ekranie i rozróżniać je.