Gesti con una mano con Scrivi


Su Wear OS 7 (livello API 37) e versioni successive, un framework per le gesture con una sola mano, insieme a un'API che fa parte di Compose for Wear OS, consente agli utenti di interagire con la tua app senza toccare lo schermo.

Sebbene inizialmente supportato sui dispositivi Pixel Watch (Pixel Watch 3 e modelli successivi), il framework è disponibile per tutti gli OEM. Se adotti questa API, il supporto dei gesti della tua app viene scalato automaticamente nell'ecosistema man mano che il supporto hardware si espande.

Per aiutare gli utenti a scoprire i gesti disponibili senza ingombrare l'interfaccia utente, il framework Wear OS fornisce indicatori di gesti animati. Questi suggerimenti visivi evidenziano dove è possibile eseguire un gesto, mentre il sistema gestisce automaticamente la cadenza di visualizzazione e la frequenza di disattivazione dell'audio in base alle preferenze dell'utente.

Gesti e azioni supportati

Il framework dei gesti di Wear OS supporta due tipi di gesti:

  • Azione principale (doppio pizzico): corrisponde all'azione principale su una schermata, ad esempio rispondere a una chiamata o attivare/disattivare la riproduzione dei contenuti multimediali.
  • Azione di chiusura (rotazione del polso): corrisponde alla navigazione all'indietro, alla chiusura di una finestra di dialogo o all'annullamento di una richiesta.

Configurare i gesti in Scrivi

Sebbene l'API per le gesture a una mano possa migliorare la tua UI, è importante tenere presente che alcuni produttori di hardware e OEM non supportano queste gesture. Se l'API rileva che la tua app è in esecuzione su uno di questi dispositivi non supportati, la libreria diventa automaticamente autonoma senza influire sulle interazioni touch standard.

Come per i comportamenti standard di Compose, puoi attivare i gesti a una mano sugli elementi dell'interfaccia utente utilizzando i modificatori. Configura i gesti della tua app in base all'azione da eseguire, ovvero principale o chiusura, e a un gestureId per coordinarsi con le preferenze dell'utente a livello di sistema, come la cadenza di visualizzazione dei suggerimenti e la disattivazione della frequenza. Questa configurazione viene espressa creando un oggetto OneHandedGestureConfiguration. Ti consigliamo di utilizzare la funzione rememberOneHandedGestureConfiguration per crearlo. In OneHandedGestureConfiguration puoi anche specificare la priorità dei gesti.

La funzione rememberOneHandedGestureConfiguration tiene traccia della cronologia delle interazioni utente tra le ricomposizioni senza esporre lo stato dell'applicazione. Una volta creata la configurazione, l'app deve passarla a Modifier.oneHandedGesture sul composable interattivo.

Per aiutare gli utenti a scoprire i gesti disponibili, la libreria fornisce il metodo OneHandedGestureClickIndicator. Questo metodo funge da wrapper che sostituisce i contenuti sottostanti per indicare all'utente che è disponibile un'azione di movimento.

Componenti interattivi

Per attivare i gesti su un controllo interattivo come un pulsante, crea una configurazione che specifichi OneHandedGestureAction.Primary e applica il modificatore oneHandedGesture. Passa lo stesso MutableInteractionSource sia al controllo che al modificatore in modo che gli eventi di movimento emettano un feedback visivo di pressione sul controllo.

Per attivare l'indicatore dei gesti, crea e memorizza un'istanza di OneHandedGestureClickIndicatorState. Poi, per attivare il feedback visivo, chiama showIndicator all'interno del callback onGestureAvailable fornito dal modificatore oneHandedGesture, che segnala al sistema che si è verificato un evento di indicazione. Una volta chiamato, il componente sostituisce brevemente il suo normale contenuto con un'animazione del gesto.

var isPlaying by remember { mutableStateOf(false) }
val onClick = { isPlaying = !isPlaying }

