Acompanhar movimentos de toque e do cursor

Testar o Compose
O Jetpack Compose é o kit de ferramentas de interface recomendado para Android. Aprenda a usar o toque e a entrada no Compose.

Esta lição descreve como rastrear o movimento em eventos de toque.

Um novo onTouchEvent() é acionado com uma ACTION_MOVE evento sempre que a posição, pressão ou tamanho do contato de toque atual mudar. Conforme descritos em Detectar gestos comuns, todos esses eventos são registrados 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 em um simples contato. Para ajudar os apps a distinguir entre gestos de movimento (como deslizar) e 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 vagar antes de o gesto ser interpretado como um gesto de movimento. Para mais informações sobre este tópico, consulte Gerenciar eventos de toque em um ViewGroup.

Há várias maneiras de acompanhar o movimento em um gesto, dependendo as necessidades do seu aplicativo. Confira alguns exemplos:

  • As posições inicial e final de um ponteiro, como mover um objeto na tela do ponto A ao ponto B.
  • A direção em que o ponteiro está viajando, conforme determinado pelos atributos X e Y. coordenadas.
  • 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 um eventos históricos usando a função getHistorical<Value> métodos. O histórico é útil para renderizar um rastro do dedo do usuário, como quanto ao desenho por toque. Consulte a referência do MotionEvent para mais detalhes.
  • A velocidade do ponteiro conforme ele se move pela tela touchscreen.

Confira estes recursos relacionados:

Velocidade de rastreamento

É possível ter um gesto de movimento baseado na distância ou direção o cursor percorre. No entanto, a velocidade costuma ser um fator determinante no rastreamento as características de um gesto ou decidir se ele ocorreu. Para fazer o cálculo da velocidade mais fácil, o Android fornece a classe VelocityTracker. VelocityTracker ajuda a rastrear a velocidade dos eventos de toque. Isso é útil para gestos em que a velocidade faz parte dos critérios do gesto, como rapidamente.

Aqui está um exemplo que ilustra a finalidade 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. It's 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. It's 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 aplicativos, como jogos e clientes de área de trabalho remota e virtualização, se beneficiam controle o ponteiro do mouse. A captura do ponteiro é um recurso disponíveis no Android 8.0 (API de nível 26) e versões posteriores que oferecem esse controle entregar todos os eventos do mouse para uma visualização focada no app;

Solicitar captura de ponteiro

Uma visualização no app pode solicitar a captura de ponteiro somente quando a hierarquia de visualizações que contém o foco. Por isso, solicite a captura do ponteiro quando houver um ação específica do usuário na visualização, como durante um onClick() ou no onWindowFocusChanged() manipulador de eventos da sua atividade.

Para solicitar a captura de ponteiro, chame o método requestPointerCapture() na visualização. O exemplo de código abaixo mostra como solicitar o ponteiro capturar 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 bem-sucedida, o Android vai chamar onPointerCaptureChange(true) O sistema entrega os eventos de mouse para a visualização focada no seu app, desde que ele está na mesma hierarquia de visualização que solicitou a captura. Outra opção os aplicativos param de receber eventos de mouse até que a captura seja liberada, incluindo ACTION_OUTSIDE eventos. O Android entrega eventos de ponteiro de outras origens além do mouse, como normal, mas o ponteiro do mouse não estará mais visível.

Processar eventos de ponteiro capturados

Depois que uma visualização adquire a captura do ponteiro, o Android mostra o eventos do mouse. A visualização em foco pode lidar com os eventos realizando um dos as seguintes tarefas:

O exemplo de código abaixo 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 is
    // 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 is
  // successfully processed.
  return true;
}

O exemplo de código a seguir mostra como registrar 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 is
    // 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 is
    // successfully processed.
    return true;
  }
});

Se você usar uma visualização personalizada ou registrar um listener, sua visualização receberá uma MotionEvent por coordenadas de ponteiro que especificam movimentos relativos, como X. ou deltas Y, semelhantes às coordenadas entregues 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 tirar a captura da visualização sem você explicitamente chamar releasePointerCapture(), geralmente porque a hierarquia de visualização que contém a visualização que solicita a captura perde o foco.