Cómo usar gestos de muñeca en Wear

Cuando no es conveniente usar una pantalla táctil, los gestos de muñeca pueden permitir interacciones rápidas con una mano en tu app.

Por ejemplo, un usuario puede desplazarse por las notificaciones con una mano mientras sostiene un vaso de agua con la otra. Entre otros casos de uso de gestos de muñeca, se incluyen los siguientes:

  • En una app para trotar, navegar a través de pantallas verticales que muestran los pasos dados, el tiempo transcurrido y el ritmo actual
  • En una app de viajes, desplazarse por la información sobre vuelos y puertas de embarque
  • En una app de noticias, desplazarse por los artículos

Para revisar los gestos de muñeca en un reloj, confirma que estén activados seleccionando Configuración > Funciones avanzadas > Gestos > Wrist Gestures On. Luego, selecciona Launch Tutorial para completar el instructivo sobre gestos en el reloj.

Nota: Agitar la muñeca representa el gesto de atrás o deshacer en todo el sistema y no está disponible para personalizar en las apps.

Los gestos de muñeca se pueden utilizar de las siguientes maneras, como se describe en esta guía:

Cada gesto de muñeca se asigna a una constante int de la clase KeyEvent, como se muestra en la siguiente tabla:

Gesto KeyEvent Descripción
Gira la muñeca hacia afuera rápidamente KEYCODE_NAVIGATE_NEXT Esta clave de código va al siguiente elemento.
Gira la muñeca hacia adentro rápidamente KEYCODE_NAVIGATE_PREVIOUS Esta clave de código va al elemento anterior.

Usa un diseño curvo que sea compatible con los gestos de muñeca

La clase WearableRecyclerView proporciona un diseño curvo para las listas y admite automáticamente los gestos de muñeca. Tiene acciones predefinidas para casos de gestos de muñeca cuando la vista está enfocada. Para obtener información sobre el uso de la clase WearableRecyclerView, consulta Cómo crear listas en Wear OS. Además, consulta la sección Prácticas recomendadas de esta guía.

Nota: La clase WearableRecyclerView reemplaza una clase obsoleta similar en la Biblioteca de compatibilidad para wearables.

Incluso si usas una WearableRecyclerView, es posible que quieras usar constantes de la clase KeyEvent. Las acciones predefinidas se pueden anular dividiendo en subclases WearableRecyclerView y volviendo a implementar la devolución de llamada onKeyDown(). El comportamiento se puede inhabilitar por completo mediante setEnableGestureNavigation(false). Para obtener más información, consulta Cómo controlar las acciones del teclado.

Usa directamente eventos clave

Puedes usar eventos de tecla fuera de WearableRecyclerView para activar nuevas acciones en respuesta a eventos gestuales. Es importante destacar que estos eventos de gestos se reconocen cuando un dispositivo está en modo activo y se entregan de la misma manera que todos los eventos de tecla.

Una clase que se relaciona con la interacción del usuario, como una View o una Activity, y que implementa KeyEvent.Callback puede escuchar eventos de tecla relacionados con los gestos de muñeca de la misma manera que escucha cualquier otro evento tecla. El framework de Android llama a los objetos View o Activity que están enfocados con los eventos de tecla. Para los gestos, se llama a la devolución de llamada del método onKeyDown() cuando se producen gestos.

Como ejemplo, una app puede anular acciones predefinidas en una View o Activity que implemente KeyEvent.Callback de la siguiente manera:

Kotlin

class GesturesActivity : Activity() {

    /* KeyEvent.Callback */
    override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
        return when (keyCode) {
            KeyEvent.KEYCODE_NAVIGATE_NEXT ->
                // Do something that advances a user View to the next item in an ordered list.
                moveToNextItem()
            KeyEvent.KEYCODE_NAVIGATE_PREVIOUS ->
                // Do something that advances a user View to the previous item in an ordered list.
                moveToPreviousItem()
            else -> {
                // If you did not handle it, let it be handled by the next possible element as determined
                // by the Activity.
                super.onKeyDown(keyCode, event)
            }
        }
    }

    /** Shows the next item in the custom list.  */
    private fun moveToNextItem(): Boolean {
        ...
        // Return true if handled successfully, otherwise return false.
        return false
    }

    /** Shows the previous item in the custom list.  */
    private fun moveToPreviousItem(): Boolean {
        ...
        // Return true if handled successfully, otherwise return false.
        return false
    }
}

