Compatibilidad de entrada en pantallas grandes

En dispositivos de pantalla grande, los usuarios suelen interactuar con las apps mediante un teclado, un mouse, un panel táctil, una pluma stylus o un control de juegos. Para permitir que tu app acepte entradas de dispositivos externos, haz lo siguiente:

  • Prueba la compatibilidad básica con el teclado, como Ctrl + Z para deshacer, Ctrl + C para copiar y Ctrl + S para guardar. Consulta Cómo controlar las acciones del teclado para obtener una lista de las combinaciones de teclas predeterminadas.
  • Prueba la compatibilidad avanzada del teclado, por ejemplo, la tecla Tab y la navegación con las teclas de flecha del teclado, la tecla Intro para confirmar la entrada de texto y la barra espaciadora para reproducir y pausar contenido en apps de música.
  • Prueba las interacciones básicas del mouse, como hacer clic con el botón derecho para el menú contextual, ver los cambios del ícono cuando se coloca el cursor sobre un elemento y los eventos de desplazamiento del panel táctil y de la rueda del mouse en los componentes personalizados.
  • Prueba dispositivos de entrada específicos de la app, como la pluma stylus, los controles de juegos y los controles MIDI de apps de música.
  • Considera admitir tipos de entradas más avanzadas con las que la app podría destacarse en entornos de escritorio (por ejemplo, el panel táctil como dispositivo de reproducción sin pausa para apps de DJ, la captura del mouse para juegos y combinaciones de teclas más especializadas para quienes utilizan mucho el teclado).

Teclado

La manera en que la app responde a la entrada del teclado contribuye a la experiencia del usuario en pantallas grandes. Existen tres tipos de entrada de teclado: navegación, pulsaciones de teclas y combinaciones de teclas.

La navegación con teclado casi nunca se implementa en apps centradas en pantallas táctiles, pero los usuarios la esperan cuando usan una app y tienen las manos en el teclado. La navegación con el teclado puede ser esencial en teléfonos, tablets, dispositivos plegables y dispositivos de escritorio para los usuarios con necesidades de accesibilidad.

En muchas apps, el framework de Android controla automáticamente la navegación con la tecla de flecha y la tecla Tab. Por ejemplo, un Button es enfocable de forma predeterminada, y la navegación con teclado suele funcionar sin ningún código adicional. Para habilitar la navegación con teclado en las vistas que no son enfocables de forma predeterminada, márcalas como enfocables, lo cual se puede hacer de manera programática o en XML:

Kotlin

yourView.isFocusable = true

Java

yourView.setFocusable(true);

También puedes establecer el atributo focusable en el archivo del diseño:

android:focusable="true"

Para obtener más información, consulta Control de enfoque.

Cuando se habilita el enfoque, el framework de Android crea una asignación de navegación para todas las vistas enfocables según su posición. Por lo general, funciona como se espera, y no se necesita ningún desarrollo adicional. Cuando la asignación predeterminada no es correcta para las necesidades de una app, se puede anular de la siguiente manera:

Kotlin

// Arrow keys
yourView.nextFocusLeftId = R.id.view_to_left
yourView.nextFocusRightId = R.id.view_to_right
yourView.nextFocusTopId = R.id.view_above
yourView.nextFocusBottomId = R.id.view_below
// Tab key
yourView.nextFocusForwardId = R.id.next_view

Java

// Arrow keys
yourView.setNextFocusLeftId(R.id.view_to_left);
yourView.setNextFocusRightId(R.id.view_to_left);
yourView.setNextFocusTopId(R.id.view_to_left);
yourView.setNextFocusBottomId(R.id.view_to_left);
// Tab key
yourView.setNextFocusForwardId(R.id.next_view);

Prueba el acceso a todos los elementos de la IU de tu app solo con el teclado. Debe ser fácil acceder a los elementos que se usan con frecuencia sin usar el mouse o mediar una entrada táctil.

Recuerda que la compatibilidad con el teclado puede ser fundamental para los usuarios con necesidades de accesibilidad.

Pulsaciones de teclas

Para la entrada de texto que se controlaría con un teclado virtual en pantalla (IME), como para, un EditText,, las apps deberían comportarse como se espera en dispositivos de pantalla grande, sin que se requieran acciones adicionales por parte del desarrollador. En el caso de que el framework no pueda anticipar las combinaciones de teclas, las apps deberán controlar el comportamiento por sí mismas, en especial, las que tiene vistas personalizadas.

Algunos ejemplos son las apps de chat que usan la tecla Intro para enviar un mensaje, las apps de música que inician y detienen la reproducción con la barra espaciadora, y los juegos que controlan el movimiento con las teclas w, a, s y d.

La mayoría de las apps anulan la devolución de llamada onKeyUp() y agregan el comportamiento esperado para cada código de clave recibido:

Kotlin

override fun onKeyUp(keyCode: Int, event: KeyEvent): Boolean {
    return when (keyCode) {
        KeyEvent.KEYCODE_ENTER -> {
            sendChatMessage()
            true
        }
        KeyEvent.KEYCODE_SPACE -> {
            playOrPauseMedia()
            true
        }
        else -> super.onKeyUp(keyCode, event)
    }
}

Java

@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
    if (keyCode == KeyEvent.KEYCODE_ENTER) {
        sendMessage();
        return true;
    } else if (KeyEvent.KEYCODE_SPACE){
        playOrPauseMedia();
        return true;
    } else {
        return super.onKeyUp(keyCode, event);
    }
}

Un evento onKeyUp se produce cuando se suelta una tecla. El uso de la devolución de llamada evita que las apps necesiten procesar varios eventos onKeyDown si se mantiene presionada una tecla o si esta se suelta lentamente. Los juegos y las apps que necesitan detectar el momento en que se presiona una tecla o si el usuario mantiene presionada una tecla pueden detectar el evento onKeyDown y controlar los eventos onKeyDown repetidos.

Para obtener más información, consulta Cómo controlar las acciones del teclado.

Accesos directos

Cuando se utiliza un teclado de hardware, se esperan combinaciones de teclas comunes que incluyen las teclas Ctrl, Alt, Mayúsculas y Meta. Si una app no implementa atajos, la experiencia puede ser frustrante para los usuarios. Los usuarios avanzados también valoran los accesos directos para las tareas específicas de la app que se usan con frecuencia. Los accesos directos facilitan el uso de una app y la diferencian de aquellas que no tienen accesos directos.

Algunas combinaciones de teclas habituales incluyen Ctrl + S (guardar), Ctrl + Z (deshacer) y Ctrl + Mayúsculas + Z (rehacer). Para obtener una lista de los atajos predeterminados, consulta Cómo controlar las acciones del teclado.

Para habilitar las combinaciones de teclas, se puede implementar dispatchKeyShortcutEvent() para interceptar todas las combinaciones de teclas (Alt, Ctrl, Mayúsculas y Meta) para un código de tecla determinado. Para verificar una tecla modificadora específica, usa lo siguiente:

Kotlin

override fun dispatchKeyShortcutEvent(event: KeyEvent): Boolean {
  return when (event.keyCode) {
    KeyEvent.KEYCODE_O -> {
      openFile() // Ctrl+O, Shift+O, Alt+O
      true
    }
    KeyEvent.KEYCODE_Z-> {
      if (event.isCtrlPressed) {
        if (event.isShiftPressed) {
          redoLastAction() // Ctrl+Shift+Z pressed
          true
        } else {
          undoLastAction() // Ctrl+Z pressed
          true
        }
      }
    }
    else -> {
      return super.dispatchKeyShortcutEvent(event)
    }
  }
}

Java

@Override
public boolean dispatchKeyShortcutEvent(KeyEvent event) {
  if (event.getKeyCode() == KeyEvent.KEYCODE_O) {
      openFile(); // Ctrl+O, Shift+O, Alt+O
      return true;
  } else if(event.getKeyCode() == KeyEvent.KEYCODE_Z) {
      if (event.isCtrlPressed()) {
          if (event.isShiftPressed()) {
              redoLastAction();
              return true;
          }
          else {
              undoLastAction();
              return true;
          }
      }
  }
  return super.dispatchKeyShortcutEvent(event);
}

