Gestire i gesti multi-touch

Prova la funzionalità Scrivi
Jetpack Compose è il toolkit per l'interfaccia utente consigliato per Android. Scopri come utilizzare il tocco e l'input in Compose.

Un gesto multi-touch si verifica quando più cursori (dita) toccano lo schermo contemporaneamente. Questo documento descrive come rilevare i gesti che coinvolgono più cursori.

Monitorare più cursori

Quando più cursori toccano lo schermo contemporaneamente, il sistema genera i seguenti eventi tocco:

  • ACTION_DOWN: inviata quando il primo cursore tocca lo schermo. Questo avvia il gesto. I dati del puntatore per questo puntatore sono sempre all'indice 0 in MotionEvent.
  • ACTION_POINTER_DOWN: viene inviato quando altri cursori entrano nella schermata dopo il primo. Puoi ottenere l'indice del cursore che è appena sceso utilizzando getActionIndex().
  • ACTION_MOVE: viene inviato quando si verifica una modifica in un gesto, che coinvolge un numero qualsiasi di cursori.
  • ACTION_POINTER_UP: inviato quando un cursore non principale si sposta verso l'alto. Puoi ottenere l'indice del cursore che è appena salito utilizzando getActionIndex().
  • ACTION_UP: inviato quando l'ultimo cursore esce dallo schermo.
  • ACTION_CANCEL: indica che l'intero gesto, inclusi tutti i cursori, viene annullato.

Gesti di inizio e fine

Un gesto è una serie di eventi che inizia con un evento ACTION_DOWN e termina con un evento ACTION_UP o ACTION_CANCEL. È attivo un solo gesto alla volta. Le azioni GIU, MUOVI, SU e ANNULLA si applicano all'intero gesto. Ad esempio, un evento con ACTION_MOVE può indicare un movimento per tutti i cursori in basso in quel momento.

Monitora i cursori

Utilizza l'indice e l'ID del cursore per tenere traccia delle posizioni dei singoli cursori all'interno di un MotionEvent.

  • Indice: un MotionEvent memorizza le informazioni del puntatore in un array. L'indice di un puntatore è la sua posizione all'interno di questo array. La maggior parte dei metodi MotionEvent prende come parametro l'indice del cursore anziché l'ID del cursore.
  • ID: ogni cursore ha anche una mappatura dell'ID che rimane costante tra gli eventi di tocco per consentire il monitoraggio di un singolo cursore nell'intero gesto.

I singoli cursori vengono visualizzati all'interno di un evento di movimento in un ordine non definito. Pertanto, l'indice di un cursore può cambiare da un evento all'altro, ma l'ID cursore di un cursore è garantito per rimanere costante finché il cursore rimane attivo. Utilizza il metodo getPointerId() per ottenere l'ID di un cursore in modo da monitorarlo in tutti gli eventi di movimento successivi in un gesto. Poi, per gli eventi di movimento successivi, utilizza il metodo findPointerIndex() per ottenere l'indice del cursore per un determinato ID cursore nell'evento di movimento. Ad esempio:

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

Per supportare più cursori tocco, puoi memorizzare nella cache tutti i cursori attivi con i relativi ID al momento dell'evento ACTION_POINTER_DOWN e ACTION_DOWN. Rimuovi i puntatori dalla cache agli eventi ACTION_POINTER_UP e ACTION_UP. Potresti trovare utili questi ID memorizzati nella cache per gestire correttamente altri eventi di azione. Ad esempio, durante l'elaborazione di un evento ACTION_MOVE, trova l'indice di ogni ID cursore attivo memorizzato nella cache, recupera le coordinate del cursore utilizzando le funzioni getX() e getY(), quindi confronta queste coordinate con quelle memorizzate nella cache per scoprire quali cursori si sono spostati.

Utilizza la funzione getActionIndex() solo con gli eventi ACTION_POINTER_UP e ACTION_POINTER_DOWN. Non utilizzare questa funzione con gli eventi ACTION_MOVE, poiché restituisce sempre 0.

Recuperare le azioni MotionEvent

Utilizza il metodo getActionMasked() o la versione di compatibilità MotionEventCompat.getActionMasked() per recuperare l'azione di un MotionEvent. A differenza del metodo getAction() precedente, getActionMasked() è progettato per funzionare con più cursori. Restituisce l'azione senza gli indici del cursore. Per le azioni con un indice del puntatore valido, utilizza getActionIndex() per restituire l'indice degli indicatori associati all'azione, come mostrato nello snippet seguente:

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 "";
}
Figura 1. Schemi di disegno multi-touch.

Risorse aggiuntive

Per ulteriori informazioni sugli eventi di input, consulta i seguenti riferimenti: