Rilevamento con più dita a tocco
Questo argomento illustra come tenere traccia degli eventi di tocco da più dita
Ci sono momenti in cui un'applicazione multitocco deve tenere traccia delle singole dita mentre si spostano contemporaneamente sullo schermo. Un'applicazione tipica è un programma di pittura di dito. Si vuole che l'utente sia in grado di disegnare con un solo dito, ma anche di disegnare con più dita contemporaneamente. Poiché il programma elabora più eventi di tocco, deve distinguere quali eventi corrispondono a ogni dito. Android fornisce un codice ID a questo scopo, ma ottenere e gestire tale codice può essere un po' complicato.
Per tutti gli eventi associati a un dito specifico, il codice ID rimane invariato. Il codice ID viene assegnato quando un dito tocca per la prima volta lo schermo e diventa non valido dopo che il dito si solleva dallo schermo. Questi codici ID sono in genere numeri interi molto piccoli e Android li riutilizza per gli eventi di tocco successivi.
Quasi sempre, un programma che tiene traccia delle singole dita mantiene un dizionario per il rilevamento del tocco. La chiave del dizionario è il codice ID che identifica un dito specifico. Il valore del dizionario dipende dall'applicazione. Nell'esempio FingerPaint ogni tratto del dito (dal tocco al rilascio) è associato a un oggetto che contiene tutte le informazioni necessarie per eseguire il rendering della linea disegnata con il dito. Il programma definisce una piccola FingerPaintPolyline
classe a questo scopo:
class FingerPaintPolyline
{
public FingerPaintPolyline()
{
Path = new Path();
}
public Color Color { set; get; }
public float StrokeWidth { set; get; }
public Path Path { private set; get; }
}
Ogni polilinea ha un colore, una larghezza del tratto e un oggetto grafico Path
Android per accumulare ed eseguire il rendering di più punti della linea mentre viene disegnato.
Il resto del codice illustrato di seguito è contenuto in un View
derivato denominato FingerPaintCanvasView
. Tale classe mantiene un dizionario di oggetti di tipo FingerPaintPolyline
durante il tempo in cui vengono disegnati attivamente da una o più dita:
Dictionary<int, FingerPaintPolyline> inProgressPolylines = new Dictionary<int, FingerPaintPolyline>();
Questo dizionario consente alla visualizzazione di ottenere rapidamente le FingerPaintPolyline
informazioni associate a un dito specifico.
La FingerPaintCanvasView
classe gestisce anche un List
oggetto per le polilinee completate:
List<FingerPaintPolyline> completedPolylines = new List<FingerPaintPolyline>();
Gli oggetti in questo List
oggetto si trovano nello stesso ordine in cui sono stati disegnati.
FingerPaintCanvasView
esegue l'override di due metodi definiti da View
: OnDraw
e OnTouchEvent
.
OnDraw
Nell'override, la vista disegna le polilinee completate e quindi disegna le polilinee in corso.
L'override del OnTouchEvent
metodo inizia ottenendo un pointerIndex
valore dalla ActionIndex
proprietà . Questo ActionIndex
valore distingue tra più dita, ma non è coerente tra più eventi. Per questo motivo, si usa per pointerIndex
ottenere il valore del puntatore id
dal GetPointerId
metodo . Questo ID è coerente tra più eventi:
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;
}
Si noti che l'override usa la ActionMasked
proprietà nell'istruzione switch
anziché la Action
proprietà . Ecco perché:
Quando si ha a che fare con il multitocco, la Action
proprietà ha un valore per MotionEventsAction.Down
il primo dito per toccare lo schermo, quindi i valori e Pointer2Down
Pointer3Down
come secondo e terzo dita toccano anche lo schermo. Come il quarto e il quinto dita fanno contatto, la Action
proprietà ha valori numerici che non corrispondono nemmeno ai membri dell'enumerazione MotionEventsAction
. È necessario esaminare i valori dei flag di bit nei valori per interpretare il significato.
Analogamente, quando le dita lasciano il contatto con lo schermo, la Action
proprietà ha valori di Pointer2Up
e Pointer3Up
per il secondo e il terzo dita e Up
per il primo dito.
La ActionMasked
proprietà accetta un numero minore di valori perché deve essere usata insieme alla ActionIndex
proprietà per distinguere tra più dita. Quando le dita toccano lo schermo, la proprietà può essere uguale MotionEventActions.Down
solo per il primo dito e PointerDown
per le dita successive. Quando le dita lasciano lo schermo, ActionMasked
ha valori per Pointer1Up
le dita successive e Up
per il primo dito.
Quando si usa ActionMasked
, la ActionIndex
distinzione tra le dita successive da toccare e lasciare lo schermo, ma in genere non è necessario usare tale valore tranne come argomento per altri metodi nell'oggetto MotionEvent
. Per il multitocco, uno dei metodi più importanti è GetPointerId
chiamato nel codice precedente. Questo metodo restituisce un valore che è possibile utilizzare per una chiave del dizionario per associare eventi specifici alle dita.
L'override OnTouchEvent
nell'esempio elabora gli MotionEventActions.Down
eventi e PointerDown
in modo identico creando un nuovo FingerPaintPolyline
oggetto e aggiungendolo al dizionario:
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;
// ...
}
// ...
}
Si noti che pointerIndex
viene usato anche per ottenere la posizione del dito all'interno della visualizzazione. Tutte le informazioni sul tocco sono associate al pointerIndex
valore . Identifica id
in modo univoco le dita su più messaggi, in modo che venga usata per creare la voce del dizionario.
Analogamente, l'override OnTouchEvent
gestisce anche e Pointer1Up
MotionEventActions.Up
in modo identico trasferendo la polilinea completata alla completedPolylines
raccolta in modo che possano essere disegnati durante l'overrideOnDraw
. Il codice rimuove anche la id
voce dal dizionario:
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;
}
// ...
}
Ora per la parte difficile.
Tra gli eventi down e up, in genere sono presenti molti MotionEventActions.Move
eventi. Questi vengono raggruppati in una singola chiamata a OnTouchEvent
e devono essere gestiti in modo diverso dagli Down
eventi e Up
. Il pointerIndex
valore ottenuto in precedenza dalla ActionIndex
proprietà deve essere ignorato. Al contrario, il metodo deve ottenere più pointerIndex
valori eseguendo un ciclo tra 0 e la PointerCount
proprietà e quindi ottenere un per id
ognuno di questi pointerIndex
valori:
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;
// ...
}
// ...
}
Questo tipo di elaborazione consente al campione di tenere traccia delle singole dita e di disegnare i risultati sullo schermo:
Ora hai visto come tenere traccia delle singole dita sullo schermo e distinguerle.