Rileva gesti comuni

Prova Compose
Jetpack Compose è il toolkit UI consigliato per Android. Scopri come utilizzare il tocco e l'input in Compose.

Un gesto di tocco si verifica quando un utente posiziona uno o più dita sul touchscreen e la tua app interpreta questo pattern di tocchi come un gesto. Il rilevamento dei gesti prevede due fasi:

  1. Raccolta dei dati degli eventi touch.
  2. Interpretazione dei dati per determinare se soddisfano i criteri per i gesti supportati dalla tua app.

Classi AndroidX

Gli esempi in questo documento utilizzano le classi GestureDetectorCompat e MotionEventCompat. Queste classi si trovano nella libreria AndroidX. Utilizza le classi AndroidX, se possibile, per garantire la compatibilità con i dispositivi precedenti. MotionEventCompat non sostituisce la MotionEvent classe. Fornisce invece metodi di utilità statici a cui passi l'oggetto MotionEvent per ricevere l'azione associata a quell'evento.

Raccogli i dati

Quando un utente posiziona uno o più dita sullo schermo, questo attiva il callback onTouchEvent() nella visualizzazione che riceve gli eventi di tocco. Per ogni sequenza di eventi di tocco, ad esempio posizione, pressione, dimensione e aggiunta di un altro dito, identificata come gesto, onTouchEvent() viene attivato più volte.

Il gesto inizia quando l'utente tocca lo schermo per la prima volta, continua mentre il sistema monitora la posizione del dito o delle dita dell'utente e termina con l'acquisizione dell'evento finale dell'ultimo dito dell'utente che lascia lo schermo. Durante questa interazione, MotionEvent fornito a onTouchEvent() fornisce i dettagli di ogni interazione. La tua app può utilizzare i dati forniti da MotionEvent per determinare se si verifica un gesto che le interessa.

Acquisisci gli eventi di tocco per un'attività o una visualizzazione

Per intercettare gli eventi di tocco in un Activity o View, esegui l'override del callback onTouchEvent().

Il seguente snippet di codice utilizza getAction() per estrarre l'azione eseguita dall'utente dal parametro event. In questo modo ottieni i dati non elaborati necessari per determinare se si verifica un gesto che ti interessa.

Kotlin

class MainActivity : Activity() {
    ...
    // This example shows an Activity. You can use the same approach if you are 
    // subclassing a View.
    override fun onTouchEvent(event: MotionEvent): Boolean {
        return when (event.action) {
            MotionEvent.ACTION_DOWN -> {
                Log.d(DEBUG_TAG, "Action was DOWN")
                true
            }
            MotionEvent.ACTION_MOVE -> {
                Log.d(DEBUG_TAG, "Action was MOVE")
                true
            }
            MotionEvent.ACTION_UP -> {
                Log.d(DEBUG_TAG, "Action was UP")
                true
            }
            MotionEvent.ACTION_CANCEL -> {
                Log.d(DEBUG_TAG, "Action was CANCEL")
                true
            }
            MotionEvent.ACTION_OUTSIDE -> {
                Log.d(DEBUG_TAG, "Movement occurred outside bounds of current screen element")
                true
            }
            else -> super.onTouchEvent(event)
        }
    }
}

Java

