Entrada por seletor giratório com o Compose

O termo "entrada por seletor giratório" se refere a ações feitas usando peças do relógio que giram. Em média, os usuários passam apenas alguns segundos interagindo com o relógio. É possível melhorar a experiência usando a entrada por seletor giratório para que os usuários possam realizar várias tarefas rapidamente.

As três principais fontes de entrada por seletor giratório na maioria dos relógios incluem o botão lateral giratório (RSB, na sigla em inglês) e uma borda física ou sensível ao toque, que é uma zona de toque circular ao redor da tela. Embora o comportamento esperado possa variar de acordo com o tipo de entrada, recomendamos que você ofereça suporte à entrada por seletor giratório para todas as interações essenciais.

Rolagem

A maioria dos usuários espera que os apps tenham suporte ao gesto de rolagem. À medida que o conteúdo passa pela tela, ofereça feedback visual aos usuários em resposta a interações pelo seletor giratório. O feedback visual pode incluir indicadores de posição para rolagem vertical ou indicadores de página.

ScalingLazyColumn e Picker oferecem suporte ao gesto de rolagem por padrão, desde que você precise colocar esses componentes dentro de um Scaffold. O Scaffold fornece a estrutura de layout básica usada em apps para Wear OS e já tem um slot para um indicador de rolagem. Para mostrar o progresso da rolagem, crie um indicador de posição com base no objeto de estado da lista, conforme mostrado no snippet de código abaixo:

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

É possível configurar um comportamento de ajuste para ScalingLazyColumn usando ScalingLazyColumnDefaults.snapFlingBehavior, conforme mostrado no snippet de código a seguir:

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

Ações personalizadas

Também é possível criar ações personalizadas que respondam à entrada por seletor giratório no app. Por exemplo, use a entrada por seletor giratório para aumentar e diminuir o zoom ou controlar o volume em um app de música.

Se o componente não tiver suporte nativo a eventos de rolagem, como o controle de volume, você poderá processar esses eventos por conta própria.

// VolumeScreen.kt  val focusRequester: FocusRequester = remember { FocusRequester() }  Column(     modifier = Modifier         .fillMaxSize()         .onRotaryScrollEvent {             // handle rotary scroll events             true         }         .focusRequester(focusRequester)         .focusable(), ) { ... } 

Crie um estado personalizado gerenciado no modelo de visualização e um callback individualizado que seja usado para processar eventos de rolagem por seletor giratório.

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

Para simplificar, o exemplo anterior usa valores de pixel que, se usados de fato, serão muito sensíveis.

Use o callback quando receber os eventos, conforme mostrado no snippet a seguir.

val focusRequester: FocusRequester = remember { FocusRequester() } val volumeState by volumeViewModel.volumeState.collectAsState()  Column(     modifier = Modifier         .fillMaxSize()         .onRotaryScrollEvent {             volumeViewModel                 .onVolumeChangeByScroll(it.verticalScrollPixels)             true         }         .focusRequester(focusRequester)         .focusable(), ) { ... }