Compatibilità degli input con schermi di grandi dimensioni

Sui dispositivi con schermi di grandi dimensioni, gli utenti spesso interagiscono con le app utilizzando una tastiera, un mouse, un trackpad, uno stilo o un gamepad. Per consentire alla tua app di accettare input da dispositivi esterni, segui questi passaggi:

  • Testa il supporto di base della tastiera, ad esempio Ctrl+Z per annullare, Ctrl+C per copiare e Ctrl+S per salvare. Consulta Gestisci le azioni della tastiera per un elenco delle scorciatoie da tastiera predefinite.
  • Testa il supporto avanzato della tastiera, ad esempio la navigazione da tastiera con i tasti Tab e le frecce, la conferma dell'inserimento di testo con il tasto Invio e la riproduzione e la messa in pausa con la barra spaziatrice nelle app multimediali.
  • Testa le interazioni di base con il mouse, tra cui il clic con il tasto destro del mouse per il menu contestuale, le modifiche alle icone al passaggio del mouse e gli eventi di scorrimento con la rotellina del mouse o il trackpad sui componenti personalizzati.
  • Testa i dispositivi di input specifici per l'app, come stilo, controller di gioco e controller MIDI per app musicali.
  • Prendi in considerazione il supporto di input avanzati che potrebbero far risaltare l'app negli ambienti desktop, ad esempio il touchpad come crossfader per le app per DJ, l'acquisizione del mouse per i giochi e le scorciatoie da tastiera per gli utenti che utilizzano principalmente la tastiera.

Tastiera

Il modo in cui l'app risponde all'input da tastiera contribuisce all'esperienza utente su schermi di grandi dimensioni. Esistono tre tipi di input da tastiera: navigazione, pressioni di tasti e scorciatoie.

La navigazione da tastiera viene implementata raramente nelle app incentrate sul tocco, ma gli utenti se l'aspettano quando utilizzano un'app e hanno le mani su una tastiera. La navigazione da tastiera può essere essenziale su smartphone, tablet, pieghevoli e computer per gli utenti con esigenze di accessibilità.

Per molte app, la navigazione con i tasti Freccia e Tab viene gestita automaticamente dal framework Android. Ad esempio, alcuni componibili sono attivabili per impostazione predefinita, come un Button o un componibile con il modificatore clickable; la navigazione da tastiera dovrebbe funzionare in genere senza codice aggiuntivo. Per attivare la navigazione da tastiera per gli elementi componibili personalizzati che non sono attivabili per impostazione predefinita, aggiungi il modificatore focusable:

var color by remember { mutableStateOf(Green) }
Box(
    Modifier
        .background(color)
        .onFocusChanged { color = if (it.isFocused) Blue else Green }
        .focusable()
) {
    Text("Focusable 1")
}

Per saperne di più, consulta Rendere un composable attivabile.

Quando lo stato attivo è attivo, il framework Android crea una mappatura di navigazione per tutti i componenti attivabili in base alla loro posizione. In genere, questo funziona come previsto e non è necessario alcun ulteriore sviluppo.

Tuttavia, Compose non determina sempre l'elemento successivo corretto per la navigazione a schede per elementi componibili complessi come schede ed elenchi, ad esempio quando uno degli elementi componibili è un elemento scorrevole orizzontale non completamente visibile.

Per controllare il comportamento dello stato attivo, aggiungi il modificatore focusGroup al componibile principale di una raccolta di componibili. Lo stato attivo si sposta sul gruppo, poi attraverso il gruppo prima di passare al componente attivabile successivo, ad esempio:

Row {
    Column(Modifier.focusGroup()) {
        Button({}) { Text("Row1 Col1") }
        Button({}) { Text("Row2 Col1") }
        Button({}) { Text("Row3 Col1") }
    }
    Column(Modifier.focusGroup()) {
        Button({}) { Text("Row1 Col2") }
        Button({}) { Text("Row2 Col2") }
        Button({}) { Text("Row3 Col2") }
    }
}

Per maggiori informazioni, consulta Fornire una navigazione coerente con i focus group.

Testa l'accesso a ogni elemento UI della tua app utilizzando solo la tastiera. Gli elementi utilizzati di frequente devono essere accessibili senza input tramite mouse o tocco.

Ricorda che il supporto della tastiera potrebbe essere essenziale per gli utenti con esigenze di accessibilità.

