Um "gesto de toque" acontece quando um usuário coloca um ou mais dedos na tela, e seu aplicativo interpreta esse padrão de toques como um gesto específico. Existem duas fases correspondentes para a detecção de gestos:
- Coletar dados sobre eventos de toque.
- Interpretar os dados para verificar se eles atendem aos critérios de algum dos gestos com que o app é compatível.
Confira os seguintes recursos relacionados:
Classes da Biblioteca de Suporte
Os exemplos desta lição usam as classes GestureDetectorCompat
e MotionEventCompat
. Essas classes estão na Biblioteca de Suporte. Use as classes da Biblioteca de Suporte sempre que possível para oferecer compatibilidade com dispositivos com o Android 1.6 e mais recente. Observe que MotionEventCompat
não é um substituto para a classe MotionEvent
. Em vez disso, ele oferece métodos utilitários estáticos para os quais você transmite o objeto MotionEvent
a fim de receber a ação desejada associada a esse evento.
Coletar dados
Quando um usuário coloca um ou mais dedos na tela, aciona o callback onTouchEvent()
na visualização que recebeu os eventos de toque.
Para cada sequência de eventos de toque (posição, pressão, tamanho, adição de outro dedo etc.)
que é identificada como um gesto, onTouchEvent()
é acionado várias vezes.
O gesto começa quando o usuário toca na tela pela primeira vez, continua enquanto o sistema rastreia a posição dos dedos do usuário e termina com a captura do evento final dos dedos do usuário deixando a tela. Durante essa interação, o MotionEvent
exibido em onTouchEvent()
fornece os detalhes de cada interação. Seu app pode usar os dados fornecidos pelo MotionEvent
para determinar se um gesto é relevante para ele.
Capturar eventos de toque para uma atividade ou visualização
Para interceptar eventos de toque em uma atividade ou visualização, modifique o callback onTouchEvent()
.
O snippet a seguir usa getActionMasked()
para extrair a ação que o usuário executou do parâmetro event
. Essa ação disponibiliza os dados brutos necessários para determinar se um gesto relevante ocorreu:
Kotlin
class MainActivity : Activity() { ... // This example shows an Activity, but you would use the same approach if // you were subclassing a View. override fun onTouchEvent(event: MotionEvent): Boolean { val action: Int = MotionEventCompat.getActionMasked(event) return when (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, but you would use the same approach if // you were subclassing a View. @Override public boolean onTouchEvent(MotionEvent event){ int action = MotionEventCompat.getActionMasked(event); switch(action) { 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); } }
Você pode fazer o próprio processamento nesses eventos para determinar se ocorreu um gesto. Esse é o tipo de processamento necessário para um gesto personalizado. No entanto, se o app usa gestos comuns, como tocar duas vezes, tocar e manter pressionado, movimentar etc., você pode se beneficiar com a classe GestureDetector
. GestureDetector
facilita a detecção de gestos comuns sem que você precise processar os eventos de toque individuais por conta própria. Esse assunto é discutido abaixo em Detectar gestos.
Capturar eventos de toque para uma visualização única
Como alternativa a onTouchEvent()
, você pode anexar um objeto View.OnTouchListener
a qualquer objeto View
usando o método setOnTouchListener()
. Dessa forma, é possível ouvir eventos de toque sem subclassificar uma View
existente. Por exemplo:
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; } });
Cuidado ao criar um listener que retorne false
para o evento ACTION_DOWN
. Se você fizer isso, o listener não será chamado para a string de eventos ACTION_MOVE
e ACTION_UP
subsequente. Isso ocorre porque ACTION_DOWN
é o ponto de partida para todos os eventos de toque.
Se você estiver criando uma visualização personalizada, poderá modificar onTouchEvent()
, conforme descrito acima.
Detectar gestos
O Android oferece a classe GestureDetector
para a detecção de gestos comuns. Alguns dos gestos compatíveis incluem onDown()
, onLongPress()
, onFling()
entre outros. Você pode usar GestureDetector
em conjunto com o método onTouchEvent()
descrito acima.
Detectar todos os gestos compatíveis
Quando você instancia um objeto GestureDetectorCompat
, um dos parâmetros necessários é uma classe que implementa a interface GestureDetector.OnGestureListener
.
GestureDetector.OnGestureListener
notifica os usuários quando ocorre um evento de toque específico. Para que seu objeto GestureDetector
receba eventos, modifique o método onTouchEvent()
da visualização ou da atividade e transmita todos os eventos observados para a instância do detector.
No snippet a seguir, um valor de retorno de true
dos métodos on<TouchEvent>
individuais indica que você processou o evento de toque. Um valor de retorno de false
transmite os eventos pela pilha de visualização até que o toque seja processado.
Execute o snippet a seguir para ter uma ideia de como as ações são iniciadas quando você interage com a touchscreen e qual é o conteúdo do MotionEvent
para cada evento de toque. Você perceberá que uma grande quantidade de dados é gerada mesmo para as interações simples.
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; } }
Detectar um subconjunto de gestos compatíveis
Se você quer processar apenas alguns gestos, pode estender o GestureDetector.SimpleOnGestureListener
em vez de implementar a interface do GestureDetector.OnGestureListener
.
GestureDetector.SimpleOnGestureListener
fornece uma implementação para todos os métodos on<TouchEvent>
retornando false
para todos eles. Assim, você pode modificar apenas os métodos do seu interesse.
Por exemplo, o snippet abaixo cria uma classe que estende GestureDetector.SimpleOnGestureListener
e modifica onFling()
e onDown()
.
Quer você use ou não o GestureDetector.OnGestureListener
, implementar um método onDown()
que retorna true
é uma prática recomendada. Isso ocorre porque todos os gestos começam com uma mensagem onDown()
. Se você retornar false
de onDown()
, como GestureDetector.SimpleOnGestureListener
faz por padrão, o sistema presume que você quer ignorar o restante do gesto e os outros métodos de GestureDetector.OnGestureListener
nunca serão chamados.
Isso pode causar problemas inesperados no seu app. A única vez que é necessário retornar false
de onDown()
é se você realmente quiser ignorar um gesto inteiro.
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){ this.mDetector.onTouchEvent(event); 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; } } }