val gestureConfig = rememberOneHandedGestureConfiguration(
    action = OneHandedGestureAction.Primary
)
val indicatorState = remember { OneHandedGestureClickIndicatorState() }
val coroutineScope = rememberCoroutineScope()
val interactionSource = remember { MutableInteractionSource() }

Button(
    onClick = onClick,
    interactionSource = interactionSource,
    modifier = Modifier
        .fillMaxWidth()
        .oneHandedGesture(
            gestureConfiguration = gestureConfig,
            interactionSource = interactionSource,
            onGestureLabel = if (isPlaying) "pause" else "play",
            onGestureAvailable = { coroutineScope.launch { indicatorState.showIndicator() } },
            onGesture = onClick
        )
) {
    OneHandedGestureClickIndicator(
        gestureConfiguration = gestureConfig,
        state = indicatorState
    ) {
        Text(if (isPlaying) "Pause" else "Play", modifier = Modifier.fillMaxWidth())
    }
}

Contenitori scorrevoli

Per le schermate o gli elenchi scorrevoli, crea una configurazione che specifichi OneHandedGestureAction.Primary e applica il modificatore oneHandedGesture al contenitore, chiamando un helper di scorrimento come scrollDown.

Per fornire un feedback visivo per le azioni di scorrimento, puoi utilizzare OneHandedGestureScrollIndicator. Questo componente funziona come indicatore di scorrimento standard che mostra la posizione di scorrimento, ma può anche indicare all'utente che è disponibile un gesto di scorrimento. Questo indicatore viene in genere passato allo slot scrollIndicator di un ScreenScaffold ed è accoppiato allo stato di un contenitore scorrevole, ad esempio un TransformingLazyColumn. Osserva anche un OneHandedGestureScrollIndicatorState per gestire le transizioni visive.

Per attivare il feedback visivo, chiama showIndicator in questo stato, in genere all'interno del callback onGestureAvailable del modificatore oneHandedGesture. Una volta attivato, l'indicatore sostituisce temporaneamente il suo stato visivo standard con una sequenza di animazione dei gesti per avvisare l'utente.

val scrollState = rememberTransformingLazyColumnState()
val gestureConfig = rememberOneHandedGestureConfiguration(
    action = OneHandedGestureAction.Primary,
    priority = OneHandedGesturePriority.Scrollable
)
val indicatorState = remember(gestureConfig) { OneHandedGestureScrollIndicatorState() }
val coroutineScope = rememberCoroutineScope()

ScreenScaffold(
    scrollState = scrollState,
    scrollIndicator = {
        OneHandedGestureScrollIndicator(
            gestureConfiguration = gestureConfig,
            indicatorState = indicatorState,
            scrollState = scrollState,
            modifier = Modifier.align(Alignment.CenterEnd)
        )
    }
) { contentPadding ->
    TransformingLazyColumn(
        state = scrollState,
        contentPadding = contentPadding,
        modifier = Modifier
            .fillMaxSize()
            .oneHandedGesture(
                gestureConfiguration = gestureConfig,
                onGestureLabel = "scroll",
                onGestureAvailable = {
                    coroutineScope.launch { indicatorState.showIndicator() }
                },
                onGesture = { OneHandedGestureDefaults.scrollDown(scrollState) }
            )
    ) {
        items(10) { index ->
            Text("Item $index", modifier = Modifier.padding(8.dp))
        }
    }
}

Combinare più gesti

Puoi configurare sia un gesto di scorrimento sia un gesto di clic con la stessa azione principale aggiungendo gesturePriority all'oggetto OneHandedGestureConfiguration:

  • OneHandedGesturePriority.Clickable (massima): Assegna ai controlli interattivi, ad esempio quelli di tipo Button o Card, in modo che acquisiscano i gesti quando sono visibili sullo schermo.
  • OneHandedGesturePriority.Scrollable (media): Assegna a contenitori scorrevoli o impaginabili in modo che cedano il passo a elementi secondari selezionabili, ma scorra quando non è visibile alcun controllo selezionabile.
  • OneHandedGesturePriority.Unspecified (più bassa): Una priorità non assegnata. Questo è il valore predefinito per un gesto che non ha un priority impostato.

Se imposti in modo esplicito priority = OneHandedGesturePriority.Clickable su un pulsante interno e priority = OneHandedGesturePriority.Scrollable sull'elenco principale, il sistema può mostrare questo comportamento di priorità dei gesti. Quando l'utente attiva l'azione principale con il gesto con una sola mano, l'elenco viene prima scorre verso il basso fino a quando il pulsante non è visibile, poi viene acquisita l'azione di clic del pulsante.

Testare ed eseguire il debug dei gesti con ADB

Puoi testare i gesti con una sola mano su un dispositivo fisico o un emulatore senza eseguire movimenti fisici del polso utilizzando Android Debug Bridge (adb) e il servizio di sistema IWearGestureService.

Attivare la simulazione dei gesti

Prima di simulare i gesti utilizzando ADB, configura le impostazioni del dispositivo e gli override dei vincoli:

  1. Verifica che sul dispositivo Wear OS sia installato Wear OS 7 (livello API 37) o versioni successive:

    adb shell getprop ro.build.version.sdk
    
  2. Se esegui il test su un dispositivo fisico che non è al tuo polso o è appoggiato su un caricabatterie, esegui l'override del vincolo di non indossato in modo che il framework dei gesti rimanga attivo:

    adb shell cmd IWearGestureService override-constraints offbody-state
    

Attivare eventi di gesture utilizzando ADB

Per simulare il gesto Doppio pizzico (che è l'azione Primary sugli smartwatch Pixel), esegui questo comando della shell ADB:

adb shell cmd IWearGestureService gesture DoublePinch

Per simulare il gesto Rotazione del polso (che è l'azione Dismiss sugli smartwatch Pixel), esegui questo comando della shell ADB:

adb shell cmd IWearGestureService gesture WristTurn

Reimpostare il monitoraggio dei suggerimenti per i gesti

Il sistema tiene traccia della cronologia delle interazioni dell'utente e mostra suggerimenti per i gesti mobili in base all'impostazione della cadenza globale (ad esempio Sempre o Giornalmente). Quando esegui il debug degli indicatori di gesture della tua app, reimposta questa cronologia di monitoraggio in modo che i suggerimenti vengano visualizzati di nuovo per il tuo pacchetto:

  • Su build o emulatori userdebug:

    adb shell cmd IWearGestureService hint clear <your_package_name>
    
  • Nelle build retail (user):

    Sui dispositivi commerciali senza accesso root, hint clear è bloccato dalle autorizzazioni di sistema. Cancella i dati locali dell'app per reimpostare la scoperta dei suggerimenti:

    adb shell pm clear <your_package_name>
    

Ripristinare i vincoli predefiniti

Per reimpostare tutti gli override dei vincoli di debug al termine del test:

adb shell cmd IWearGestureService override-constraints reset

Risolvere i problemi di iniezione di gesti

Se la tua app non riceve gesti simulati:

  1. Verifica che lo schermo dello smartwatch sia attivo e acceso. Il framework dei gesti non invia gesti alle applicazioni quando lo schermo è spento o in modalità Ambient. Per attivare il display utilizzando ADB, esegui:

    adb shell input keyevent KEYCODE_WAKEUP
    
  2. Controlla se la tua app è registrata come abbonato attivo ai gesti e attualmente ha il focus della finestra:

    adb shell cmd IWearGestureService get-active-gestures -readable
    

    Quando la schermata dei gesti dell'app è in primo piano e lo schermo è attivo, questo comando restituisce [DoublePinch] o [DoublePinch, WristTurn]. Se viene restituito un elenco vuoto ([]), controlla se la finestra è attiva o se i vincoli fuori dal corpo bloccano l'attivazione.

  3. Controlla lo stato del servizio di gesture interno e i token abbonati attivi:

    adb shell dumpsys IWearGestureService
    

Risorse aggiuntive

Per indicazioni di progettazione su quando e dove utilizzare i gesti con una mano, consulta Gesti con una mano.