Sequenze di tasti

Per l'inserimento di testo che verrebbe gestito da una tastiera virtuale sullo schermo (IME), ad esempio per un TextField , le app dovrebbero comportarsi come previsto sui dispositivi con schermi di grandi dimensioni senza ulteriori interventi di sviluppo. Per le pressioni dei tasti che non possono essere previste dal framework, le app devono gestire il comportamento autonomamente. Questo è particolarmente vero per le app con visualizzazioni personalizzate.

Alcuni esempi sono le app di chat che utilizzano il tasto Invio per inviare un messaggio, le app multimediali che avviano e interrompono la riproduzione con la barra spaziatrice e i giochi che controllano i movimenti con i tasti W, A, S e D.

Puoi gestire le singole pressioni dei tasti con il modificatore onKeyEvent, che accetta una funzione lambda chiamata quando il componente modificato riceve un evento chiave. La proprietà KeyEvent#type ti consente di determinare se l'evento è una pressione di un tasto (KeyDown) o un rilascio di un tasto (KeyUp):

Box(
    modifier = Modifier.focusable().onKeyEvent {
        if(
            it.type == KeyEventType.KeyUp &&
            it.key == Key.S
        ) {
            doSomething()
            true
        } else {
            false
        }
    }
)  {
    Text("Press S key")
}

In alternativa, puoi eseguire l'override del callback onKeyUp() e aggiungere il comportamento previsto per ogni codice tasto ricevuto:

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

Un evento onKeyUp si verifica quando un tasto viene rilasciato. L'utilizzo del callback evita che le app debbano elaborare più eventi onKeyDown se un tasto viene tenuto premuto o rilasciato lentamente. I giochi e le app che devono rilevare il momento in cui viene premuto un tasto o se l'utente tiene premuto un tasto possono ascoltare l'evento onKeyDown e gestire autonomamente gli eventi onKeyDown ripetuti.

Per saperne di più, consulta Gestisci le azioni della tastiera.

Scorciatoie

Quando utilizzi una tastiera hardware, sono previste scorciatoie da tastiera comuni che includono i tasti Ctrl, Alt, Maiusc e Meta. Se un'app non implementa le scorciatoie, l'esperienza può risultare frustrante per gli utenti. Gli utenti esperti apprezzano anche le scorciatoie per le attività specifiche dell'app utilizzate di frequente. Le scorciatoie rendono un'app più facile da usare e la differenziano dalle app che non ne hanno.

Alcune scorciatoie comuni includono Ctrl+S (salva), Ctrl+Z (annulla) e Ctrl+Maiusc+Z (ripeti). Per un elenco delle scorciatoie predefinite, consulta Gestisci le azioni da tastiera.

Un oggetto KeyEvent ha i seguenti attributi che indicano se i tasti di modifica sono premuti:

Ad esempio:

Box(
    Modifier.onKeyEvent {
        if (it.isAltPressed && it.key == Key.A) {
            println("Alt + A is pressed")
            true
        } else {
            false
        }
    }
    .focusable()
)

Per ulteriori informazioni, consulta le seguenti risorse:

Stilo

Molti dispositivi con schermo grande sono dotati di uno stilo. Le app per Android gestiscono gli stilo come input touchscreen. Alcuni dispositivi potrebbero anche avere una tavoletta grafica USB o Bluetooth, come Wacom Intuos. Le app per Android possono ricevere input Bluetooth, ma non input USB.

Per accedere agli oggetti dello stilo MotionEvent, aggiungi il modificatore pointerInteropFilter a una superficie di disegno. Implementa una classe ViewModel con un metodo che elabora gli eventi di movimento; passa il metodo come lambda onTouchEvent del modificatore pointerInteropFilter:

@Composable
@OptIn(ExperimentalComposeUiApi::class)
fun DrawArea(modifier: Modifier = Modifier) {
   Canvas(modifier = modifier
       .clipToBounds()
       .pointerInteropFilter {
           viewModel.processMotionEvent(it)
       }

   ) {
       // Drawing code here.
   }
}

L'oggetto MotionEvent contiene informazioni sull'evento:

Punti storici

Android raggruppa gli eventi di input e li invia una volta per frame. Uno stilo può segnalare eventi a frequenze molto più elevate rispetto al display. Quando crei app di disegno, controlla gli eventi che potrebbero essersi verificati nel recente passato utilizzando le API getHistorical:

