Wprowadzanie obrotowe z funkcją tworzenia


Pokrętło to element zegarka, który się obraca. Użytkownicy spędzają średnio tylko kilka sekund na interakcji z zegarkiem. Możesz poprawić wrażenia użytkowników, używając pokrętła, aby umożliwić im szybkie wykonywanie różnych zadań.

W większości zegarków są 3 główne źródła danych wejściowych z pokrętła: obrotowy przycisk boczny (RSB) oraz fizyczny lub dotykowy pierścień, który jest okrągłą strefą dotykową wokół ekranu. Chociaż oczekiwane działanie może się różnić w zależności od typu danych wejściowych, pamiętaj, aby obsługiwać pokrętło we wszystkich najważniejszych interakcjach.

Wskaźnik przewijania

Większość użytkowników oczekuje, że aplikacje będą obsługiwać gest przewijania. Gdy treść przewija się na ekranie, przekazuj użytkownikom wizualne informacje zwrotne w odpowiedzi na interakcje z pokrętłem. Wizualne informacje zwrotne mogą obejmować wskaźniki przewijania w przypadku przewijania w pionie lub wskaźniki stron.

ScalingLazyColumn, TransformingLazyColumn i Picker domyślnie obsługują gest przewijania, jeśli umieścisz te komponenty wewnątrz AppScaffold i ScreenScaffold oraz przekażesz stan listy między ScreenScaffold a komponentem, takim jak TransformingLazyColumn.

AppScaffold i ScreenScaffold zapewniają podstawową strukturę układu aplikacji na Wear OS i mają już miejsce na wskaźnik przewijania z domyślną implementacją. Aby dostosować postęp przewijania, utwórz wskaźnik przewijania na podstawie obiektu stanu listy, jak pokazano w tym fragmencie kodu:

val listState = rememberTransformingLazyColumnState()
ScreenScaffold(
    scrollState = listState,
    scrollIndicator = {
        ScrollIndicator(state = listState)
    }
) {
    // ...
}

Możesz skonfigurować zachowanie przyciągania dla ScalingLazyColumn za pomocą ScalingLazyColumnDefaults.snapFlingBehavior, jak pokazano w tym fragmencie kodu:

val listState = rememberScalingLazyListState()
ScreenScaffold(
    scrollState = listState,
    scrollIndicator = {
        ScrollIndicator(state = listState)
    }
) {

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

Działania niestandardowe

Możesz też tworzyć działania niestandardowe, które reagują na pokrętło w aplikacji. Możesz na przykład używać pokrętła do powiększania i pomniejszania lub do sterowania głośnością w aplikacji do multimediów.

Jeśli komponent nie obsługuje natywnie zdarzeń przewijania, takich jak regulacja głośności, możesz samodzielnie obsługiwać zdarzenia przewijania.

Pierwszym krokiem jest utworzenie stanu niestandardowego zarządzanego w modelu widoku oraz niestandardowego wywołania zwrotnego, które służy do przetwarzania zdarzeń przewijania obrotowego.

class VolumeRange(
    val max: Int = 10,
    val min: Int = 0
)

private object VolumeViewModel {
    class MyViewModel : ViewModel() {
        private val _volumeState = mutableIntStateOf(0)
        val volumeState: State<Int>
            get() = _volumeState

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

Następnie użyj wywołania zwrotnego po otrzymaniu zdarzeń, jak pokazano w tym fragmencie kodu.

val focusRequester: FocusRequester = remember { FocusRequester() }
val volumeViewModel: VolumeViewModel.MyViewModel =
    viewModel()
val volumeState by volumeViewModel.volumeState

TransformingLazyColumn(
    modifier = Modifier
        .fillMaxSize()
        .onRotaryScrollEvent {
            volumeViewModel.onVolumeChangeByScroll(it.verticalScrollPixels)
            true
        }
        .focusRequester(focusRequester)
        .focusable(),
) {
    // You can use volumeState here, for example:
    item {
        Text("Volume: $volumeState")
    }
}

Pamiętaj, że dla uproszczenia w powyższym przykładzie użyto wartości pikseli, które w praktyce byłyby prawdopodobnie zbyt czułe.