Un gesto di tocco si verifica quando un utente posiziona una o più dita sul touchscreen e la tua app interpreta questo schema di tocchi come un gesto. Il rilevamento dei gesti prevede due fasi:
- Raccolta di dati sugli eventi touch.
- 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. Se possibile, utilizza le classi di AndroidX per garantire la compatibilità con i dispositivi precedenti.
MotionEventCompat
non sostituisce la classe MotionEvent
. Piuttosto, fornisce metodi di utilità statici a cui passi l'oggetto MotionEvent
per ricevere l'azione associata all'evento.
Raccogliere i dati
Quando un utente posiziona una o più dita sullo schermo, viene attivato il callback onTouchEvent()
sulla visualizzazione che riceve gli eventi tocco. Per ogni sequenza di eventi tocco, ad esempio posizione, pressione, dimensioni e aggiunta di un altro dito, identificata come gesto, l'elemento onTouchEvent()
viene attivato più volte.
Il gesto inizia quando l'utente tocca per la prima volta lo schermo, continua mentre il sistema monitora la posizione del dito o delle dita dell'utente e termina catturando l'evento finale dell'ultimo dito dell'utente mentre lascia lo schermo.
Durante questa interazione, il MotionEvent
pubblicato in
onTouchEvent()
fornisce i dettagli di ogni interazione. L'app
può utilizzare i dati forniti da MotionEvent
per determinare se avviene
un gesto che gli interessa.
Acquisire eventi touch per un'attività o una visualizzazione
Per intercettare gli eventi tocco in Activity
o
View
, sostituisci il callback onTouchEvent()
.
Il seguente snippet di codice utilizza getAction()
per estrarre l'azione eseguita dall'utente dal parametro event
.
Questo fornisce 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 genera messaggi come il seguente in Logcat quando l'utente tocca, tocca, blocca e trascina:
GESTURES D Action was DOWN GESTURES D Action was UP GESTURES D Action was MOVE
Per i gesti personalizzati, puoi quindi elaborare personalmente questi eventi per determinare se rappresentano un gesto che devi gestire. Tuttavia, se l'app utilizza gesti comuni, come il doppio tocco, il tocco e la pressione, lo scorrimento e così via, puoi trarre vantaggio dalla classe GestureDetector
. GestureDetector
consente di rilevare più facilmente gesti
comuni senza elaborare autonomamente i singoli eventi di tocco. Questo argomento viene ulteriormente approfondito in Rileva gesti.
Acquisisci eventi touch per un'unica visualizzazione
In alternativa a onTouchEvent()
, puoi collegare un oggetto View.OnTouchListener
a qualsiasi oggetto View
utilizzando il metodo setOnTouchListener()
. In questo modo è possibile ascoltare gli eventi touch senza sottoclassificare un 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; } });
Presta attenzione se crei un listener che restituisca false
per
l'evento ACTION_DOWN
.
In questo caso, il listener non viene chiamato per le successive sequenze di eventi ACTION_MOVE
e ACTION_UP
. Questo perché ACTION_DOWN
è il punto di partenza per tutti
gli eventi touch.
Se stai creando una visualizzazione personalizzata, puoi sostituire
onTouchEvent()
, come descritto in precedenza.
Rileva gesti
Android fornisce la classe GestureDetector
per rilevare i gesti comuni. Tra i gesti supportati ci sono
onDown()
,
onLongPress()
e
onFling()
.
Puoi utilizzare GestureDetector
in combinazione con il
metodo onTouchEvent()
descritto in precedenza.
Rileva tutti i gesti supportati
Quando crei un'istanza di un oggetto GestureDetectorCompat
, uno dei parametri necessari è una classe che implementa l'interfaccia GestureDetector.OnGestureListener
. GestureDetector.OnGestureListener
avvisa gli utenti quando si verifica un
determinato evento tocco. Per fare in modo che l'oggetto GestureDetector
riceva eventi, sostituisci il metodo onTouchEvent()
della visualizzazione o dell'attività e trasmetti tutti gli eventi osservati all'istanza del rilevatore.
Nello snippet seguente, un valore restituito da true
dei singoli metodi on<TouchEvent>
indica che viene gestito l'evento touch. Un valore restituito false
trasmette gli eventi attraverso lo stack di visualizzazione finché non viene gestito correttamente il tocco.
Se esegui il seguente snippet in un'app di prova, puoi avere un'idea di come vengono attivate le azioni quando interagisci con il touchscreen e quali sono i contenuti dell'MotionEvent
per ogni evento touch. Puoi quindi vedere 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
invece di implementare l'interfaccia GestureDetector.OnGestureListener
.
GestureDetector.SimpleOnGestureListener
fornisce un'implementazione per tutti i metodi on<TouchEvent>
restituendo false
per tutti. In questo modo puoi ignorare solo i
metodi che ti interessano. Ad esempio, il seguente snippet di codice crea una classe che estende
GestureDetector.SimpleOnGestureListener
e sostituisce
onFling()
e onDown()
.
Che tu utilizzi GestureDetector.OnGestureListener
o GestureDetector.SimpleOnGestureListener
, una best practice consiste nell'implementare un metodo onDown()
che restituisca true
. Il motivo è che tutti i gesti iniziano con un messaggio onDown()
. Se
restituisci false
da onDown()
, come
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 nell'app. Restituisci solo false
da onDown()
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
- Panoramica degli eventi di input
- Panoramica dei sensori
- Rendere interattiva una visualizzazione personalizzata