Separar el código de acceso directo de otros controles de combinaciones de teclas (como los eventos onKeyUp() y onKeyDown()) acepta teclas modificadoras de forma predeterminada sin tener que implementar, de forma manual, las comprobaciones de teclas modificadoras en cada caso. Permitir todas las combinaciones de teclas modificadoras también puede ser más conveniente para los usuarios que están acostumbrados a diferentes diseños de teclado y sistemas operativos.

Sin embargo, también puedes implementar combinaciones de teclas en onKeyUp() si verificas los elementos KeyEvent.isCtrlPressed(), KeyEvent.isShiftPressed() o KeyEvent.isAltPressed(). Se puede mantener, de forma más fácil, si el comportamiento de la tecla modificada es más un cambio en el comportamiento de una app que un acceso directo. Por ejemplo, en juegos, cuando W significa "caminar hacia adelante" y Mayúscula + W significa "correr hacia adelante".

Kotlin

override fun onKeyUp(keyCode: Int, event: KeyEvent): Boolean {
  return when(keyCode) {
    KeyEvent.KEYCODE_W-> {
      if (event.isShiftPressed) {
        if (event.isCtrlPressed) {
          flyForward() // Ctrl+Shift+W pressed
          true
        } else {
          runForward() // Shift+W pressed
          true
        }
      } else {
        walkForward() // W pressed
        true
      }
    }
    else -> super.onKeyUp(keyCode, event)
  }
}

Java

@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
    if (keyCode == KeyEvent.KEYCODE_W) {
        if (event.isShiftPressed()) {
            if (event.isCtrlPressed()) {
                flyForward(); // Ctrl+Shift+W pressed
                return true;
            } else {
                runForward(); // Shift+W pressed
                return true;
            }
        } else {
            walkForward();
            return true;
        }
    }
    return super.onKeyUp(keyCode, event);
}

Consulta también Ayuda en las combinaciones de teclas.

Pluma stylus

Muchos dispositivos con pantalla grande incluyen una pluma stylus. Las apps para Android controlan las plumas stylus como entrada de pantalla táctil. Algunos dispositivos también pueden tener una tabla de dibujo con USB o Bluetooth, como Wacom Intuos. Las apps para Android pueden recibir entradas Bluetooth, pero no entradas USB.

View#onTouchEvent() o View#onGenericMotionEvent() informan un evento de pluma stylus como un evento de pantalla táctil y contienen un elemento MotionEvent#getSource() del tipo SOURCE_STYLUS.

El objeto MotionEvent contiene información sobre el evento:

Puntos históricos

Android agrupa eventos de entrada y los entrega una vez por fotograma. Una pluma stylus puede informar eventos con frecuencias mucho más altas que la pantalla. Cuando creas apps de dibujo, verifica los eventos que pueden estar en el pasado reciente mediante las APIs de getHistorical:

Rechazo de la palma

Cuando los usuarios dibujan, escriben o interactúan con la app con una pluma stylus, a veces tocan la pantalla con la palma de las manos. El evento táctil (configurado en ACTION_DOWN o ACTION_POINTER_DOWN) se puede informar a tu app antes de que el sistema reconozca e ignore el toque inadvertido de la palma.

Para cancelar los eventos táctiles de la palma, Android envía un MotionEvent. Si tu app recibe ACTION_CANCEL, cancela el gesto. Si tu app recibe ACTION_POINTER_UP, verifica si FLAG_CANCELED está configurado. Si es así, cancela el gesto.

No verifiques solo FLAG_CANCELED. En Android 13 (nivel de API 33) y versiones posteriores, el sistema establece FLAG_CANCELED para eventos ACTION_CANCEL, pero no establece la marca en versiones anteriores de Android.

Android 12

En Android 12 (nivel de API 32) y versiones anteriores, solo se puede detectar el rechazo de la palma de la mano para eventos táctiles de un solo puntero. Si un toque de palma es el único puntero, el sistema configura ACTION_CANCEL en el objeto de evento de movimiento para cancelar el evento. Si otros punteros están inactivos, el sistema establece ACTION_POINTER_UP, que no es suficiente para detectar el rechazo de la palma.

