Input dari alat rotasi dengan Compose


Input dari alat rotasi mengacu pada input dari smartwatch yang berputar atau berotasi. Rata-rata, pengguna hanya menghabiskan beberapa detik untuk berinteraksi dengan smartwatch. Anda dapat meningkatkan pengalaman pengguna menggunakan input dari alat rotasi agar pengguna dapat menyelesaikan berbagai tugas dengan cepat.

Tiga sumber utama input dari alat rotasi pada sebagian besar smartwatch meliputi tombol samping yang berputar (RSB), dan bingkai fisik atau bingkai sentuh, yang merupakan zona sentuh melingkar di sekitar layar. Meskipun perilaku yang diharapkan dapat berbeda-beda berdasarkan jenis input, pastikan input dari alat rotasi untuk semua interaksi penting dapat didukung.

Scroll

Sebagian besar pengguna berharap aplikasi mendukung gestur scroll. Saat konten di-scroll di layar, berikan masukan visual kepada pengguna sebagai respons terhadap interaksi putar. Masukan visual dapat menyertakan indikator scroll untuk scroll vertikal atau indikator halaman.

ScalingLazyColumn, TransformingLazyColumn, dan Picker mendukung gestur scroll secara default, selama Anda perlu menempatkan komponen tersebut di dalam AppScaffold dan ScreenScaffold serta meneruskan status daftar antara ScreenScaffold dan komponen, seperti TransformingLazyColumn.

AppScaffold dan ScreenScaffold menyediakan struktur tata letak dasar untuk aplikasi Wear OS dan telah dilengkapi slot untuk indikator scroll dengan implementasi default. Untuk menyesuaikan progres scroll, buat indikator scroll berdasarkan objek status daftar, seperti yang ditunjukkan dalam cuplikan kode berikut:

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

Anda dapat mengonfigurasi perilaku snap untuk ScalingLazyColumn menggunakan ScalingLazyColumnDefaults.snapFlingBehavior, seperti yang ditunjukkan dalam cuplikan kode berikut:

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

Tindakan kustom

Anda juga bisa membuat tindakan khusus yang merespons input dari alat rotasi di aplikasi Anda. Misalnya, gunakan input dari alat rotasi untuk memperbesar dan memperkecil atau mengontrol volume di aplikasi media.

Jika komponen Anda tidak mendukung peristiwa scroll seperti kontrol volume secara native, Anda dapat menangani peristiwa scroll sendiri.

// VolumeScreen.kt

val focusRequester: FocusRequester = remember { FocusRequester() }

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

Membuat status kustom yang dikelola di model tampilan, dan callback kustom yang digunakan untuk memproses peristiwa scroll putar.

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

Agar lebih praktis, contoh sebelumnya menggunakan nilai piksel yang jika benar-benar digunakan justru akan cenderung terlalu sensitif.

Gunakan callback setelah Anda menerima peristiwa, seperti yang ditunjukkan dalam cuplikan berikut.

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

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