Teilen über


Multitouchfingerverfolgung

In diesem Thema wird das Nachverfolgen von Touchereignissen von mehreren Fingern veranschaulicht.

Es gibt Situationen, in denen eine Multitouchanwendung einzelne Finger nachverfolgen muss, während sie gleichzeitig auf dem Bildschirm bewegt werden. Eine typische Anwendung ist ein Fingerfarbenprogramm. Sie möchten, dass der Benutzer mit einem Finger zeichnen kann, aber auch mit mehreren Fingern gleichzeitig zeichnen kann. Wenn Ihr Programm mehrere Touchereignisse verarbeitet, muss unterschieden werden, welche Ereignisse jedem Finger entsprechen. Android stellt einen ID-Code für diesen Zweck zur Hand, aber das Abrufen und Behandeln dieses Codes kann ein wenig schwierig sein.

Für alle Ereignisse, die einem bestimmten Finger zugeordnet sind, wird der ID-Code erneut Standard identisch. Der ID-Code wird zugewiesen, wenn ein Finger zuerst den Bildschirm berührt und nach dem Aufheben des Fingers vom Bildschirm ungültig wird. Diese ID-Codes sind im Allgemeinen sehr kleine ganze Zahlen, und Android verwendet sie für spätere Touchereignisse wieder.

Fast immer, ein Programm, das einzelne Finger verfolgt, Standard ein Wörterbuch zur Touchverfolgung enthält. Der Wörterbuchschlüssel ist der ID-Code, der einen bestimmten Finger identifiziert. Der Wörterbuchwert hängt von der Anwendung ab. Im FingerPaint-Beispiel ist jeder Fingerstrich (von Der Fingereingabe bis zur Freigabe) einem Objekt zugeordnet, das alle erforderlichen Informationen enthält, um die mit dem Finger gezeichnete Linie zu rendern. Das Programm definiert für diesen Zweck eine kleine FingerPaintPolyline Klasse:

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 mehrere Punkte der Linie zu sammeln und zu rendern, während sie gezeichnet wird.

Der Re Standard der des unten gezeigten Codes ist in einem View Abgeleiteten namens FingerPaintCanvasViewenthalten. Diese Klasse Standard enthält ein Wörterbuch von Objekten vom Typ während FingerPaintPolyline der Zeit, in der 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 FingerPaintPolyline mit einem bestimmten Finger verbundenen Informationen abrufen.

Die FingerPaintCanvasView Klasse Standard enthält auch ein List Objekt für die polylines, die abgeschlossen wurden:

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

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

FingerPaintCanvasView überschreibt zwei methoden, die durch View: OnDraw und OnTouchEvent. In seiner OnDraw Überschreibung zeichnet die Ansicht die fertigen 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 für mehrere Ereignisse konsistent. Aus diesem Grund verwenden Sie den pointerIndex Zeigerwert id aus der GetPointerId Methode. 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 mit Multitouch arbeiten, hat die Action Eigenschaft einen Wert für MotionEventsAction.Down den ersten Finger, um den Bildschirm zu berühren, und dann werte von Pointer2Down und Pointer3Down als zweiter und dritter Finger auch den Bildschirm. Wenn der vierte und fünfte Finger Kontakt herstellen, weist die Action Eigenschaft numerische Werte auf, die nicht einmal Mitgliedern der MotionEventsAction Enumeration entsprechen! Sie müssen die Werte von Bitkennzeichnungen in den Werten untersuchen, um die Bedeutung zu interpretieren.

Ebenso wie die Finger den Kontakt mit dem Bildschirm verlassen, weist die Action Eigenschaft Werte für Pointer2UpPointer3Up die zweiten und dritten Finger und Up für den ersten Finger auf.

Die ActionMasked Eigenschaft verwendet weniger Werte, da sie zusammen mit der ActionIndex Eigenschaft verwendet werden soll, um zwischen mehreren Fingern zu unterscheiden. Wenn Finger den Bildschirm berühren, kann die Eigenschaft nur für den ersten Finger und PointerDown für nachfolgende Finger gleich MotionEventActions.Down sein. Wenn die Finger den Bildschirm verlassen, ActionMasked weist die Werte Pointer1Up für die nachfolgenden Finger und Up für den ersten Finger auf.

Bei Verwendung ActionMaskedwird zwischen ActionIndex den nachfolgenden Fingern unterschieden, um den Bildschirm zu berühren und den Bildschirm zu verlassen. In der Regel müssen Sie diesen Wert jedoch nicht verwenden, außer als Argument für andere Methoden im MotionEvent Objekt. Bei 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 bestimmten Ereignissen Fingern zuzuordnen.

Die OnTouchEvent Außerkraftsetzung im Beispiel verarbeitet die MotionEventActions.Down Ereignisse und PointerDown Ereignisse 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 die pointerIndex Position des Fingers in der Ansicht auch verwendet wird. Alle Fingereingabeinformationen sind dem pointerIndex Wert zugeordnet. Die id Finger werden eindeutig über mehrere Nachrichten hinweg identifiziert, sodass sie zum Erstellen des Wörterbucheintrags verwendet werden.

Ebenso behandelt die OnTouchEvent Überschreibung auch die MotionEventActions.Up und Pointer1Up identische Überschreibung, indem die abgeschlossene Polylinie in die completedPolylines Auflistung übertragen wird, sodass sie während der OnDraw Überschreibung gezeichnet werden können. Der Code entfernt außerdem 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;
    }
    // ...        
}

Jetzt für den schwierigen Teil.

Zwischen den Down- und Up-Ereignissen gibt es in der Regel viele MotionEventActions.Move Ereignisse. Diese werden in einem einzigen Aufruf OnTouchEventgebündelt und müssen anders behandelt werden als die Down Ereignisse Up . 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 durchlaufen und dann einen 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 von Verarbeitung ermöglicht es dem Beispiel, einzelne Finger nachzuverfolgen und die Ergebnisse auf dem Bildschirm zu zeichnen:

Beispielfoto aus FingerPaint

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