Gestione delle modifiche all'input

I Chromebook offrono agli utenti molte opzioni di input diverse: tastiera, mouse, trackpad, touchscreen, stilo, MIDI e gamepad/controller Bluetooth. Ciò significa che lo stesso dispositivo può diventare la stazione di un DJ, la tela di un artista o la piattaforma preferita di un gamer per i giochi in streaming AAA.

In qualità di sviluppatore, questa funzionalità ti offre l'opportunità di creare esperienze app versatili ed entusiasmanti per i tuoi utenti, sfruttando i dispositivi di input che hanno già a portata di mano, da una tastiera collegata a uno stilo a un controller di gioco Stadia. Tuttavia, tutte queste possibilità richiedono anche di pensare alla tua UI per rendere l'esperienza dell'app fluida e logica. Ciò è particolarmente vero se la tua app o il tuo gioco è stato progettato pensando ai cellulari. Ad esempio, se il tuo gioco ha un joystick touch sullo schermo per gli smartphone, probabilmente vorrai nasconderlo quando un utente gioca con la tastiera.

In questa pagina troverai i principali problemi da tenere presenti quando si considerano più fonti di input e le strategie per risolverli.

Individuazione da parte dell'utente dei metodi di immissione supportati

Idealmente, la tua app risponderà senza problemi a qualsiasi input scelto dall'utente. Spesso è semplice e non richiede di fornire all'utente informazioni aggiuntive. Ad esempio, un pulsante deve funzionare se l'utente fa clic su di esso con un mouse, un trackpad, il touchscreen, uno stilo e così via e non è necessario comunicare all'utente che può utilizzare questi diversi dispositivi per attivare il pulsante.

Tuttavia, in alcune situazioni il metodo di input può migliorare l'esperienza dell'utente e potrebbe essere utile comunicare che la tua app lo supporta. Ecco alcuni esempi:

  • Un'app di riproduzione multimediale potrebbe supportare molte scorciatoie da tastiera utili che l'utente potrebbe non essere in grado di indovinare facilmente.
  • Se hai creato un'app per DJ, l'utente potrebbe utilizzare inizialmente il touchscreen e non rendersi conto che gli hai consentito di utilizzare la tastiera/il trackpad per fornire l'accesso tattile ad alcune funzionalità. Allo stesso modo, potrebbero non rendersi conto che supporti diversi controller MIDI per DJ e incoraggiarli a dare un'occhiata all'hardware supportato potrebbe rendere l'esperienza di DJing più autentica.
  • Il tuo gioco potrebbe essere ottimo con il touchscreen e la tastiera/il mouse, ma gli utenti potrebbero non rendersi conto che supporta anche una serie di controller di gioco Bluetooth. Il collegamento di uno di questi potrebbe aumentare la soddisfazione e il coinvolgimento degli utenti.

Puoi aiutare gli utenti a scoprire le opzioni di input con messaggi al momento opportuno. L'implementazione sarà diversa per ogni app. Alcuni esempi includono:

  • Popup di primo avvio o suggerimenti del giorno
  • Le opzioni di configurazione nei pannelli delle impostazioni possono indicare passivamente agli utenti che esiste un supporto. Ad esempio, una scheda "Controller di gioco" nel pannello delle impostazioni di un gioco indica che i controller sono supportati.
  • Messaggi contestuali. Ad esempio, se rilevi una tastiera fisica e scopri che l'utente fa clic su un'azione utilizzando un mouse o un touchscreen, potresti mostrare un suggerimento utile che consiglia una scorciatoia da tastiera.
  • Elenchi di scorciatoie da tastiera. Quando viene rilevata una tastiera fisica, fornire un'indicazione nell'interfaccia utente di un modo per visualizzare un elenco di scorciatoie da tastiera disponibili ha il duplice scopo di pubblicizzare la presenza del supporto della tastiera e di fornire agli utenti un modo semplice per visualizzare e ricordare le scorciatoie supportate.

Etichettatura/layout dell'interfaccia utente per la variazione dell'input

Idealmente, l'interfaccia visiva non dovrebbe richiedere molte modifiche se viene utilizzato un dispositivo di input diverso e tutti gli input possibili dovrebbero "funzionare". Tuttavia, esistono importanti eccezioni. Due delle principali sono l'interfaccia utente specifica per il tocco e i prompt sullo schermo.