Android 13

En Android 13 (nivel de API 33) y versiones posteriores, si un toque de palma es el único puntero, el sistema configura ACTION_CANCEL y FLAG_CANCELED en el objeto de evento de movimiento para cancelar el evento. Si otros punteros están inactivos, el sistema establece ACTION_POINTER_UP y FLAG_CANCELED.

Cuando tu app reciba un evento de movimiento con ACTION_POINTER_UP, busca FLAG_CANCELED para determinar si el evento indica el rechazo de la palma (o cualquier otra cancelación del evento).

Apps para tomar notas

ChromeOS tiene un intent especial que les muestra a los usuarios apps registradas para tomar notas. Para registrar una app como una para tomar notas, agrega lo siguiente al manifiesto de la app:

<intent-filter>
    <action android:name="org.chromium.arc.intent.action.CREATE_NOTE" />
    <category android:name="android.intent.category.DEFAULT" />
</intent-filter>

Cuando se registra una app en el sistema, el usuario puede seleccionarla como la predeterminada para tomar notas. Cuando se solicita una nota nueva, la app debe crear una nota vacía y lista para la entrada de la pluma stylus. Cuando el usuario desea escribir en una imagen (como una captura de pantalla o una imagen descargada), la app se inicia con el objeto ClipData que contiene uno o más elementos con URI de content://. La app debe crear una nota que use la primera imagen adjunta como imagen de fondo y que ingrese un modo en el que el usuario pueda dibujarla en la pantalla con una pluma stylus.

Cómo probar intents para tomar notas sin una pluma stylus

[TBD quitar sección.]

Para probar si una app responde correctamente a los intents para tomar notas sin una pluma stylus activa, usa el siguiente método para mostrar las opciones para tomar notas en ChromeOS:

  1. Cambia al modo de desarrollo y haz que se pueda escribir en el dispositivo.
  2. Presiona Ctrl + Alt + F2 para abrir una terminal.
  3. Ejecuta el comando sudo vi /etc/chrome_dev.conf.
  4. Presiona i para editar y agregar el elemento --ash-enable-palette a una nueva línea al final del archivo.
  5. Para guardar, presiona Esc, escribe :, w, q y presiona Intro.
  6. Presiona Ctrl + Alt + F1 para volver a la IU normal de ChromeOS.
  7. Sal y vuelve a acceder.

Ahora debería haber un menú de la pluma stylus en la biblioteca:

  • Presiona el botón de la pluma stylus en la barra y elige Nueva nota. Se debería abrir una nota de dibujo en blanco.
  • Toma una captura de pantalla. En la barra, selecciona el botón de la pluma stylus > Captura de pantalla o descarga una imagen. En la notificación, debería aparecer la opción Escribir en la imagen. Se debería iniciar la app con la imagen lista para poder escribir sobre ella.

Compatibilidad con mouse y panel táctil

Por lo general, la mayoría de las apps solo necesitan controlar tres eventos centrados en pantallas grandes: hacer clic con el botón derecho, colocar el cursor sobre un elemento y arrastrar y soltar.

Hacer clic con el botón derecho

Cualquier acción que permita que una app muestre un menú contextual, como mantener presionado un elemento de la lista, también debe responder a eventos de clic con el botón derecho.

Para controlar eventos de clic con el botón derecho, las apps deben registrar un objeto View.OnContextClickListener:

Kotlin

yourView.setOnContextClickListener {
    showContextMenu()
    true
}

Java

yourView.setOnContextClickListener(v -> {
    showContextMenu();
    return true;
});

Para obtener detalles sobre cómo crear menús contextuales, consulta Cómo crear un menú contextual.

Colocar el cursor sobre un elemento

Puedes lograr que los diseños de tus apps se sientan optimizados y más fáciles de usar si controlas los eventos de desplazamiento. Esto es especialmente cierto para los componentes vistas:

Kotlin