Palm rejection

Quando gli utenti disegnano, scrivono o interagiscono con la tua app utilizzando uno stilo, a volte toccano lo schermo con il palmo della mano. L'evento touch (impostato su ACTION_DOWN o ACTION_POINTER_DOWN) può essere segnalato alla tua app prima che il sistema riconosca e ignori il tocco involontario del palmo.

Android annulla gli eventi touch del palmo inviando un MotionEvent. Se la tua app riceve ACTION_CANCEL, annulla il gesto. Se la tua app riceve ACTION_POINTER_UP, controlla se FLAG_CANCELED è impostato. In questo caso, annulla il gesto.

Non controllare solo FLAG_CANCELED. Su Android 13 (livello API 33) e versioni successive, il sistema imposta FLAG_CANCELED per gli eventi ACTION_CANCEL, ma non imposta il flag sulle versioni di Android precedenti.

Android 12

Su Android 12 (livello API 32) e versioni precedenti, il rilevamento della funzionalità di palm rejection è possibile solo per gli eventi touch con un singolo puntatore. Se il tocco con il palmo è l'unico puntatore, il sistema annulla l'evento impostando ACTION_CANCEL sull'oggetto evento di movimento. Se altri puntatori sono in basso, il sistema imposta ACTION_POINTER_UP, che non è sufficiente per rilevare il rifiuto del palmo.

Android 13

Su Android 13 (livello API 33) e versioni successive, se il tocco con il palmo è l'unico puntatore, il sistema annulla l'evento impostando ACTION_CANCEL e FLAG_CANCELED sull'oggetto dell'evento di movimento. Se altri puntatori sono in basso, il sistema imposta ACTION_POINTER_UP e FLAG_CANCELED.

Ogni volta che la tua app riceve un evento di movimento con ACTION_POINTER_UP, controlla FLAG_CANCELED per determinare se l'evento indica il palm rejection (o un altro annullamento dell'evento).

App per prendere appunti

ChromeOS ha un intent speciale che mostra agli utenti le app per prendere appunti registrate. Per registrare un'app come app per prendere appunti, aggiungi quanto segue al manifest dell'app:

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

