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:

  • Testa il supporto di base della tastiera, ad esempio Ctrl+Z per annullare, Ctrl+C per copiare e Ctrl+S per salvare. Consulta Gestire le azioni da tastiera per un elenco delle scorciatoie da tastiera predefinite.
  • Testa il supporto avanzato della tastiera, ad esempio il tasto Tab e la navigazione con i tasti freccia, la conferma dell'inserimento di testo con il tasto Invio e la riproduzione e la pausa con la barra spaziatrice nelle app multimediali.
  • Testa le interazioni di base del mouse, tra cui il clic con il tasto destro per il menu contestuale, le modifiche delle icone al passaggio del mouse e gli eventi di scorrimento della rotellina del mouse o del trackpad sui componenti personalizzati.
  • Testa i dispositivi di input specifici dell'app, come stilo, controller di gioco e controller MIDI per app musicali.
  • Valuta la possibilità di aggiungere il supporto di input avanzato che potrebbe 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 la tua app risponde all'input da tastiera contribuisce all'esperienza utente su schermi di grandi dimensioni. Esistono tre tipi di input da tastiera: navigazione, sequenze di tasti e scorciatoie.

La navigazione da tastiera viene implementata raramente nelle app incentrate sul tocco, ma gli utenti se lo aspettano quando utilizzano un'app e hanno le mani su una tastiera. La navigazione da tastiera può essere essenziale su smartphone, tablet, dispositivi 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 composable sono focalizzabili per impostazione predefinita, come un Button o un composable con il modificatore clickable; la navigazione da tastiera dovrebbe in genere funzionare senza codice aggiuntivo. Per attivare la navigazione da tastiera per i composable 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 Creazione di un elemento componibile selezionabile.

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

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

Per controllare il comportamento dello stato attivo, aggiungi il modificatore focusGroup al composable principale di una raccolta di composable. Lo stato attivo si sposta sul gruppo, quindi attraverso il gruppo prima di passare al componente successivo su cui è possibile impostare lo stato attivo, 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 saperne di più, consulta Fornire una navigazione coerente con i focus group.

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

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

Sequenze di tasti

Per l'input 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 sequenze di tasti che non possono essere previste dal framework, le app devono gestire il comportamento autonomamente. Ciò vale in particolare per le app con viste 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 il movimento con i tasti W, A, S e D.

Puoi gestire i singoli tasti con il modificatore onKeyEvent, che accetta una lambda chiamata quando il componente modificato riceve un evento chiave. La proprietà KeyEvent#type ti consente di determinare se l'evento è una pressione del tasto (KeyDown) o un rilascio del 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 chiave 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 viene rilasciato un tasto. L'utilizzo del callback impedisce alle app di dover 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 lo sta tenendo premuto possono ascoltare l'evento onKeyDown e gestire autonomamente gli eventi onKeyDown ripetuti.

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

Scorciatoie

Le scorciatoie da tastiera comuni che includono i tasti Ctrl, Alt, Maiusc e Meta sono previste quando si utilizza una tastiera hardware. Se un'app non implementa scorciatoie, l'esperienza può risultare frustrante per gli utenti. Anche gli utenti avanzati apprezzano le scorciatoie per le attività specifiche delle app utilizzate di frequente. Le scorciatoie rendono un'app più facile da usare e la distinguono dalle app che non le hanno.

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

Un oggetto KeyEvent ha i seguenti attributi che indicano se i tasti modificatori 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 stilo. Le app Android gestiscono gli stili come input touchscreen. Alcuni dispositivi potrebbero avere anche un tablet da disegno USB o Bluetooth, come Wacom Intuos. Le app per Android possono ricevere input Bluetooth, ma non input USB.

Per accedere agli oggetti MotionEvent dello stilo, 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 espressione 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. Una stilo può segnalare eventi a frequenze molto più elevate rispetto al display. Quando crei app di disegno, controlla gli eventi che potrebbero essersi verificati di recente 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 tocco (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 di tocco 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 è impostato FLAG_CANCELED. 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 precedenti di Android.

Android 12

Su Android 12 (livello API 32) e versioni precedenti, il rilevamento del rifiuto del palmo è possibile solo per gli eventi tocco con un solo puntatore. Se il tocco del palmo è l'unico puntatore, il sistema annulla l'evento impostando ACTION_CANCEL sull'oggetto dell'evento di movimento. Se gli altri puntatori sono inattivi, 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 del palmo è l'unico puntatore, il sistema annulla l'evento impostando ACTION_CANCEL e FLAG_CANCELED sull'oggetto evento di movimento. Se gli altri indicatori sono abbassati, 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 rifiuto del palmo (o un altro annullamento dell'evento).

App per prendere note

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 richiesta una nuova nota, l'app deve creare una nota vuota pronta 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 utilizza la prima immagine allegata come immagine di sfondo e attivare una modalità in cui l'utente può disegnare sullo schermo con uno stilo.

Testare gli intenti di scrittura di note senza uno stilo

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

  1. Passare alla modalità sviluppatore e rendere scrivibile il dispositivo
  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 aggiungere --ash-enable-palette a una nuova riga alla fine del file
  5. Salva premendo Esc, quindi digitando :, w, q e premendo 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 nella barra e scegli Nuova nota. Si dovrebbe aprire una nota di disegno vuota.
  • Fai uno screenshot. Dalla barra, 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

La maggior parte delle app in genere deve gestire solo tre eventi incentrati sul grande schermo: clic con il tasto destro del mouse, passaggio del mouse e trascinamento e rilascio.

Fare clic con il pulsante destro del mouse

Qualsiasi azione che fa sì che un'app mostri un menu contestuale, ad esempio toccare e tenere premuto un elemento di elenco, deve reagire anche 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 informazioni dettagliate sulla creazione di menu contestuali, vedi Creare un menu contestuale.

Passaci il mouse sopra

Puoi rendere i layout delle app più raffinati e facili da usare gestendo gli eventi al passaggio del mouse. Ciò vale soprattutto 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 li sorvola

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 tablet, smartphone e dispositivi pieghevoli in modalità split-screen.

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, vedi Trascina e rilascia e consulta il post del blog Android su ChromeOS: implementazione del trascinamento.

Considerazioni speciali per ChromeOS

Supporto avanzato del puntatore

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 di grandi dimensioni supportano fino a quattro controller di gioco. Utilizza le API standard per i controller di gioco Android per gestire i controller di gioco (vedi Supporto dei controller di gioco).

I pulsanti del controller di gioco sono mappati su valori comuni seguendo una mappatura comune. Tuttavia, non tutti i produttori di controller di gioco 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à di traduzione dell'input

ChromeOS attiva una modalità di 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 del display non elaborate con le coordinate della finestra. In genere, gli sviluppatori di app non devono implementare autonomamente nessuno di questi comportamenti.

Se un'app implementa un comportamento di input personalizzato, ad esempio definendo un'azione di pizzico personalizzata con due dita sul touchpad, 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 manifest Android:

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

Risorse aggiuntive