public class MainActivity extends Activity {
...
// This example shows an Activity. You can use the same approach if you are
// subclassing a View.
@Override
public boolean onTouchEvent(MotionEvent event){
    switch(event.getAction()) {
        case (MotionEvent.ACTION_DOWN) :
            Log.d(DEBUG_TAG,"Action was DOWN");
            return true;
        case (MotionEvent.ACTION_MOVE) :
            Log.d(DEBUG_TAG,"Action was MOVE");
            return true;
        case (MotionEvent.ACTION_UP) :
            Log.d(DEBUG_TAG,"Action was UP");
            return true;
        case (MotionEvent.ACTION_CANCEL) :
            Log.d(DEBUG_TAG,"Action was CANCEL");
            return true;
        case (MotionEvent.ACTION_OUTSIDE) :
            Log.d(DEBUG_TAG,"Movement occurred outside bounds of current screen element");
            return true;
        default :
            return super.onTouchEvent(event);
    }
}

Questo codice produce messaggi simili ai seguenti in Logcat quando l'utente tocca, tocca e tieni premuto e trascina:

GESTURES D   Action was DOWN
GESTURES D   Action was UP
GESTURES D   Action was MOVE

Per i gesti personalizzati, puoi quindi eseguire l'elaborazione di questi eventi per determinare se rappresentano un gesto che devi gestire. Tuttavia, se la tua app utilizza gesti comuni, come doppio tocco, tocco e tieni premuto, scorrimento e così via, puoi sfruttare la classe GestureDetector. GestureDetector semplifica il rilevamento dei gesti comuni senza dover elaborare personalmente i singoli eventi di tocco. Questo argomento viene approfondito in Rileva i gesti.

Acquisisci gli eventi di tocco per una singola visualizzazione

In alternativa a onTouchEvent(), puoi collegare un View.OnTouchListener oggetto a qualsiasi View oggetto utilizzando il setOnTouchListener() metodo. In questo modo è possibile ascoltare gli eventi di tocco senza creare una sottoclasse di una View esistente, come mostrato nell'esempio seguente:

Kotlin

findViewById<View>(R.id.my_view).setOnTouchListener { v, event ->
    // Respond to touch events.
    true
}

Java

View myView = findViewById(R.id.my_view);
myView.setOnTouchListener(new OnTouchListener() {
    public boolean onTouch(View v, MotionEvent event) {
        // Respond to touch events.
        return true;
    }
});

Fai attenzione a non creare un listener che restituisca false per l'evento ACTION_DOWN. In questo caso, il listener non viene chiamato per la sequenza di eventi ACTION_MOVE e ACTION_UP successiva. Questo perché ACTION_DOWN è il punto di partenza per tutti gli eventi di tocco.

Se stai creando una visualizzazione personalizzata, puoi eseguire l'override di onTouchEvent(), come descritto in precedenza.

Rileva i gesti

Android fornisce la classe GestureDetector per rilevare i gesti comuni. Alcuni dei gesti supportati includono onDown(), onLongPress() e onFling(). Puoi utilizzare GestureDetector insieme al metodo onTouchEvent() descritto in precedenza.

Rileva tutti i gesti supportati

Quando crei un'istanza di un oggetto GestureDetectorCompat, uno dei parametri che accetta è una classe che implementa l'interfaccia GestureDetector.OnGestureListener. GestureDetector.OnGestureListener invia una notifica agli utenti quando si verifica un evento touch specifico. Per consentire all'oggetto GestureDetector di ricevere eventi, esegui l'override del metodo onTouchEvent() della visualizzazione o dell'attività e passa tutti gli eventi osservati all'istanza del rilevatore.

Nel seguente snippet, un valore restituito di true dai singoli on<TouchEvent> metodi indica che l'evento di tocco viene gestito. Un valore restituito di false passa gli eventi attraverso lo stack di visualizzazione finché il tocco non viene gestito correttamente.

Se esegui il seguente snippet in un'app di test, puoi farti un'idea di come vengono attivate le azioni quando interagisci con il touchscreen e quali sono i contenuti di MotionEvent per ogni evento touch. Vedrai quindi quanti dati vengono generati per le interazioni semplici.

Kotlin

private const val DEBUG_TAG = "Gestures"

class MainActivity :
        Activity(),
        GestureDetector.OnGestureListener,
        GestureDetector.OnDoubleTapListener {

    private lateinit var mDetector: GestureDetectorCompat

    // Called when the activity is first created.
    public override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        // Instantiate the gesture detector with the
        // application context and an implementation of
        // GestureDetector.OnGestureListener.
        mDetector = GestureDetectorCompat(this, this)
        // Set the gesture detector as the double-tap
        // listener.
        mDetector.setOnDoubleTapListener(this)
    }

    override fun onTouchEvent(event: MotionEvent): Boolean {
        return if (mDetector.onTouchEvent(event)) {
            true
        } else {
            super.onTouchEvent(event)
        }
    }

    override fun onDown(event: MotionEvent): Boolean {
        Log.d(DEBUG_TAG, "onDown: $event")
        return true
    }

    override fun onFling(
            event1: MotionEvent,
            event2: MotionEvent,
            velocityX: Float,
            velocityY: Float
    ): Boolean {
        Log.d(DEBUG_TAG, "onFling: $event1 $event2")
        return true
    }

    override fun onLongPress(event: MotionEvent) {
        Log.d(DEBUG_TAG, "onLongPress: $event")
    }

    override fun onScroll(
            event1: MotionEvent,
            event2: MotionEvent,
            distanceX: Float,
            distanceY: Float
    ): Boolean {
        Log.d(DEBUG_TAG, "onScroll: $event1 $event2")
        return true
    }

    override fun onShowPress(event: MotionEvent) {
        Log.d(DEBUG_TAG, "onShowPress: $event")
    }

    override fun onSingleTapUp(event: MotionEvent): Boolean {
        Log.d(DEBUG_TAG, "onSingleTapUp: $event")
        return true
    }

    override fun onDoubleTap(event: MotionEvent): Boolean {
        Log.d(DEBUG_TAG, "onDoubleTap: $event")
        return true
    }

    override fun onDoubleTapEvent(event: MotionEvent): Boolean {
        Log.d(DEBUG_TAG, "onDoubleTapEvent: $event")
        return true
    }

    override fun onSingleTapConfirmed(event: MotionEvent): Boolean {
        Log.d(DEBUG_TAG, "onSingleTapConfirmed: $event")
        return true
    }

}

Java