// Change the icon to a "hand" pointer on hover.
// Highlight the view by changing the background.
yourView.setOnHoverListener { view, _ ->
    addVisualHighlighting(true)
    view.pointerIcon =
        PointerIcon.getSystemIcon(view.context, PointerIcon.TYPE_HAND)
    true // Listener consumes the event.
}

Java

// Change the icon to a "hand" pointer on hover.
// Highlight the view by changing the background.
yourView.setOnHoverListener((view, event) -> {
    addVisualHighlighting(true);
    view.setPointerIcon(
        PointerIcon.getSystemIcon(view.getContext(), PointerIcon.TYPE_HAND)
    );
    return true; // Listener consumes the event.
});

Los dos ejemplos más comunes son los siguientes:

  • Cambiar el ícono del puntero del mouse para indicarles a los usuarios si un elemento tiene un comportamiento interactivo, como elementos que se pueden editar o en los que se puede hacer clic
  • Agregar comentarios visuales a los elementos en una lista o una cuadrícula grande cuando el puntero se coloca sobre ellos

Arrastrar y soltar

En un entorno multiventana, los usuarios esperan poder arrastrar y soltar elementos entre apps. Esto se aplica a dispositivos de escritorio, así como a tablets, teléfonos y dispositivos plegables en modo de pantalla dividida.

Considera si es probable que los usuarios arrastren elementos a tu app. Por ejemplo, los editores de fotos deben esperar recibir fotos, los reproductores de audio deben esperar recibir archivos de audio y los programas de dibujo deben esperar recibir fotos.

Para agregar compatibilidad con la función de arrastrar y soltar, consulta Habilitar arrastrar y soltar y la entrada de blog Android en ChromeOS: Implementación de arrastrar y soltar.

Consideraciones especiales para ChromeOS

Compatibilidad con puntero avanzado

Las apps que realizan un control avanzado de la entrada del mouse y del panel táctil deben implementar un modificador View#onGenericMotionEvent() y usa [MotionEvent.getSource()][] para distinguir entre SOURCE_MOUSE y SOURCE_TOUCHSCREEN.

Examina el objeto MotionEvent para implementar el comportamiento requerido:

  • El movimiento genera los eventos ACTION_HOVER_MOVE.
  • Los botones generan los eventos ACTION_BUTTON_PRESS y ACTION_BUTTON_RELEASE. También puedes verificar el estado actual de todos los botones del mouse y del panel táctil con getButtonState().
  • El desplazamiento de la rueda del mouse genera eventos ACTION_SCROLL.

Controles de juegos

Algunos dispositivos Android de pantalla grande admiten hasta cuatro controles de juegos. Usa las APIs estándar de control de juegos de Android para controlar los controles de juegos (consulta Cómo brindar compatibilidad con controles de juegos).

Los botones del controlador de juegos se asignan a valores comunes después de una asignación común. Sin embargo, no todos los fabricantes de controles de juegos siguen las mismas convenciones de asignación. Puedes proporcionar una experiencia mucho mejor si permites que los usuarios seleccionen diferentes asignaciones populares para controles. Para obtener más información, consulta Cómo procesar la presión de las teclas de los botones de los controles de juegos.

Modo de traducción de entrada

ChromeOS habilita un modo de traducción de entrada de forma predeterminada. En la mayoría de las apps para Android, ese modo ayuda a que funcionen como se espera en un entorno de escritorio. Algunos ejemplos incluyen la habilitación automática del desplazamiento con dos dedos en el panel táctil, el desplazamiento con la rueda del mouse y la asignación de coordenadas de pantalla sin procesar a las coordenadas de la ventana. Por lo general, los desarrolladores de apps no necesitan implementar ninguno de esos comportamientos.

Si una app implementa un comportamiento de entrada personalizado (por ejemplo, definir una acción personalizada de pellizcar con dos dedos en el panel táctil) o si esas traducciones de entrada no proporcionan los eventos de entrada que espera la app, puedes inhabilitar el modo de traducción de entrada agregando la siguiente etiqueta al manifiesto de Android:

<uses-feature
    android:name="android.hardware.type.pc"
    android:required="false" />

Recursos adicionales