Esta lição descreve como rastrear o movimento em eventos de toque.
Um novo onTouchEvent()
é acionado com um evento ACTION_MOVE
sempre que a posição, pressão ou tamanho do contato de toque atual muda. Conforme descrito em Detectar gestos comuns, todos esses eventos são registrados no parâmetro MotionEvent
de onTouchEvent()
.
Como o toque com o dedo nem sempre é a forma mais precisa de interação, a detecção de eventos de toque geralmente se baseia mais no movimento do que no simples contato. Para ajudar os apps a diferenciar gestos de movimento (como deslizar) de gestos sem movimento (como um único toque), o Android inclui a noção de "tolerância de toque". A tolerância de toque refere-se à distância em pixels que o toque do usuário pode percorrer antes que o gesto seja interpretado como um gesto de movimento. Para ver mais discussões sobre esse assunto, consulte Gerenciar eventos de toque em um ViewGroup.
Existem várias maneiras de rastrear o movimento em um gesto, dependendo das necessidades do seu aplicativo. Por exemplo:
- As posições inicial e final de um ponteiro (por exemplo, mova um objeto na tela do ponto A para o ponto B).
- A direção do percurso do ponteiro, conforme determinado pelas coordenadas x e y.
- O histórico. Você pode encontrar o tamanho do histórico de um gesto chamando o método
MotionEvent
getHistorySize()
. É possível saber as posições, tamanhos, horários e pressões de cada evento histórico usando os métodosgetHistorical<Value>
do evento de movimento. O histórico é útil ao renderizar uma trilha do dedo do usuário, bem como para desenhar com o toque. Consulte a referência doMotionEvent
para ver mais detalhes. - A velocidade do ponteiro conforme ele se move na tela.
Confira os seguintes recursos relacionados:
Velocidade de rastreamento
Você pode ter um gesto de movimento baseado apenas na distância e/ou direção que o ponteiro percorre. No entanto, a velocidade geralmente é um fator determinante para o rastreamento das características de um gesto ou até para identificar se o gesto realmente ocorreu. Para facilitar o cálculo da velocidade, o Android oferece a classe VelocityTracker
. A VelocityTracker
ajuda a rastrear a velocidade dos eventos de toque. Isso é útil quando a velocidade faz parte dos critérios do gesto, como no caso de uma rolagem rápida.
Este é um exemplo simples que ilustra o objetivo dos métodos na API VelocityTracker
:
Kotlin
private const val DEBUG_TAG = "Velocity" class MainActivity : Activity() { private var mVelocityTracker: VelocityTracker? = null override fun onTouchEvent(event: MotionEvent): Boolean { when (event.actionMasked) { MotionEvent.ACTION_DOWN -> { // Reset the velocity tracker back to its initial state. mVelocityTracker?.clear() // If necessary retrieve a new VelocityTracker object to watch the // velocity of a motion. mVelocityTracker = mVelocityTracker ?: VelocityTracker.obtain() // Add a user's movement to the tracker. mVelocityTracker?.addMovement(event) } MotionEvent.ACTION_MOVE -> { mVelocityTracker?.apply { val pointerId: Int = event.getPointerId(event.actionIndex) addMovement(event) // When you want to determine the velocity, call // computeCurrentVelocity(). Then call getXVelocity() // and getYVelocity() to retrieve the velocity for each pointer ID. computeCurrentVelocity(1000) // Log velocity of pixels per second // Best practice to use VelocityTrackerCompat where possible. Log.d("", "X velocity: ${getXVelocity(pointerId)}") Log.d("", "Y velocity: ${getYVelocity(pointerId)}") } } MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> { // Return a VelocityTracker object back to be re-used by others. mVelocityTracker?.recycle() mVelocityTracker = null } } return true } }
Java
public class MainActivity extends Activity { private static final String DEBUG_TAG = "Velocity"; ... private VelocityTracker mVelocityTracker = null; @Override public boolean onTouchEvent(MotionEvent event) { int index = event.getActionIndex(); int action = event.getActionMasked(); int pointerId = event.getPointerId(index); switch(action) { case MotionEvent.ACTION_DOWN: if(mVelocityTracker == null) { // Retrieve a new VelocityTracker object to watch the // velocity of a motion. mVelocityTracker = VelocityTracker.obtain(); } else { // Reset the velocity tracker back to its initial state. mVelocityTracker.clear(); } // Add a user's movement to the tracker. mVelocityTracker.addMovement(event); break; case MotionEvent.ACTION_MOVE: mVelocityTracker.addMovement(event); // When you want to determine the velocity, call // computeCurrentVelocity(). Then call getXVelocity() // and getYVelocity() to retrieve the velocity for each pointer ID. mVelocityTracker.computeCurrentVelocity(1000); // Log velocity of pixels per second // Best practice to use VelocityTrackerCompat where possible. Log.d("", "X velocity: " + mVelocityTracker.getXVelocity(pointerId)); Log.d("", "Y velocity: " + mVelocityTracker.getYVelocity(pointerId)); break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: // Return a VelocityTracker object back to be re-used by others. mVelocityTracker.recycle(); break; } return true; } }
Usar captura de ponteiro
Alguns apps, como jogos, áreas de trabalho remotas e clientes de virtualização, são significativamente beneficiados com o controle do ponteiro do mouse. A captura de ponteiro é um recurso disponível no Android 8.0 (API nível 26) e posterior que proporciona esse controle enviando todos os eventos de mouse para uma visualização focalizada no seu app.
Solicitar captura de ponteiro
Uma visualização no seu app só pode solicitar a captura de ponteiro quando a hierarquia da visualização que a contém está em foco. Por isso, solicite a captura de ponteiro quando houver uma ação específica do usuário na visualização, como durante um evento onClick()
ou no manipulador de eventos onWindowFocusChanged()
da sua atividade.
Para solicitar a captura de ponteiro, chame o método requestPointerCapture()
na visualização. O exemplo de código a seguir mostra como solicitar a captura de ponteiro quando o usuário clica em uma visualização:
Kotlin
fun onClick(view: View) { view.requestPointerCapture() }
Java
@Override public void onClick(View view) { view.requestPointerCapture(); }
Quando a solicitação para capturar o ponteiro for realizada, o Android chamará onPointerCaptureChange(true)
.
O sistema enviará os eventos de mouse para a visualização focalizada no seu app, desde que ela esteja na mesma hierarquia da visualização que solicitou a captura. Outros apps deixarão de receber eventos de mouse até a captura ser liberada, incluindo os eventos ACTION_OUTSIDE
. O Android enviará eventos de ponteiro de outras fontes além do mouse normalmente, mas o ponteiro do mouse não estará mais visível.
Processar eventos de ponteiro capturados
Depois que uma visualização recebe a captura do ponteiro, o Android começa a enviar os eventos de mouse. Sua visualização focalizada pode processar os eventos realizando uma das tarefas a seguir:
- Se você está usando uma visualização personalizada, modifique
onCapturedPointerEvent(MotionEvent)
. - Caso contrário, registre um
OnCapturedPointerListener
.
O exemplo de código a seguir mostra como implementar onCapturedPointerEvent(MotionEvent)
:
Kotlin
override fun onCapturedPointerEvent(motionEvent: MotionEvent): Boolean { // Get the coordinates required by your app val verticalOffset: Float = motionEvent.y // Use the coordinates to update your view and return true if the event was // successfully processed return true }
Java
@Override public boolean onCapturedPointerEvent(MotionEvent motionEvent) { // Get the coordinates required by your app float verticalOffset = motionEvent.getY(); // Use the coordinates to update your view and return true if the event was // successfully processed return true; }
O exemplo de código a seguir mostra como registrar um OnCapturedPointerListener
:
Kotlin
myView.setOnCapturedPointerListener { view, motionEvent -> // Get the coordinates required by your app val horizontalOffset: Float = motionEvent.x // Use the coordinates to update your view and return true if the event was // successfully processed true }
Java
myView.setOnCapturedPointerListener(new View.OnCapturedPointerListener() { @Override public boolean onCapturedPointer (View view, MotionEvent motionEvent) { // Get the coordinates required by your app float horizontalOffset = motionEvent.getX(); // Use the coordinates to update your view and return true if the event was // successfully processed return true; } });
Independentemente de você usar uma visualização personalizada ou registrar um listener, sua visualização recebe um MotionEvent
com coordenadas de ponteiro que especificam movimentos relativos, como deltas X/Y, semelhantes às coordenadas enviadas por um dispositivo trackball. Você pode recuperar as coordenadas usando getX()
e getY()
.
Liberar captura de ponteiro
A visualização no seu app pode liberar a captura do ponteiro chamando releasePointerCapture()
, conforme mostrado neste exemplo de código:
Kotlin
override fun onClick(view: View) { view.releasePointerCapture() }
Java
@Override public void onClick(View view) { view.releasePointerCapture(); }
O sistema pode retirar a captura da visualização sem que você chame releasePointerCapture()
de maneira explícita. Normalmente, isso acontece porque a hierarquia relacionada à visualização que solicitou a visualização perdeu o foco.