public class MainActivity extends Activity implements
        GestureDetector.OnGestureListener,
        GestureDetector.OnDoubleTapListener{

    private static final String DEBUG_TAG = "Gestures";
    private GestureDetectorCompat mDetector;

    // Called when the activity is first created.
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // Instantiate the gesture detector with the
        // application context and an implementation of
        // GestureDetector.OnGestureListener.
        mDetector = new GestureDetectorCompat(this,this);
        // Set the gesture detector as the double-tap
        // listener.
        mDetector.setOnDoubleTapListener(this);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event){
        if (this.mDetector.onTouchEvent(event)) {
            return true;
        }
        return super.onTouchEvent(event);
    }

    @Override
    public boolean onDown(MotionEvent event) {
        Log.d(DEBUG_TAG,"onDown: " + event.toString());
        return true;
    }

    @Override
    public boolean onFling(MotionEvent event1, MotionEvent event2,
            float velocityX, float velocityY) {
        Log.d(DEBUG_TAG, "onFling: " + event1.toString() + event2.toString());
        return true;
    }

    @Override
    public void onLongPress(MotionEvent event) {
        Log.d(DEBUG_TAG, "onLongPress: " + event.toString());
    }

    @Override
    public boolean onScroll(MotionEvent event1, MotionEvent event2, float distanceX,
            float distanceY) {
        Log.d(DEBUG_TAG, "onScroll: " + event1.toString() + event2.toString());
        return true;
    }

    @Override
    public void onShowPress(MotionEvent event) {
        Log.d(DEBUG_TAG, "onShowPress: " + event.toString());
    }

    @Override
    public boolean onSingleTapUp(MotionEvent event) {
        Log.d(DEBUG_TAG, "onSingleTapUp: " + event.toString());
        return true;
    }

    @Override
    public boolean onDoubleTap(MotionEvent event) {
        Log.d(DEBUG_TAG, "onDoubleTap: " + event.toString());
        return true;
    }

    @Override
    public boolean onDoubleTapEvent(MotionEvent event) {
        Log.d(DEBUG_TAG, "onDoubleTapEvent: " + event.toString());
        return true;
    }

    @Override
    public boolean onSingleTapConfirmed(MotionEvent event) {
        Log.d(DEBUG_TAG, "onSingleTapConfirmed: " + event.toString());
        return true;
    }
}

Rileva un sottoinsieme di gesti supportati

Se vuoi elaborare solo alcuni gesti, puoi estendere GestureDetector.SimpleOnGestureListener anziché implementare l'interfaccia GestureDetector.OnGestureListener.

GestureDetector.SimpleOnGestureListener fornisce un' implementazione per tutti i on<TouchEvent> metodi restituendo false per tutti. In questo modo puoi eseguire l'override solo dei metodi che ti interessano. Ad esempio, il seguente snippet di codice crea una classe che estende GestureDetector.SimpleOnGestureListener ed esegue l'override di onFling() e onDown().

Indipendentemente dal fatto che utilizzi GestureDetector.OnGestureListener o GestureDetector.SimpleOnGestureListener, è una best practice implementare un metodo onDown() che restituisca true. Questo perché tutti i gesti iniziano con un messaggio onDown(). Se restituisci false da onDown(), come fa GestureDetector.SimpleOnGestureListener per impostazione predefinita, il sistema presuppone che tu voglia ignorare il resto del gesto e gli altri metodi di GestureDetector.OnGestureListener non vengono chiamati. Ciò potrebbe causare problemi imprevisti nella tua app. Restituisci false da onDown() solo se vuoi davvero ignorare un intero gesto.

Kotlin

private const val DEBUG_TAG = "Gestures"

class MainActivity : Activity() {

    private lateinit var mDetector: GestureDetectorCompat

    public override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        mDetector = GestureDetectorCompat(this, MyGestureListener())
    }

    override fun onTouchEvent(event: MotionEvent): Boolean {
        mDetector.onTouchEvent(event)
        return super.onTouchEvent(event)
    }

    private class MyGestureListener : GestureDetector.SimpleOnGestureListener() {

        override fun onDown(event: MotionEvent): Boolean {
            Log.d(DEBUG_TAG, "onDown: $event")
            return true
        }

        override fun onFling(
                event1: MotionEvent,
                event2: MotionEvent,
                velocityX: Float,
                velocityY: Float
        ): Boolean {
            Log.d(DEBUG_TAG, "onFling: $event1 $event2")
            return true
        }
    }
}

Java

public class MainActivity extends Activity {

    private GestureDetectorCompat mDetector;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mDetector = new GestureDetectorCompat(this, new MyGestureListener());
    }

    @Override
    public boolean onTouchEvent(MotionEvent event){
        if (this.mDetector.onTouchEvent(event)) {
              return true;
        }
        return super.onTouchEvent(event);
    }

    class MyGestureListener extends GestureDetector.SimpleOnGestureListener {
        private static final String DEBUG_TAG = "Gestures";

        @Override
        public boolean onDown(MotionEvent event) {
            Log.d(DEBUG_TAG,"onDown: " + event.toString());
            return true;
        }

        @Override
        public boolean onFling(MotionEvent event1, MotionEvent event2,
                float velocityX, float velocityY) {
            Log.d(DEBUG_TAG, "onFling: " + event1.toString() + event2.toString());
            return true;
        }
    }
}

Risorse aggiuntive