Quando un'app viene registrata nel sistema, l'utente può selezionarla come app predefinita per prendere appunti. Quando viene richiesto un nuovo appunto, l'app deve creare un appunto vuoto pronto per l'input con lo stilo. Quando l'utente vuole annotare un'immagine (ad esempio uno screenshot o un'immagine scaricata), l'app viene avviata con ClipData contenente uno o più elementi con URI content://. L'app deve creare una nota che utilizzi la prima immagine allegata come immagine di sfondo e attivare una modalità in cui l'utente può disegnare sullo schermo con uno stilo.

Testa gli intent per prendere appunti senza uno stilo

Per verificare se un'app risponde correttamente agli intent di scrittura di note senza uno stilo attivo, utilizza il seguente metodo per visualizzare le opzioni di scrittura di note su ChromeOS:

  1. Passa alla modalità sviluppatore e rendi il dispositivo scrivibile
  2. Premi Ctrl+Alt+F2 per aprire un terminale.
  3. Esegui il comando sudo vi /etc/chrome_dev.conf
  4. Premi i per modificare e aggiungi --ash-enable-palette in una nuova riga alla fine del file
  5. Salva premendo Esc, quindi digita :, w, q e premi Invio.
  6. Premi Ctrl+Alt+F1 per tornare alla normale UI di ChromeOS.
  7. Esci e accedi di nuovo

Ora sullo scaffale dovrebbe essere presente un menu dello stilo:

  • Tocca il pulsante dello stilo sulla barra delle app e scegli Nuova nota. Dovrebbe aprirsi una nota di disegno vuota.
  • Fai uno screenshot. Dalla barra delle app, seleziona pulsante dello stilo > Acquisisci schermo o scarica un'immagine. Nella notifica dovrebbe essere presente l'opzione Annota immagine. In questo modo, l'app dovrebbe avviarsi con l'immagine pronta per essere annotata.

Supporto per mouse e touchpad

In genere, la maggior parte delle app deve gestire solo tre eventi incentrati sugli schermi di grandi dimensioni: clic con il tasto destro del mouse, passaggio del mouse e trascinamento.

Fare clic con il pulsante destro del mouse

Tutte le azioni che fanno sì che un'app mostri un menu contestuale, come toccare e tenere premuto un elemento dell'elenco, devono anche reagire agli eventi di clic con il tasto destro del mouse.

Per gestire gli eventi di clic con il tasto destro del mouse, le app devono registrare un View.OnContextClickListener:

Box(modifier = Modifier.fillMaxSize()) {
    AndroidView(
        modifier = Modifier.fillMaxSize(),
        factory = { context ->
            val rootView = FrameLayout(context)
            val onContextClickListener =
                View.OnContextClickListener { view ->
                    showContextMenu()
                    true
                }
            rootView.setOnContextClickListener(onContextClickListener)
            rootView
        },
    )
}

Per maggiori dettagli sulla creazione di menu contestuali, vedi Crea un menu contestuale.

Passaci il mouse sopra

Puoi rendere i layout della tua app più rifiniti e facili da usare gestendo gli eventi di passaggio del mouse. Questo vale in particolare per i componenti personalizzati:

I due esempi più comuni sono:

  • Indicare agli utenti se un elemento ha un comportamento interattivo, ad esempio se è cliccabile o modificabile, modificando l'icona del puntatore del mouse
  • Aggiunta di un feedback visivo agli elementi di un elenco o di una griglia di grandi dimensioni quando il puntatore si trova sopra di essi

Trascina

In un ambiente multi-finestra, gli utenti si aspettano di poter trascinare gli elementi tra le app. Questo vale sia per i computer che per i tablet, gli smartphone e i pieghevoli in modalità schermo diviso.

Valuta se è probabile che gli utenti trascinino elementi nella tua app. Ad esempio, gli editor di foto dovrebbero aspettarsi di ricevere foto, i lettori audio dovrebbero aspettarsi di ricevere file audio e i programmi di disegno dovrebbero aspettarsi di ricevere foto.

Per aggiungere il supporto del trascinamento, consulta Trascina e dai un'occhiata al blog post Android su ChromeOS - Implementazione del trascinamento.

Considerazioni speciali per ChromeOS

Supporto di puntatori avanzati

Le app che gestiscono in modo avanzato l'input del mouse e del touchpad devono implementare un modificatore pointerInput per ottenere un PointerEvent:

@Composable
private fun LogPointerEvents(filter: PointerEventType? = null) {
    var log by remember { mutableStateOf("") }
    Column {
        Text(log)
        Box(
            Modifier
                .size(100.dp)
                .background(Color.Red)
                .pointerInput(filter) {
                    awaitPointerEventScope {
                        while (true) {
                            val event = awaitPointerEvent()
                            // handle pointer event
                            if (filter == null || event.type == filter) {
                                log = "${event.type}, ${event.changes.first().position}"
                            }
                        }
                    }
                }
        )
    }
}

Esamina l'oggetto PointerEvent per determinare quanto segue:

Controller di gioco

Alcuni dispositivi Android con schermo grande supportano fino a quattro controller di gioco. Utilizza le API standard dei controller di gioco Android per gestire i controller di gioco (consulta Supporta i controller di gioco).

I pulsanti del controller di gioco sono associati a valori comuni seguendo una mappatura comune. Tuttavia, non tutti i produttori di controller per videogiochi seguono le stesse convenzioni di mappatura. Puoi offrire un'esperienza molto migliore se consenti agli utenti di selezionare diverse mappature dei controller più diffuse. Per maggiori informazioni, consulta la pagina Elaborare pressioni dei pulsanti del gamepad.

Modalità Traduzione input

ChromeOS attiva una modalità Traduzione dell'input per impostazione predefinita. Per la maggior parte delle app per Android, questa modalità consente alle app di funzionare come previsto in un ambiente desktop. Alcuni esempi includono l'attivazione automatica dello scorrimento con due dita sul touchpad, lo scorrimento con la rotellina del mouse e la mappatura delle coordinate di visualizzazione non elaborate alle coordinate della finestra. In genere, gli sviluppatori di app non devono implementare nessuno di questi comportamenti.

Se un'app implementa un comportamento di input personalizzato, ad esempio definendo un'azione di pizzicamento del touchpad con due dita personalizzata, o se queste traduzioni di input non forniscono gli eventi di input previsti dall'app, puoi disattivare la modalità di traduzione di input aggiungendo il seguente tag al file manifest Android:

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

Risorse aggiuntive