Multi-Touch-Bewegungen

Compose ausprobieren
Jetpack Compose ist das empfohlene UI-Toolkit für Android. Informationen zur Verwendung von Touch- und Eingabefunktionen in Compose

Bei einer Multi-Touch-Geste tippen mehrere Zeiger (Finger) gleichzeitig auf den Bildschirm. In diesem Dokument wird beschrieben, wie Sie Gesten mit mehreren Zeigern erkennen.

Mehrere Zeiger verfolgen

Wenn mehrere Zeiger gleichzeitig auf den Bildschirm tippen, generiert das System die folgenden Touch-Ereignisse:

  • ACTION_DOWN: Wird gesendet, wenn der erste Zeiger auf den Bildschirm tippt. Dadurch wird die Geste gestartet. Die Zeigerdaten für diesen Zeiger befinden sich immer am Index 0 in MotionEvent.
  • ACTION_POINTER_DOWN: Wird gesendet, wenn nach dem ersten zusätzliche Zeiger auf den Bildschirm kommen. Mit getActionIndex() können Sie den Index des Zeigers abrufen, der gerade nach unten bewegt wurde.
  • ACTION_MOVE: Wird gesendet, wenn sich eine Touchgeste ändert, an der eine beliebige Anzahl von Zeigern beteiligt ist.
  • ACTION_POINTER_UP: Wird gesendet, wenn ein nicht primärer Zeiger nach oben geht. Mit getActionIndex() können Sie den Index des Zeigers abrufen, der gerade nach oben verschoben wurde.
  • ACTION_UP: Wird gesendet, wenn der letzte Zeiger den Bildschirm verlässt.
  • ACTION_CANCEL: Gibt an, dass die gesamte Geste, einschließlich aller Zeiger, abgebrochen wird.

Gesten zum Starten und Beenden

Eine Geste ist eine Reihe von Ereignissen, die mit einem ACTION_DOWN-Ereignis beginnt und entweder mit einem ACTION_UP- oder einem ACTION_CANCEL-Ereignis endet. Es kann immer nur eine Geste aktiv sein. Die Aktionen DOWN, MOVE, UP und CANCEL gelten für die gesamte Touchgeste. Ein Ereignis mit ACTION_MOVE kann beispielsweise darauf hinweisen, dass sich alle Zeiger zu diesem Zeitpunkt nach unten bewegen.

Zeiger im Blick behalten

Verwenden Sie den Index und die ID des Zeigers, um die Positionen der einzelnen Zeiger in einem MotionEvent zu verfolgen.

  • Index: Ein MotionEvent speichert Zeigerinformationen in einem Array. Der Index eines Zeigers ist seine Position in diesem Array. Bei den meisten MotionEvent-Methoden wird der Zeigerindex anstelle der Zeiger-ID als Parameter verwendet.
  • ID: Jeder Zeiger hat auch eine ID-Zuweisung, die über Touch-Events hinweg bestehen bleibt, sodass ein einzelner Zeiger über die gesamte Touchgeste hinweg verfolgt werden kann.

Einzelne Zeiger werden in einem Motion-Event in einer nicht definierten Reihenfolge angezeigt. Der Index eines Zeigers kann sich also von einem Ereignis zum nächsten ändern, die Zeiger-ID eines Zeigers bleibt jedoch garantiert konstant, solange der Zeiger aktiv ist. Mit der Methode getPointerId() können Sie die ID eines Zeigers abrufen, um ihn bei allen nachfolgenden Bewegungsereignissen in einer Geste zu verfolgen. Verwenden Sie dann für nachfolgende Motion-Events die Methode findPointerIndex(), um den Zeigerindex für eine bestimmte Zeiger-ID in diesem Motion-Event abzurufen. Beispiel:

Kotlin

private var mActivePointerId: Int = 0

override fun onTouchEvent(event: MotionEvent): Boolean {
    ...
    // Get the pointer ID.
    mActivePointerId = event.getPointerId(0)

    // ... Many touch events later...

    // Use the pointer ID to find the index of the active pointer
    // and fetch its position.
    val (x: Float, y: Float) = event.findPointerIndex(mActivePointerId).let { pointerIndex ->
        // Get the pointer's current position.
        event.getX(pointerIndex) to event.getY(pointerIndex)
    }
    ...
}

Java

private int mActivePointerId;

public boolean onTouchEvent(MotionEvent event) {
    ...
    // Get the pointer ID.
    mActivePointerId = event.getPointerId(0);

    // ... Many touch events later...

    // Use the pointer ID to find the index of the active pointer
    // and fetch its position.
    int pointerIndex = event.findPointerIndex(mActivePointerId);
    // Get the pointer's current position.
    float x = event.getX(pointerIndex);
    float y = event.getY(pointerIndex);
    ...
}