UI specifica per il tocco

Ogni volta che la tua app ha elementi dell'interfaccia utente specifici per il tocco, ad esempio un joystick sullo schermo in un gioco, devi considerare l'esperienza utente quando il tocco non viene utilizzato. In alcuni giochi per dispositivi mobili, una parte significativa dello schermo è occupata dai controlli necessari per il gioco basato sul tocco, ma non necessari se l'utente utilizza un gamepad o una tastiera per giocare. La tua app o il tuo gioco deve fornire una logica per rilevare quale metodo di input viene utilizzato attivamente e regolare di conseguenza l'interfaccia utente. Per alcuni esempi di come procedere, consulta la sezione Implementazione di seguito.

Interfacce utente di un gioco di corse automobilistiche: una con controlli sullo schermo e una con tastiera

Prompt sullo schermo

La tua app potrebbe fornire prompt utili sullo schermo agli utenti. Fai attenzione che non dipendano dal dispositivo di input. Ad esempio:

  • Scorri fino a…
  • Tocca in un punto qualsiasi per chiudere
  • Pizzica per eseguire lo zoom
  • Premi "X" per…
  • Tieni premuto per attivare

Alcune app potrebbero essere in grado di modificare la formulazione per essere indipendenti dall'input. Questa soluzione è preferibile quando ha senso, ma in molti casi la specificità è importante e potresti dover mostrare messaggi diversi a seconda del metodo di input utilizzato, in particolare nelle modalità di tipo tutorial come alla prima esecuzione delle app.

Considerazioni relative alle più lingue

Se la tua app supporta più lingue, dovrai pensare alla struttura delle stringhe. Ad esempio, se supporti 3 modalità di input e 5 lingue, potrebbero esserci 15 versioni diverse di ogni messaggio dell'interfaccia utente. In questo modo, aumenterà la quantità di lavoro necessaria per aggiungere nuove funzionalità e la probabilità di errori ortografici.

Un approccio consiste nel considerare le azioni dell'interfaccia come un insieme separato di stringhe. Ad esempio, se definisci l'azione "chiudi" come variabile stringa a sé stante con varianti specifiche per l'input, ad esempio: "Tocca un punto qualsiasi per chiudere", "Premi Esc per chiudere", "Fai clic in un punto qualsiasi per chiudere", "Premi un pulsante qualsiasi per chiudere", tutti i messaggi dell'interfaccia utente che devono indicare all'utente come chiudere un elemento possono utilizzare questa singola variabile stringa "chiudi". Quando il metodo di input cambia, modifica semplicemente il valore di questa variabile.

Input da tastiera su schermo / IME

Tieni presente che se un utente utilizza un'app senza tastiera fisica, l'inserimento di testo può avvenire tramite una tastiera sullo schermo. Assicurati di testare che gli elementi dell'interfaccia utente necessari non siano oscurati quando viene visualizzata una tastiera sullo schermo. Per saperne di più, consulta la documentazione sulla visibilità dell'IME di Android.

Implementazione

Nella maggior parte dei casi, le app o i giochi devono rispondere correttamente a tutti gli input validi, indipendentemente da ciò che viene mostrato sullo schermo. Se un utente utilizza il touch screen per 10 minuti, ma poi passa improvvisamente all'uso della tastiera, l'input da tastiera deve funzionare, indipendentemente dai prompt o dai controlli visivi sullo schermo. In altre parole, la funzionalità deve avere la priorità su elementi visivi/testo.In questo modo, la tua app/il tuo gioco sarà utilizzabile anche se la logica di rilevamento dell'input presenta un errore o si verifica una situazione imprevista.

Il passaggio successivo consiste nel mostrare la corretta UI per il metodo di input attualmente in uso. Come fai a rilevarlo con precisione? Cosa succede se gli utenti cambiano metodo di inserimento mentre utilizzano la tua app? Cosa succede se utilizza più metodi contemporaneamente?

Macchina a stati con priorità in base agli eventi ricevuti

Un approccio consiste nel tenere traccia dell'"input attivo" corrente, ovvero i prompt di input attualmente visualizzati sullo schermo per l'utente, monitorando gli eventi di input effettivi ricevuti dall'app e passando da uno stato all'altro utilizzando una logica con priorità.

Priorità a

Perché dare la priorità agli stati di input? Gli utenti interagiscono con i propri dispositivi in molti modi diversi e la tua app deve supportare la loro scelta. Ad esempio, se un utente sceglie di utilizzare contemporaneamente il touchscreen e un mouse Bluetooth, questa funzionalità deve essere supportata. Ma quali prompt e controlli di input sullo schermo dovresti visualizzare? Mouse o tocco?

Definire ogni insieme di prompt come "stato di input" e poi assegnare loro una priorità può aiutarti a prendere questa decisione in modo coerente.

Gli eventi di input ricevuti determinano lo stato

Perché agire solo sugli eventi di input ricevuti? Potresti pensare di poter tenere traccia delle connessioni Bluetooth per indicare se è stato collegato un controller Bluetooth o monitorare le connessioni USB per i dispositivi USB. Questo approccio non è consigliato per due motivi principali.

Innanzitutto, le informazioni che puoi dedurre sui dispositivi connessi in base alle variabili API non sono coerenti e il numero di dispositivi Bluetooth/hardware/stilo è in continua crescita.

Il secondo motivo per utilizzare gli eventi ricevuti anziché lo stato della connessione è che gli utenti potrebbero avere un mouse, un controller Bluetooth, un controller MIDI e così via collegati, ma non utilizzarli attivamente per interagire con la tua app o il tuo gioco.

Se rispondi agli eventi di input ricevuti attivamente dalla tua app, ti assicuri di rispondere alle azioni reali degli utenti in tempo reale e di non cercare di indovinare le loro intenzioni con informazioni incomplete.

Esempio: gioco con supporto touch, tastiera/mouse e controller

Immagina di aver sviluppato un gioco di corse automobilistiche per cellulari touch. I giocatori possono accelerare, decelerare, girare a destra, girare a sinistra o usare il nitro per aumentare la velocità.

L'attuale interfaccia touchscreen è costituita da un joystick sullo schermo in basso a sinistra per i controlli di velocità e direzione e da un pulsante in basso a destra per il nitro. L'utente può raccogliere bombole di nitro sul tracciato e, quando la barra del nitro nella parte inferiore dello schermo è piena, sopra il pulsante viene visualizzato il messaggio "Premi per il nitro!". Se è la prima partita dell'utente o non vengono ricevuti input per un po' di tempo, sopra il joystick viene visualizzato il testo "tutorial" che mostra all'utente come far muovere l'auto.

Vuoi aggiungere il supporto per la tastiera e il controller di gioco Bluetooth. Da dove si inizia?

Gioco di corse automobilistiche con controlli touch

Stati di input

Inizia identificando tutti gli stati di input in cui potrebbe essere in esecuzione il tuo gioco, quindi elenca tutti i parametri che vuoi modificare in ogni stato.

                                                                                                                                                                        
ToccoTastiera/mouseController di gioco
       

Reagisce a

     
       

Tutti gli input

     
       

Tutti gli input

     
       

Tutti gli input

     
       

Controlli sullo schermo

     
       

- Joystick sullo schermo
- Pulsante Nitro

     
       

- Nessun joystick
- Nessun pulsante Nitro

     
       

- Nessun joystick
- Nessun pulsante Nitro

     
       

Testo

     
       

Tocca per Nitro.

     
       

Premi "N" per il Nitro!

     
       

Premi "A" per Nitro!

     
       

Tutorial

     
       

Immagine del joystick per velocità/direzione

     
       

Immagine dei tasti freccia e WASD per velocità/direzione

     
       

Immagine del gamepad per velocità/direzione

     

Tieni traccia dello stato "input attivo" e poi aggiorna la UI in base a questo stato.

Nota: ricorda che il tuo gioco/app deve rispondere a tutti i metodi di input, indipendentemente dallo stato. Ad esempio, se un utente sta guidando l'auto con il touchscreen, ma preme N sulla tastiera, l'azione Nitro deve essere attivata.

Modifiche di stato con priorità

Alcuni utenti potrebbero utilizzare contemporaneamente l'input da touchscreen e tastiera. Alcuni potrebbero iniziare a utilizzare il gioco/l'app sul divano in modalità tablet e poi passare all'utilizzo della tastiera sul tavolo. Altri potrebbero connettere o disconnettere i controller di gioco durante la partita.

Idealmente, non vuoi avere elementi UI errati, ad esempio dire all'utente di premere il tasto N quando utilizza il touchscreen. Allo stesso tempo, nel caso di utenti che utilizzano contemporaneamente più dispositivi di input, come touchscreen e tastiera, non vuoi che l'interfaccia utente passi costantemente da uno stato all'altro.

Un modo per gestire questo problema è stabilire le priorità del tipo di input e inserire un ritardo tra le modifiche dello stato. Per il gioco di auto sopra, devi sempre assicurarti che il joystick sullo schermo sia visibile ogni volta che vengono ricevuti eventi di tocco dello schermo, altrimenti il gioco potrebbe sembrare inutilizzabile per l'utente. In questo modo, il touchscreen diventa il dispositivo con la priorità più alta.

Se gli eventi della tastiera e del touchscreen venivano ricevuti contemporaneamente, il gioco dovrebbe rimanere in modalità touchscreen, anche se reagisce comunque all'input della tastiera. Se dopo 5 secondi non viene ricevuto alcun input dal touchscreen e vengono ancora ricevuti eventi della tastiera, i controlli sullo schermo potrebbero sbiadire e il gioco potrebbe passare allo stato della tastiera.

L'input del controller di gioco seguirebbe un pattern simile: lo stato dell'UI del controller avrebbe una priorità inferiore rispetto a tastiera/mouse e touch e apparirebbe solo se venisse ricevuto l'input del controller di gioco e non altre forme di input. Il gioco rispondeva sempre all'input del controller.

Di seguito è riportato un diagramma di stato e una tabella di transizione per l'esempio. Adatta l'idea alla tua app o al tuo gioco.

Macchina a stati con priorità: touchscreen, tastiera/mouse, controller di gioco

                                                                                                                                        
#1 Touchscreen#2 Tastiera#3 Gamepad
       

Move to #1

     
       

N/D

     
       

- Input tocco ricevuto
- Passa immediatamente allo stato Input tocco

     
       

- Input tocco ricevuto
- Passa immediatamente allo stato Input tocco

     
       

Passa al punto 2

     
       

- Nessun tocco per 5 secondi
- Input da tastiera ricevuto
- Passa allo stato di input da tastiera

     
       

N/D

     
       

- Input da tastiera ricevuto
(passa immediatamente allo stato Input da tastiera)

     
       

Passa al punto 3

     
       

- Nessun tocco per 5 secondi
- Nessuna tastiera per 5 secondi
- Input del gamepad ricevuto
- Passa allo stato di input del gamepad

     
       

- Nessuna tastiera per 5 secondi
- Input gamepad ricevuto
- Passa allo stato di input del gamepad

     
       

N/D

     

Nota: nota come la definizione delle priorità contribuisca a rendere chiaro quale tipo di input deve essere dominante. Lo stato dell'input si sposta immediatamente verso l'alto nella priorità:

3. Gamepad -> 2. Tastiera -> 1. Tocco

non appena viene utilizzato un dispositivo con priorità più alta, ma la priorità diminuisce lentamente, solo dopo un periodo di ritardo e solo se il dispositivo con priorità inferiore è in uso.

Eventi di input

Ecco un esempio di codice che mostra come rilevare gli eventi di input da vari tipi di dispositivi di input utilizzando le API Android standard. Utilizza questi eventi per gestire la macchina a stati, come sopra. Devi adattare il concetto generale ai tipi di eventi di input che ti aspetti e alla tua app o al tuo gioco.

Pulsanti della tastiera e del controller

// 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:per KeyEvents, puoi scegliere di utilizzare onKeyDown() o onKeyUp(). In questo caso, onKeyDown() viene utilizzato per controllare la macchina a stati, mentre onKeyUp() viene utilizzato per attivare gli eventi di gioco.

Se un utente preme e tiene premuto un pulsante, onKeyUp() verrà attivato una sola volta per pressione del tasto, mentre onKeyDown() verrà chiamato più volte. Se vuoi reagire alla pressione verso il basso, devi gestire gli eventi di gioco in onKeyDown() e implementare la logica per gestire gli eventi ripetuti. Per ulteriori informazioni, consulta la documentazione relativa alla gestione delle azioni da tastiera.

Tocco e stilo

// 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 e 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)
}