Entrada rotativa con Compose

La entrada rotativa hace referencia a la entrada de piezas de tu reloj que giran o rotan. En promedio, los usuarios solo interactúan unos segundos con su reloj. Puedes mejorar la experiencia del usuario con la entrada rotativa para que este pueda realizar varias tareas rápidamente.

Las tres fuentes principales de entrada rotativa en la mayoría de los relojes incluyen el botón lateral giratorio (RSB) y un bisel físico o un bisel táctil, que es una zona táctil circular alrededor de la pantalla. Aunque el comportamiento esperado puede variar según el tipo de entrada, asegúrate de admitir la entrada rotativa para todas las interacciones esenciales.

Desplazamiento

La mayoría de los usuarios esperan que las apps admitan el gesto de desplazamiento. A medida que el contenido se desplaza en la pantalla, brinda a los usuarios respuestas visuales a las interacciones rotativas. Las respuestas visuales pueden incluir indicadores de posición para el desplazamiento vertical o indicadores de página.

ScalingLazyColumn y Picker admiten el gesto de desplazamiento de forma predeterminada, siempre que necesites colocar esos componentes dentro de un Scaffold. Scaffold proporciona la estructura de diseño básica de apps para Wear OS y ya tiene una ranura para un indicador de desplazamiento. Para mostrar el progreso del desplazamiento, crea un indicador de posición basado en el objeto de estado de la lista, como se muestra en el siguiente fragmento de código:

val listState = rememberScalingLazyListState()
Scaffold(
    positionIndicator = {
        PositionIndicator(scalingLazyListState = listState)
    }
) {
    // ...
}

Puedes configurar un comportamiento de ajuste para ScalingLazyColumn con ScalingLazyColumnDefaults.snapFlingBehavior, como se muestra en el siguiente fragmento de código:

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

    val state = rememberScalingLazyListState()
    ScalingLazyColumn(
        modifier = Modifier.fillMaxWidth(),
        state = state,
        flingBehavior = ScalingLazyColumnDefaults.snapFlingBehavior(state = state)
    ) {
        // Content goes here
        // ...
    }
}

Acciones personalizadas

También puedes crear acciones personalizadas que respondan a entradas rotativas en tu app. Por ejemplo, usa la entrada rotativa para acercar y alejar, o para controlar el volumen en una app de contenido multimedia.

Si tu componente no admite eventos de desplazamiento de forma nativa, como el control de volumen, puedes controlarlos por tu cuenta.

// VolumeScreen.kt

val focusRequester: FocusRequester = remember { FocusRequester() }

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

Crea un estado personalizado administrado en un modelo de vistas y una devolución de llamada personalizada que se use para procesar eventos de desplazamiento rotativos.

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

Por razones de simplicidad, en el ejemplo anterior se utilizan valores de píxeles que, si se usan, podrían ser demasiado sensibles.

Usa la devolución de llamada una vez que recibas los eventos, como se muestra en el siguiente fragmento.

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

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