Um mehrere Touch-Pointer zu unterstützen, können Sie alle aktiven Pointer mit ihren IDs zum Zeitpunkt des jeweiligen ACTION_POINTER_DOWN- und ACTION_DOWN-Ereignisses zwischenspeichern. Entfernen Sie die Zeiger aus Ihrem Cache für die Ereignisse ACTION_POINTER_UP und ACTION_UP. Diese im Cache gespeicherten IDs können hilfreich sein, um andere Aktionsereignisse korrekt zu verarbeiten. Wenn Sie beispielsweise ein ACTION_MOVE-Ereignis verarbeiten, suchen Sie den Index für jede zwischengespeicherte aktive Zeiger-ID, rufen Sie die Koordinaten des Zeigers mit den Funktionen getX() und getY() ab und vergleichen Sie diese Koordinaten dann mit Ihren zwischengespeicherten Koordinaten, um herauszufinden, welche Zeiger sich bewegt haben.

Verwenden Sie die Funktion getActionIndex() nur mit ACTION_POINTER_UP- und ACTION_POINTER_DOWN-Ereignissen. Verwenden Sie diese Funktion nicht mit ACTION_MOVE-Ereignissen, da sie immer 0 zurückgibt.

MotionEvent-Aktionen abrufen

Verwenden Sie die Methode getActionMasked() oder die Kompatibilitätsversion MotionEventCompat.getActionMasked(), um die Aktion eines MotionEvent abzurufen. Im Gegensatz zur früheren getAction()-Methode ist getActionMasked() für die Verwendung mit mehreren Zeigern konzipiert. Die Aktion wird ohne die Zeigerindexe zurückgegeben. Verwenden Sie für Aktionen mit einem gültigen Zeigerindex getActionIndex(), um den Index der mit der Aktion verknüpften Zeiger zurückzugeben, wie im folgenden Snippet gezeigt:

Kotlin

val (xPos: Int, yPos: Int) = MotionEventCompat.getActionMasked(event).let { action ->
    Log.d(DEBUG_TAG, "The action is ${actionToString(action)}")
    // Get the index of the pointer associated with the action.
    MotionEventCompat.getActionIndex(event).let { index ->
        // The coordinates of the current screen contact, relative to
        // the responding View or Activity.
        MotionEventCompat.getX(event, index).toInt() to MotionEventCompat.getY(event, index).toInt()
    }
}

if (event.pointerCount > 1) {
    Log.d(DEBUG_TAG, "Multitouch event")

} else {
    // Single touch event.
    Log.d(DEBUG_TAG, "Single touch event")
}

...

// Given an action int, returns a string description.
fun actionToString(action: Int): String {
    return when (action) {
        MotionEvent.ACTION_DOWN -> "Down"
        MotionEvent.ACTION_MOVE -> "Move"
        MotionEvent.ACTION_POINTER_DOWN -> "Pointer Down"
        MotionEvent.ACTION_UP -> "Up"
        MotionEvent.ACTION_POINTER_UP -> "Pointer Up"
        MotionEvent.ACTION_OUTSIDE -> "Outside"
        MotionEvent.ACTION_CANCEL -> "Cancel"
        else -> ""
    }
}

Java

int action = MotionEventCompat.getActionMasked(event);
// Get the index of the pointer associated with the action.
int index = MotionEventCompat.getActionIndex(event);
int xPos = -1;
int yPos = -1;

Log.d(DEBUG_TAG,"The action is " + actionToString(action));

if (event.getPointerCount() > 1) {
    Log.d(DEBUG_TAG,"Multitouch event");
    // The coordinates of the current screen contact, relative to
    // the responding View or Activity.
    xPos = (int)MotionEventCompat.getX(event, index);
    yPos = (int)MotionEventCompat.getY(event, index);

} else {
    // Single touch event.
    Log.d(DEBUG_TAG,"Single touch event");
    xPos = (int)MotionEventCompat.getX(event, index);
    yPos = (int)MotionEventCompat.getY(event, index);
}
...

// Given an action int, returns a string description
public static String actionToString(int action) {
    switch (action) {

        case MotionEvent.ACTION_DOWN: return "Down";
	case MotionEvent.ACTION_MOVE: return "Move";
	case MotionEvent.ACTION_POINTER_DOWN: return "Pointer Down";
	case MotionEvent.ACTION_UP: return "Up";
	case MotionEvent.ACTION_POINTER_UP: return "Pointer Up";
	case MotionEvent.ACTION_OUTSIDE: return "Outside";
	case MotionEvent.ACTION_CANCEL: return "Cancel";
    }
    return "";
}
Abbildung 1. Zeichnungsmuster mit mehreren Touchpoints

Zusätzliche Ressourcen

Weitere Informationen zu Eingabeereignissen finden Sie in den folgenden Referenzen: