Saisie par dispositif rotatif avec Compose

La saisie par dispositif rotatif fait référence aux entrées effectuées par un utilisateur sur sa montre via un mécanisme qui tourne. En moyenne, les utilisateurs ne passent que quelques secondes sur leur montre. Vous pouvez améliorer l'expérience utilisateur en utilisant la saisie par dispositif rotatif pour permettre à l'utilisateur d'effectuer rapidement diverses tâches.

Sur la plupart des montres, les trois principales sources de saisie par dispositif rotatif incluent le bouton latéral rotatif et un contour physique ou tactile qui est une zone tactile circulaire située autour de l'écran. Bien que le comportement attendu puisse varier en fonction du type d'entrée, assurez-vous de permettre la saisie par dispositif rotatif pour toutes les interactions essentielles.

Faire défiler

La plupart des utilisateurs s'attendent à ce que les applications prennent en charge le geste de défilement. Pendant le défilement du contenu, fournissez aux utilisateurs un indice visuel en réponse à une interaction avec un dispositif rotatif. Les indices visuels peuvent inclure des indicateurs de position pour le défilement vertical ou des indicateurs de page.

Implémentez le défilement par dispositif rotatif à l'aide de Compose pour Wear OS. Cet exemple décrit une application avec une structure en échafaudage et un élément ScalingLazyColumn dont le défilement est vertical. Cet échafaudage fournit la mise en page de base des applications Wear OS et contient déjà un emplacement destiné à un indicateur de défilement. Pour afficher la progression du défilement, créez un indicateur de position en fonction de l'objet d'état de la liste. Les vues déroulantes, y compris ScalingLazyColumn, disposent déjà d'un état de défilement pour ajouter la saisie par dispositif rotatif. Pour recevoir des événements de défilement par dispositif rotatif, procédez comme suit :

  1. Demandez explicitement la sélection à l'aide de FocusRequester.
  2. Ajoutez le modificateur onRotaryScrollEvent pour intercepter les événements que le système génère lorsqu'un utilisateur tourne la couronne ou fait pivoter le contour. Chaque événement rotatif a une valeur de pixels définie et peut défiler verticalement ou horizontalement. Le modificateur comporte également un rappel pour indiquer si l'événement est consommé, et arrête la propagation des événements à ses parents une fois qu'il est consommé.
val listState = rememberScalingLazyListState()

Scaffold(
    positionIndicator = {
        PositionIndicator(scalingLazyListState = listState)
    }
) {

    val focusRequester = rememberActiveFocusRequester()
    val coroutineScope = rememberCoroutineScope()

    ScalingLazyColumn(
        modifier = Modifier
            .onRotaryScrollEvent {
                coroutineScope.launch {
                    listState.scrollBy(it.verticalScrollPixels)

                    listState.animateScrollBy(0f)
                }
                true
            }
            .focusRequester(focusRequester)
            .focusable(),
        state = listState
    ) { ... }
}

Valeurs distinctes

Utilisez également des interactions rotatives pour ajuster des valeurs distinctes, telles que la luminosité dans les paramètres ou la sélection des chiffres dans l'outil de sélection de l'heure lorsque vous définissez une alarme.

Comme pour ScalingLazyColumn, le sélecteur, le curseur, le composant Stepper et les autres composables doivent être sélectionnés pour pouvoir recevoir la saisie par dispositif rotatif. Si plusieurs cibles à défilement s'affichent à l'écran (par exemple les heures et les minutes dans l'outil de sélection de l'heure), créez un FocusRequester pour chaque cible et gérez le changement de priorité en conséquence lorsque l'utilisateur appuie sur les heures ou les minutes.

@Composable
fun TimePicker() {
    var selectedColumn by remember { mutableStateOf(0) }
    val focusRequester1 = remember { FocusRequester() }
    val focusRequester2 = remember { FocusRequester() }

    Row {
       Picker(...)
       Picker(...)
    }

    LaunchedEffect(selectedColumn) {
        listOf(focusRequester1,
               focusRequester2)[selectedColumn]
            .requestFocus()
    }
}

Actions personnalisées

Vous pouvez également créer des actions personnalisées qui réagissent à la saisie par dispositif rotatif dans votre application. Par exemple, utilisez la saisie par dispositif rotatif pour faire un zoom avant ou arrière, ou pour contrôler le volume dans une application multimédia.

Si votre composant n'est pas compatible de manière native avec les événements de défilement, tels que le contrôle du volume, vous pouvez gérer vous-même les événements de défilement.

// VolumeScreen.kt

val focusRequester: FocusRequester = remember { FocusRequester() }

Column(
    modifier = Modifier
        .fillMaxSize()
        .onRotaryScrollEvent {
            // handle rotary scroll events
            true
        }
        .focusRequester(focusRequester)
        .focusable(),
) { ... }

Créez un état personnalisé géré dans le modèle de vue et un rappel personnalisé permettant de traiter les événements de défilement rotatif.

// VolumeViewModel.kt

object VolumeRange(
    public val max: Int = 10
    public val min: Int = 0
)

val volumeState: MutableStateFlow<Int> = ...

fun onVolumeChangeByScroll(pixels: Float) {
    volumeState.value = when {
        pixels > 0 -> min (volumeState.value + 1, VolumeRange.max)
        pixels < 0 -> max (volumeState.value - 1, VolumeRange.min)
    }
}

Par souci de simplicité, l'exemple précédent utilise des valeurs de pixel qui, si elles sont réellement utilisées, sont susceptibles d'être trop sensibles.

Utilisez le rappel une fois que vous avez reçu les événements, comme illustré dans l'extrait suivant.

val focusRequester: FocusRequester = remember { FocusRequester() }
val volumeState by volumeViewModel.volumeState.collectAsState()

Column(
    modifier = Modifier
        .fillMaxSize()
        .onRotaryScrollEvent {
            volumeViewModel
                .onVolumeChangeByScroll(it.verticalScrollPixels)
            true
        }
        .focusRequester(focusRequester)
        .focusable(),
) { ... }

Ressources supplémentaires

Pensez à utiliser Horologist, un projet Open Source Google fournissant un ensemble de bibliothèques Wear qui complètent les fonctionnalités proposées par Compose pour Wear OS et d'autres API Wear OS. Horologist offre une implémentation pour des cas d'utilisation avancés, ainsi que de nombreux détails spécifiques à l'appareil.

Par exemple, la sensibilité des différentes sources de saisie par dispositif rotatif peut varier. Pour une transition plus fluide entre les valeurs, vous pouvez limiter la fréquence, ou ajouter des ancrages ou des animations pour la transition. La vitesse semblera alors plus naturelle pour les utilisateurs. Horologist inclut des modificateurs pour les composants à défilement et pour les valeurs distinctes. Il comprend également des utilitaires permettant de gérer la sélection, ainsi qu'une bibliothèque d'UI audio pour mettre en œuvre le contrôle du volume à l'aide de retours haptiques.

Pour en savoir plus, consultez la page Horologist sur GitHub.