Um gesto de toque ocorre quando um usuário coloca um ou mais dedos na tela touchscreen, e o app interpreta esse padrão de toques como um gesto. A detecção de gestos tem duas fases:
- Coletando dados de eventos de toque.
- Interpretar os dados para determinar se eles atendem aos critérios para os gestos com que o app oferece suporte.
Classes do AndroidX
Os exemplos neste documento usam as classes
GestureDetectorCompat
e
MotionEventCompat
. Essas classes estão na biblioteca
AndroidX. Use as classes do AndroidX sempre que possível para oferecer compatibilidade com
dispositivos anteriores.
MotionEventCompat
não substitui a
classe
MotionEvent
. Em vez disso, ele oferece métodos utilitários estáticos para onde você transmite o objeto
MotionEvent
para receber a ação associada a esse
evento.
Colete dados
Quando um usuário coloca um ou mais dedos na tela, isso aciona o
callback
onTouchEvent()
na visualização que recebe os eventos de toque. Para cada sequência de eventos de
toque, como posição, pressão, tamanho e adição de outro
dedo, que é identificada como um gesto, onTouchEvent()
é
acionada 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 do dedo ou dos dedos e termina
capturando o evento final do último dedo do usuário saindo da tela.
Durante essa interação, a MotionEvent
entregue a
onTouchEvent()
fornece os detalhes de cada interação. Seu app
pode usar os dados fornecidos pelo MotionEvent
para determinar se um
gesto relevante acontece.
Capturar eventos de toque para uma atividade ou visualização
Para interceptar eventos de toque em uma Activity
ou
View
, substitua o callback onTouchEvent()
.
O snippet de código abaixo usa
getAction()
para extrair a ação que o usuário realiza do parâmetro event
.
Isso fornece os dados brutos necessários para determinar se ocorre um gesto
importante.
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); } }
Esse código produz mensagens como as seguintes no Logcat, conforme o usuário toca, toca, mantém pressionada e arrasta:
GESTURES D Action was DOWN GESTURES D Action was UP GESTURES D Action was MOVE
No caso de gestos personalizados, você pode fazer seu próprio processamento nesses eventos para
determinar se eles representam um gesto que você precisa processar. No entanto, caso o
app use gestos comuns, como tocar duas vezes, tocar e pressionar, deslizar rapidamente e assim por diante,
você poderá usar a
classe
GestureDetector
. GestureDetector
facilita a detecção de gestos comuns
sem processar os eventos de toque individuais por conta própria. Isso é
discutido mais detalhadamente 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()
. Isso possibilita a detecção de eventos de toque sem subclassificar uma
View
existente, conforme mostrado neste 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 sequência 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á substituir
onTouchEvent()
, conforme descrito anteriormente.
Detectar gestos
O Android oferece a classe GestureDetector
para detectar gestos
comuns. Alguns dos gestos com suporte incluem
onDown()
,
onLongPress()
e
onFling()
.
Você pode usar GestureDetector
com o
método onTouchEvent()
descrito anteriormente.
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 o objeto
GestureDetector
receba eventos, substitua o método
onTouchEvent()
da visualização ou da atividade e transmita todos os eventos
observados para a instância do detector.
No snippet abaixo, um valor de retorno de true
dos
métodos on<TouchEvent>
individuais indica que o
evento de toque é processado. Um valor de retorno de false
transmite eventos
pela pilha de visualização até que o toque seja processado.
Se você executar o snippet abaixo em um app de teste, poderá ter uma ideia de como
as ações são acionadas quando você interage com a tela sensível ao toque e qual é
o conteúdo do MotionEvent
para cada evento de toque. Depois, você verá quantos dados estão sendo gerados para 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ê quiser processar apenas alguns gestos, estenda
GestureDetector.SimpleOnGestureListener
em vez de implementar a interface
GestureDetector.OnGestureListener
.
GestureDetector.SimpleOnGestureListener
fornece uma
implementação para todos os
métodos on<TouchEvent>
retornando
false
para todos eles. Isso permite substituir apenas os métodos que
você considera importantes. Por exemplo, o snippet de código a seguir cria uma classe que estende
GestureDetector.SimpleOnGestureListener
e modifica
onFling()
e onDown()
.
Quer você use GestureDetector.OnGestureListener
ou
GestureDetector.SimpleOnGestureListener
, é uma prática recomendada
implementar um método onDown()
que retorne true
. Isso
acontece 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
supõe que você quer ignorar o restante do gesto, e os outros métodos de
GestureDetector.OnGestureListener
não serão chamados. Isso pode causar
problemas inesperados no app. Retorne false
de
onDown()
apenas 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){ 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; } } }
Outros recursos
- Visão geral dos eventos de entrada
- Visão geral dos sensores
- Tornar uma visualização personalizada interativa