Java

public final class GesturesActivity extends Activity {

 @Override /* KeyEvent.Callback */
 public boolean onKeyDown(int keyCode, KeyEvent event) {
  switch (keyCode) {
   case KeyEvent.KEYCODE_NAVIGATE_NEXT:
    // Do something that advances a user View to the next item in an ordered list.
    return moveToNextItem();
   case KeyEvent.KEYCODE_NAVIGATE_PREVIOUS:
    // Do something that advances a user View to the previous item in an ordered list.
    return moveToPreviousItem();
  }
  // If you did not handle it, let it be handled by the next possible element as determined by the Activity.
  return super.onKeyDown(keyCode, event);
 }

 /** Shows the next item in the custom list. */
 private boolean moveToNextItem() {
  boolean handled = false;
  ...
  // Return true if handled successfully, otherwise return false.
  return handled;
 }

 /** Shows the previous item in the custom list. */
 private boolean moveToPreviousItem() {
  boolean handled = false;
  ...
  // Return true if handled successfully, otherwise return false.
  return handled;
 }
}

Prácticas recomendadas

  • Revisa las páginas de KeyEvent y KeyEvent.Callback para la entrega de eventos de tecla a tu View y Activity.
  • Mantén una indicación visual direccional coherente: usa el gesto de girar la muñeca hacia afuera para ir al contenido siguiente y el de girar la muñeca hacia adentro para ir al anterior.
  • Ten un toque paralelo para un gesto.
  • Proporciona información visual.
  • No utilices un código de tecla para implementar una funcionalidad que sea contraintuitiva para el resto del sistema. Por ejemplo, no uses KEYCODE_NAVIGATE_NEXT para cancelar una acción o navegar el eje de izquierda a derecha con giros de muñeca.
  • No interceptes los eventos de tecla en elementos que no forman parte de la interfaz de usuario, como las vistas que están fuera de la pantalla o parcialmente cubiertas. Esto funciona de la misma manera que para cualquier evento de tecla.
  • No reinterpretes los gestos de giros de muñeca repetidos como un gesto nuevo. Esto podría entrar en conflicto con el gesto "agitar la muñeca" del sistema.
  • Para que una vista reciba eventos de tecla de gestos, debe estar enfocada. Consulta View.setFocusable().

    Debido a que los gestos se tratan como eventos de tecla, activan una transición del "modo táctil" que puede generar acciones inesperadas. Como los usuarios pueden alternar entre usar el tacto y los gestos, podría ser necesario usar el método View::setFocusableInTouchmode(). En algunos casos, también puede ser necesario usar setDescendantFocusability(FOCUS_BEFORE_DESCENDANTS) para que, cuando cambie el enfoque después del cambio al "modo táctil" o desde este, la vista deseada reciba el enfoque.

  • Usa requestFocus() y clearFocus() con cuidado:
    • Cuando llames a requestFocus(), asegúrate de que sea apropiado que la vista esté enfocada. Si la vista está fuera de la pantalla o cubierta por otra, podrían generarse acciones inesperadas cuando los gestos activen las devoluciones de llamada.
    • El método clearFocus() inicia una búsqueda de enfoque para encontrar otra vista adecuada. Según la jerarquía de vistas, esta búsqueda puede requerir cálculos no triviales. También puede terminar asignando el enfoque a una vista de forma inesperada.
  • Los eventos de tecla se envían primero a la vista con enfoque en la jerarquía de vistas. Si la vista enfocada no controla el evento (en otras palabras, muestra false), el evento no se entrega a la vista superior, incluso si puede recibir el enfoque y tiene un KeyListener. En su lugar, el evento se entrega a la actividad actual, por lo que se mantiene la jerarquía de vistas con enfoque.

    Por lo tanto, podría ser necesario detectar todos los eventos en el nivel superior y, luego, pasar los códigos relevantes hacia abajo. Como alternativa, puedes dividir en subclases la actividad y anular el método dispatchKeyEvent(KeyEvent event) para interceptar teclas cuando sea necesario o manejarlas cuando no se controlen en capas inferiores.