Multitouch-Fingerverfolgung

In diesem Thema wird veranschaulicht, wie Touchereignisse von mehreren Fingern aus nachverfolgt werden.

Es gibt Situationen, in denen eine Multitouchanwendung einzelne Finger verfolgen muss, während sie sich gleichzeitig auf dem Bildschirm bewegen. Eine typische Anwendung ist ein Finger-Paint-Programm. Sie möchten, dass der Benutzer mit einem einzelnen Finger zeichnen kann, aber auch mit mehreren Fingern gleichzeitig zeichnen kann. Da Ihr Programm mehrere Touchereignisse verarbeitet, muss es unterscheiden, welche Ereignisse den einzelnen Fingern entsprechen. Android stellt zu diesem Zweck einen ID-Code bereit, aber das Abrufen und Behandeln dieses Codes kann ein wenig schwierig sein.

Für alle Ereignisse, die einem bestimmten Finger zugeordnet sind, bleibt der ID-Code gleich. Der ID-Code wird zugewiesen, wenn ein Finger den Bildschirm zum ersten Mal berührt, und wird ungültig, nachdem der Finger vom Bildschirm anhebt. Diese ID-Codes sind im Allgemeinen sehr kleine ganze Zahlen, und Android verwendet sie für spätere Touchereignisse.

Fast immer verwaltet ein Programm, das einzelne Finger verfolgt, ein Wörterbuch für die Touchverfolgung. Der Wörterbuchschlüssel ist der ID-Code, der einen bestimmten Finger identifiziert. Der Wert des Wörterbuchs hängt von der Anwendung ab. Im FingerPaint-Programm ist jeder Fingerstrich (von der Berührung bis zum Loslassen) einem Objekt zugeordnet, das alle Informationen enthält, die zum Rendern der mit diesem Finger gezeichneten Linie erforderlich sind. Das Programm definiert eine kleine FingerPaintPolyline Klasse für diesen Zweck:

class FingerPaintPolyline
{
    public FingerPaintPolyline()
    {
        Path = new Path();
    }

    public Color Color { set; get; }

    public float StrokeWidth { set; get; }

    public Path Path { private set; get; }
}

Jede Polylinie verfügt über eine Farbe, eine Strichbreite und ein Android-Grafikobjekt Path , um beim Zeichnen mehrere Punkte der Linie zu sammeln und zu rendern.

Der Rest des unten gezeigten Codes ist in einer View Derivat namens FingerPaintCanvasViewenthalten. Diese Klasse verwaltet ein Wörterbuch von Objekten vom Typ FingerPaintPolyline , während sie aktiv von einem oder mehreren Fingern gezeichnet werden:

Dictionary<int, FingerPaintPolyline> inProgressPolylines = new Dictionary<int, FingerPaintPolyline>();

Mit diesem Wörterbuch kann die Ansicht schnell die Informationen abrufen, die FingerPaintPolyline einem bestimmten Finger zugeordnet sind.

Die FingerPaintCanvasView -Klasse verwaltet auch ein List -Objekt für die abgeschlossenen Polylinien:

List<FingerPaintPolyline> completedPolylines = new List<FingerPaintPolyline>();

Die Objekte in diesem List befinden sich in derselben Reihenfolge, in der sie gezeichnet wurden.

FingerPaintCanvasView überschreibt zwei Methoden, die durch definiert sind View:OnDraw und OnTouchEvent. In ihrer OnDraw Überschreibung zeichnet die Ansicht die abgeschlossenen Polylinien und zeichnet dann die laufenden Polylinien.

Die Außerkraftsetzung der OnTouchEvent -Methode beginnt mit dem Abrufen eines pointerIndex Werts aus der ActionIndex -Eigenschaft. Dieser ActionIndex Wert unterscheidet zwischen mehreren Fingern, ist aber nicht über mehrere Ereignisse hinweg konsistent. Aus diesem Grund verwenden Sie den pointerIndex , um den Zeigerwert id aus der GetPointerId -Methode abzurufen. Diese ID ist für mehrere Ereignisse konsistent:

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;
}

Beachten Sie, dass die Außerkraftsetzung die ActionMasked -Eigenschaft in der switch -Anweisung anstelle der Action -Eigenschaft verwendet. Dies ist der Grund:

Wenn Sie es mit Multitouch zu tun haben, hat die Action Eigenschaft den Wert für MotionEventsAction.Down den ersten Finger, um den Bildschirm zu berühren, und dann die Werte von Pointer2Down und Pointer3Down , da der zweite und dritte Finger den Bildschirm berühren. Wenn der vierte und fünfte Finger Kontakt herstellen, verfügt die Action Eigenschaft über numerische Werte, die nicht einmal Membern der MotionEventsAction Enumeration entsprechen! Sie müssen die Werte von Bitflags in den Werten untersuchen, um zu interpretieren, was sie bedeuten.

Wenn die Finger den Kontakt mit dem Bildschirm verlassen, weist die Action Eigenschaft die Werte Pointer2Up und Pointer3Up für den zweiten und dritten Finger sowie Up für den ersten Finger auf.

Die ActionMasked -Eigenschaft nimmt weniger Werte an, da sie in Verbindung mit der ActionIndex -Eigenschaft verwendet werden soll, um zwischen mehreren Fingern zu unterscheiden. Wenn die Finger den Bildschirm berühren, kann die -Eigenschaft nur für den ersten Finger und PointerDown für nachfolgende Finger gleich seinMotionEventActions.Down. Wenn die Finger den Bildschirm verlassen, ActionMasked hat die Werte Pointer1Up für die nachfolgenden Finger und Up für den ersten Finger.

Bei Verwendung von ActionMaskedunterscheidet die ActionIndex zwischen den nachfolgenden Fingern, um den Bildschirm zu berühren und zu verlassen, aber Sie müssen diesen Wert in der MotionEvent Regel nicht verwenden, außer als Argument für andere Methoden im Objekt. Für Multitouch wird GetPointerId eine der wichtigsten dieser Methoden im obigen Code aufgerufen. Diese Methode gibt einen Wert zurück, den Sie für einen Wörterbuchschlüssel verwenden können, um bestimmte Ereignisse den Fingern zuzuordnen.

Die OnTouchEvent Überschreibung im FingerPaint-Programm verarbeitet die MotionEventActions.Down Ereignisse und PointerDown identisch, indem ein neues FingerPaintPolyline Objekt erstellt und dem Wörterbuch hinzugefügt wird:

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;
        // ...
    }
    // ...        
}

Beachten Sie, dass auch verwendet pointerIndex wird, um die Position des Fingers in der Ansicht abzurufen. Alle Touchinformationen sind dem pointerIndex Wert zugeordnet. Der id identifiziert Finger eindeutig über mehrere Nachrichten hinweg, sodass dieser zum Erstellen des Wörterbucheintrags verwendet wird.

Ebenso behandelt die OnTouchEvent Außerkraftsetzung die MotionEventActions.Up und Pointer1Up identisch, indem die fertige Polylinie in die completedPolylines Auflistung übertragen wird, damit sie während der OnDraw Außerkraftsetzung gezeichnet werden können. Der Code entfernt auch den id Eintrag aus dem Wörterbuch:

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;
    }
    // ...        
}

Nun zum kniffligen Teil.

Zwischen den Down- und Up-Ereignissen gibt es in der Regel viele MotionEventActions.Move Ereignisse. Diese werden in einem einzelnen Aufruf von OnTouchEventgebündelt, und sie müssen anders als die Down Ereignisse und Up behandelt werden. Der pointerIndex zuvor aus der ActionIndex -Eigenschaft abgerufene Wert muss ignoriert werden. Stattdessen muss die -Methode mehrere pointerIndex Werte abrufen, indem sie zwischen 0 und der PointerCount -Eigenschaft schleift, und dann eine id für jeden dieser pointerIndex Werte abrufen:

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;
        // ...
    }
    // ...        
}

Diese Art der Verarbeitung ermöglicht es dem FingerPaint-Programm , einzelne Finger nachzuverfolgen und die Ergebnisse auf dem Bildschirm zu zeichnen:

Beispielscreenshot aus Dem FingerPaint-Beispiel

Sie haben nun gesehen, wie Sie einzelne Finger auf dem Bildschirm nachverfolgen und zwischen ihnen unterscheiden können.