Los Chromebooks ofrecen a los usuarios muchas opciones de entrada diferentes: teclado, mouse, panel táctil, pantalla táctil, lápiz óptico, MIDI y controles de juegos o Bluetooth. Esto significa que el mismo dispositivo puede convertirse en la estación de un DJ, el lienzo de un artista o la plataforma preferida de un jugador para los juegos AAA en transmisión.
Como desarrollador, esto te brinda la oportunidad de crear experiencias de apps versátiles y emocionantes para tus usuarios que aprovechen los dispositivos de entrada que ya tienen a su alcance, desde un teclado conectado hasta un lápiz óptico o un control de juegos de Stadia. Sin embargo, todas estas posibilidades también requieren que pienses en tu IU para que la experiencia de la app sea fluida y lógica. Esto es especialmente cierto si tu app o juego se diseñó teniendo en cuenta los teléfonos celulares. Por ejemplo, si tu juego tiene un joystick en pantalla controlado por tacto para teléfonos, probablemente querrás ocultarlo cuando un usuario juegue con el teclado.
En esta página, encontrarás los principales problemas que debes tener en cuenta cuando pienses en varias fuentes de entrada y estrategias para abordarlos.
Descubrimiento por parte del usuario de los métodos de entrada admitidos
Lo ideal es que tu app responda sin problemas a cualquier entrada que el usuario elija usar. A menudo, esto es simple y no requiere que le proporciones al usuario información adicional. Por ejemplo, un botón debería funcionar si el usuario hace clic en él con un mouse, un trackpad, la pantalla táctil, un lápiz óptico, etcétera, y no es necesario que le digas al usuario que puede usar estos diferentes dispositivos para activar el botón.
Sin embargo, hay situaciones en las que el método de entrada puede mejorar la experiencia del usuario, por lo que puede ser conveniente informarle que tu app lo admite. Estos son algunos ejemplos:
- Una app de reproducción de medios puede admitir muchos atajos de teclado útiles que el usuario no puede adivinar fácilmente.
- Si creaste una app para DJ, es posible que el usuario use la pantalla táctil al principio y no se dé cuenta de que le permitiste usar el teclado o el panel táctil para acceder de forma táctil a algunas de las funciones. Del mismo modo, es posible que no se den cuenta de que admites varios controladores MIDI para DJ, por lo que alentarlos a que prueben el hardware compatible podría brindarles una experiencia de DJ más auténtica.
- Es posible que tu juego sea excelente con la pantalla táctil y el teclado o mouse, pero los usuarios tal vez no se den cuenta de que también admite varios controles de juegos Bluetooth. Conectar uno de estos podría aumentar la satisfacción y la participación de los usuarios.
Puedes ayudar a los usuarios a descubrir opciones de entrada con mensajes en el momento adecuado. La implementación se verá diferente para cada app. Algunos ejemplos incluyen los siguientes:
- Ventanas emergentes de sugerencias del día o de primer uso
- Las opciones de configuración en los paneles de configuración pueden indicar de forma pasiva a los usuarios que existe asistencia. Por ejemplo, una pestaña “Control de juegos” en el panel de configuración de un juego indica que se admiten controles.
- Mensajes contextuales Por ejemplo, si detectas un teclado físico y ves que el usuario hace clic en una acción con un mouse o una pantalla táctil, es posible que quieras mostrar una sugerencia útil que proponga un atajo de teclado.
- Listas de combinaciones de teclas Cuando se detecta un teclado físico, indicar en la IU una forma de mostrar una lista de combinaciones de teclas disponibles cumple el doble propósito de anunciar que hay compatibilidad con el teclado y proporcionar una forma sencilla para que los usuarios vean y recuerden las combinaciones de teclas admitidas.
Etiquetado y diseño de la IU para la variación de entrada
Idealmente, tu interfaz visual no debería cambiar mucho si se usa un dispositivo de entrada diferente. Todas las entradas posibles deberían “funcionar sin problemas”. Sin embargo, hay excepciones importantes. Dos de los principales son la IU específica para pantallas táctiles y los mensajes en pantalla.
IU específica para pantallas táctiles
Cada vez que tu app tenga elementos de la IU específicos para pantallas táctiles, p.ej., un joystick en pantalla en un juego, debes tener en cuenta cómo será la experiencia del usuario cuando no se use la pantalla táctil. En algunos juegos para dispositivos móviles, los controles ocupan una parte importante de la pantalla, lo que es necesario para jugar con la pantalla táctil, pero innecesario si el usuario usa un control de juegos o un teclado. Tu app o juego debe proporcionar lógica para detectar qué método de entrada se está usando de forma activa y ajustar la IU en consecuencia. Consulta la sección de implementación a continuación para ver algunos ejemplos de cómo hacerlo.
Indicaciones en pantalla
Es posible que tu app proporcione mensajes útiles en pantalla a los usuarios. Ten cuidado de que no dependan de un dispositivo de entrada. Por ejemplo:
- Desliza para…
- Presiona en cualquier lugar para cerrar
- Pellizcar para acercar
- Presiona la “X” para…
- Mantén presionado para activar
Es posible que algunas apps puedan ajustar su redacción para que no dependa del método de entrada. Esto es preferible cuando tiene sentido, pero, en muchos casos, la especificidad es importante y es posible que necesites mostrar diferentes mensajes según el método de entrada que se use, en especial en los modos de tipo tutorial, como en la primera ejecución de las apps.
Consideraciones sobre varios idiomas
Si tu app admite varios idiomas, deberás pensar en la arquitectura de cadenas. Por ejemplo, si admites 3 modos de entrada y 5 idiomas, eso podría significar 15 versiones diferentes de cada mensaje de la IU. Esto aumentará la cantidad de trabajo necesario para agregar funciones nuevas y amplificará la probabilidad de errores ortográficos.
Un enfoque es considerar las acciones de la interfaz como un conjunto independiente de cadenas. Por ejemplo, si defines la acción “cerrar” como su propia variable de cadena con variantes específicas para la entrada, como “Presiona en cualquier lugar para cerrar”, “Presiona ‘Esc’ para cerrar”, “Haz clic en cualquier lugar para cerrar”, “Presiona cualquier botón para cerrar”, todos los mensajes de la IU que necesiten indicarle al usuario cómo cerrar algo pueden usar esta única variable de cadena “cerrar”. Cuando cambia el método de entrada, simplemente cambia el valor de esta variable.
Entrada de IME o teclado en pantalla
Recuerda que, si un usuario usa una app sin un teclado físico, la entrada de texto puede realizarse a través de un teclado en pantalla. Asegúrate de probar que los elementos necesarios de la IU no estén obstruidos cuando aparezca un teclado en pantalla. Consulta la documentación sobre la visibilidad del IME de Android para obtener más información.
Implementación
En la mayoría de los casos, las apps o los juegos deben responder correctamente a todas las entradas válidas, independientemente de lo que se muestre en la pantalla. Si un usuario usa la pantalla táctil durante 10 minutos, pero luego cambia repentinamente al teclado, la entrada del teclado debería funcionar, independientemente de las indicaciones visuales o los controles en pantalla. En otras palabras, la funcionalidad debe tener prioridad sobre los elementos visuales o el texto.Esto ayuda a garantizar que tu app o juego se puedan usar incluso si la lógica de detección de entrada tiene un error o surge una situación inesperada.
El siguiente paso es mostrar la IU correcta para el método de entrada que se está usando. ¿Cómo se detecta con precisión? ¿Qué sucede si los usuarios cambian entre diferentes métodos de entrada mientras usan tu app? ¿Qué sucede si usan varios métodos al mismo tiempo?
Máquina de estados priorizada según los eventos recibidos
Un enfoque es hacer un seguimiento del “estado de entrada activo” actual, que representa las instrucciones de entrada que se muestran en la pantalla al usuario, haciendo un seguimiento de los eventos de entrada reales que recibe la app y realizando la transición entre los estados con lógica priorizada.
Priorizar
¿Por qué priorizar los estados de entrada? Los usuarios interactúan con sus dispositivos de muchas maneras, y tu app debe admitir sus elecciones. Por ejemplo, si un usuario elige usar la pantalla táctil y un mouse Bluetooth al mismo tiempo, esto debería ser compatible. Pero, ¿qué mensajes y controles en pantalla deberías mostrar? ¿Mouse o táctil?
Definir cada conjunto de instrucciones como un "estado de entrada" y, luego, priorizarlos puede ayudar a decidir esto de manera coherente.
Los eventos de entrada recibidos determinan el estado
¿Por qué solo se actúa en función de los eventos de entrada recibidos? Quizás pienses que podrías hacer un seguimiento de las conexiones Bluetooth para indicar si se conectó un control Bluetooth o supervisar las conexiones USB para los dispositivos USB. Este no es un enfoque recomendado por dos motivos principales.
En primer lugar, la información que puedes adivinar sobre los dispositivos conectados en función de las variables de la API no es coherente, y la cantidad de dispositivos Bluetooth, hardware y lápices ópticos crece continuamente.
El segundo motivo para usar los eventos recibidos en lugar del estado de conexión es que los usuarios pueden tener un mouse, un control Bluetooth, un controlador MIDI, etcétera, conectados, pero no los usan de forma activa para interactuar con tu app o juego.
Si respondes a los eventos de entrada que tu app recibió de forma activa, te aseguras de responder a las acciones reales de los usuarios en tiempo real y no intentas adivinar sus intenciones con información incompleta.
Ejemplo: Juego con compatibilidad para pantalla táctil, teclado/mouse y control
Imagina que desarrollaste un juego de carreras de autos para teléfonos celulares táctiles. Los jugadores pueden acelerar, desacelerar, girar a la derecha, girar a la izquierda o usar nitro para aumentar la velocidad.
La interfaz actual de la pantalla táctil consta de un joystick en pantalla en la parte inferior izquierda para controlar la velocidad y la dirección, y un botón en la parte inferior derecha para el nitro. El usuario puede recolectar bidones de nitro en la pista y, cuando la barra de nitro en la parte inferior de la pantalla esté llena, aparecerá un mensaje sobre el botón que dice “¡Presiona para usar nitro!”. Si es el primer juego del usuario o no se recibe ninguna entrada durante un tiempo, aparecerá un texto de “tutorial” sobre la palanca de control que le mostrará al usuario cómo hacer que el automóvil se mueva.
Quieres agregar compatibilidad con el teclado y el control de juegos Bluetooth. ¿Por dónde empiezas?
Estados de entrada
Comienza por identificar todos los estados de entrada en los que podría ejecutarse tu juego y, luego, enumera todos los parámetros que te gustaría cambiar en cada estado.
| Tocar | Teclado/mouse | Controlador de juegos | |
|---|---|---|---|
|
Reacciona a |
Todas las entradas |
Todas las entradas |
Todas las entradas |
|
Controles en pantalla |
- Joystick en pantalla |
- Sin palanca de control |
- Sin palanca de control |
|
Texto |
Presiona para obtener Nitro. |
Presiona "N" para Nitro. |
Presiona "A" para usar Nitro. |
|
Instructivo |
Imagen del joystick para controlar la velocidad y la dirección |
Imagen de las teclas de flecha y WASD para la velocidad y la dirección |
Imagen del gamepad para la velocidad y la dirección |
Haz un seguimiento del estado de "entrada activa" y, luego, actualiza la IU según sea necesario en función de ese estado.
Nota: Recuerda que tu juego o app debe responder a todos los métodos de entrada, independientemente del estado. Por ejemplo, si un usuario conduce el automóvil con la pantalla táctil, pero presiona la letra N en el teclado, se debe activar la acción de nitro.
Cambios de estado priorizados
Algunos usuarios pueden usar la pantalla táctil y la entrada de teclado al mismo tiempo. Es posible que algunos comiencen a usar tu juego o app en el sofá en modo tablet y, luego, cambien al teclado en la mesa. Otros pueden conectar o desconectar controles de juegos durante la partida.
Lo ideal es no tener elementos de la IU incorrectos, como indicarle al usuario que presione la tecla N cuando está usando la pantalla táctil. Al mismo tiempo, en el caso de los usuarios que usan varios dispositivos de entrada de forma simultánea, como la pantalla táctil y el teclado, no querrás que la IU cambie constantemente entre los dos estados.
Una forma de controlar esto es establecer prioridades de tipo de entrada y agregar una demora entre los cambios de estado. En el caso del juego de autos anterior, siempre querrás asegurarte de que el joystick en pantalla sea visible cada vez que se reciban eventos de toque en la pantalla. De lo contrario, el usuario podría pensar que el juego no se puede usar. Esto haría que la pantalla táctil sea el dispositivo de mayor prioridad.
Si se recibían eventos de teclado y pantalla táctil de forma simultánea, el juego debería permanecer en el modo de pantalla táctil, aunque seguiría reaccionando a la entrada del teclado. Si no se recibió ninguna entrada de pantalla táctil después de 5 segundos y se seguían recibiendo eventos del teclado, tal vez los controles en pantalla se desvanecerían y el juego pasaría al estado del teclado.
La entrada del controlador de juegos seguiría un patrón similar: el estado de la IU del controlador tendría una prioridad más baja que el teclado/mouse y el tacto, y solo aparecería si se recibía la entrada del controlador de juegos y no otras formas de entrada. El juego siempre respondería a la entrada del control.
A continuación, se muestran un diagrama de estados y una tabla de transición para el ejemplo. Adapta la idea a tu app o juego.
| #1 Pantalla táctil | Teclado nº 2 | #3 Control de juegos | |
|---|---|---|---|
|
Mover al puesto núm. 1 |
N/A |
- Se recibió entrada táctil |
- Se recibió entrada táctil |
|
Ir al número 2 |
- No se toca la pantalla durante 5 s |
N/A |
- Se recibió entrada de teclado |
|
Ir al número 3 |
- No se toca la pantalla durante 5 s |
- No hay teclado durante 5 s |
N/A |
Nota: Observa cómo la priorización ayuda a aclarar qué tipo de entrada debe ser dominante. El estado de entrada se mueve instantáneamente hacia arriba en la prioridad:
3. Control de juegos -> 2. Teclado -> 1. Tocar
tan pronto como se usa un dispositivo de mayor prioridad, pero lentamente se mueve “hacia abajo” en la prioridad, solo después de un período de demora y solo si se está usando activamente el dispositivo de menor prioridad.
Eventos de entrada
A continuación, se muestra un ejemplo de código para detectar eventos de entrada de varios tipos de dispositivos de entrada con las APIs de Android estándares. Usa estos eventos para controlar tu máquina de estados, como se muestra arriba. Debes adaptar el concepto general a los tipos de eventos de entrada que esperas y a tu app o juego.
Botones del teclado y del control
// Drive the state machine based on the received input type // onKeyDown drives the state machine, but does not trigger game actions // Both keyboard and game controller events come through as key events override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean { if (event != null) { // Check input source val outputMessage = when (event.source) { SOURCE_KEYBOARD -> { MyStateMachine.KeyboardEventReceived() "Keyboard event" } SOURCE_GAMEPAD -> { MyStateMachine.ControllerEventReceived() "Game controller event" } else -> "Other key event: ${event.source}" } // Do something based on source type findViewById(R.id.text_message).text = outputMessage } // Pass event up to system return super.onKeyDown(keyCode, event) }
// Trigger game events based on key release // Both keyboard and game controller events come through as key events override fun onKeyUp(keyCode: Int, event: KeyEvent?): Boolean { when(keyCode) { KeyEvent.KEYCODE_N -> { MyStateMachine.keyboardEventReceived() engageNitro() return true // event handled here, return true } } // If event not handled, pass up to system return super.onKeyUp(keyCode, event) }
Nota: En el caso de KeyEvents, puedes usar onKeyDown() o onKeyUp(). Aquí, onKeyDown() se usa para controlar la máquina de estados, mientras que onKeyUp() se usa para activar eventos del juego.
Si un usuario mantiene presionado un botón, onKeyUp() solo se activará una vez por cada presión de tecla, mientras que onKeyDown() se llamará varias veces. Si quieres reaccionar a la presión hacia abajo, debes controlar los eventos del juego en onKeyDown() y, luego, implementar la lógica para abordar los eventos repetidos. Consulta la documentación sobre cómo controlar las acciones del teclado para obtener más información.
Modo táctil y pluma stylus
// Touch and stylus events come through as touch events override fun onTouchEvent(event: MotionEvent?): Boolean { if (event != null) { // Get tool type val pointerIndex = event.action and ACTION_POINTER_INDEX_MASK shr ACTION_POINTER_INDEX_SHIFT val toolType = event.getToolType(pointerIndex) // Check tool type val outputMessage = when (toolType) { TOOL_TYPE_FINGER -> { MyStateMachine.TouchEventReceived() "Touch event" } TOOL_TYPE_STYLUS -> { MyStateMachine.StylusEventReceived() "Stylus event" } else -> "Other touch event: ${toolType}" } // Do something based on tool type, return true if event handled findViewById(R.id.text_message).text = outputMessage } // If event not handled, pass up to system return super.onGenericMotionEvent(event) }
Mouse y joystick
// Mouse and joystick events come through as generic events override fun onGenericMotionEvent(event: MotionEvent?): Boolean { if (event != null) { // Check input source val outputMessage = when (event.source) { SOURCE_JOYSTICK -> { MyStateMachine.ControllerEventReceived() "Controller event" } SOURCE_MOUSE -> { MyStateMachine.MouseEventReceived() "Mouse event" } else -> "Other generic event: ${event.source}" } // Do something based on source type, return true if event handled findViewById(R.id.text_message).text = outputMessage } // If event not handled, pass up to system return super.onGenericMotionEvent(event) }