Rechazo de la palma con la pluma stylus

Ícono de calificación de cinco estrellas

Una pluma stylus puede ser una herramienta excepcionalmente productiva y creativa. Sin embargo, cuando los usuarios dibujan, escriben o interactúan con una app usando una pluma stylus, a veces tocan la pantalla con la palma de las manos. El evento táctil se puede informar a tu app antes de que el sistema reconozca el evento y lo descarte como un toque de la palma accidental.

Prácticas recomendadas

Tu app debe identificar eventos táctiles extraños e ignorarlos. En Jetpack Compose, puedes acceder al MotionEvent subyacente de Android desde un PointerEvent. Verifica el MotionEvent para ACTION_CANCEL, que indica que un gesto debe detenerse y es posible que deba revertirse. En Android 13 (nivel de API 33) y versiones posteriores, verifica la marca FLAG_CANCELED en los eventos ACTION_CANCEL y ACTION_POINTER_UP, que proporciona un indicador sólido de que el toque fue no intencional, como un toque de palma.

Ingredientes

  • Modifier.pointerInput(): Es el modificador de Compose que se usa para procesar la entrada del puntero.
  • PointerEvent: Representa un evento de puntero en Compose, que contiene uno o más cambios.
  • PointerEvent.motionEvent: Es una propiedad de extensión que proporciona acceso al MotionEvent subyacente de Android (puede ser nulo).
  • MotionEvent: Representa eventos táctiles y de movimiento. Contiene la información necesaria para determinar si se debe ignorar un evento.
  • ACTION_CANCEL: Es una constante MotionEvent que indica que se canceló un gesto. El gesto debe detenerse y es posible que deba revertirse.
  • ACTION_POINTER_UP: Es una constante MotionEvent que indica que un puntero distinto al primero se fue hacia arriba (es decir, renunció al contacto con la pantalla del dispositivo).
  • FLAG_CANCELED: Es una constante MotionEvent que indica que el puntero hacia arriba causó un evento táctil no intencional. Se agregó a los eventos ACTION_POINTER_UP y ACTION_CANCEL en Android 13 (nivel de API 33) y versiones posteriores.

Pasos

Examina los objetos MotionEvent que se enviaron a tu app. Usa las APIs de MotionEvent para determinar las características del evento:

  • Eventos de un solo puntero: Comprueba si se cumple el evento ACTION_CANCEL, que indica que el gesto debe detenerse. En Android 13 y versiones posteriores, también comprueba si se cumple el evento FLAG_CANCELED para confirmar que el toque fue accidental.
  • Eventos de varios punteros: En Android 13 y versiones posteriores, comprueba si se cumplen los eventos ACTION_POINTER_UP y FLAG_CANCELED para identificar toques no intencionales.

Para responder a estos eventos, detén el gesto y revierte los cambios temporales.

1. Adquiere objetos de eventos de movimiento

Usa Modifier.pointerInput para procesar la entrada del puntero en un elemento Composable. Dentro del alcance de entrada del puntero, usa awaitPointerEventScope y awaitPointerEvent para recibir eventos, y recupera el MotionEvent de Android subyacente con la propiedad motionEvent:

import androidx.compose.ui.input.pointer.pointerInput
    Box(
        modifier = Modifier
            .fillMaxSize()
            .pointerInput(Unit) {
                awaitPointerEventScope {
                    while (true) {
                        val event = awaitPointerEvent()
                        val motionEvent = event.motionEvent
                        if (motionEvent != null) {
                            // Process motion event.
                        }
                    }
                }
            }
    )

2. Determina la acción y las marcas de los eventos

Verifica MotionEvent para ACTION_CANCEL, que indica un evento de un solo puntero en todos los niveles de API. En Android 13 y versiones posteriores, busca el evento FLAG_CANCELED en ACTION_POINTER_UP:

import androidx.compose.ui.input.pointer.pointerInput
    Box(
        modifier = Modifier
            .fillMaxSize()
            .pointerInput(Unit) {
                awaitPointerEventScope {
                    while (true) {
                        val event = awaitPointerEvent()
                        val motionEvent = event.motionEvent ?: continue

                        when (motionEvent.actionMasked) {
                            MotionEvent.ACTION_CANCEL -> {
                                // Process canceled single-pointer motion event for all SDK versions.
                            }
                            MotionEvent.ACTION_POINTER_UP -> {
                                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU &&
                                    (motionEvent.flags and MotionEvent.FLAG_CANCELED) == MotionEvent.FLAG_CANCELED
                                ) {
                                    // Process canceled multi-pointer motion event for Android 13 and higher.
                                }
                            }
                        }
                    }
                }
            }
    )

3. Deshace el gesto

Una vez que hayas identificado el toque de la palma, puedes deshacer los efectos en pantalla del gesto.

Tu app debe mantener un historial de acciones del usuario para deshacer las entradas no deseadas, como los toques de la palma. Para ver un ejemplo, consulta Cómo implementar una app de dibujo básica en el codelab Cómo mejorar la compatibilidad con la pluma stylus en una app para Android.

Resultados

Ahora, tu app puede identificar y rechazar los toques de la palma de la mano para eventos de varios punteros en Android 13 y niveles posteriores de API, y para eventos de un solo puntero en todos los niveles de API.

Recursos adicionales

Para obtener más información, consulta lo siguiente: