Styles API menawarkan pendekatan deklaratif dan efisien untuk mengelola perubahan UI selama status interaksi seperti hovered, focused, dan pressed. Dengan API ini, Anda dapat mengurangi kode boilerplate secara signifikan yang biasanya diperlukan saat menggunakan pengubah.
Untuk memfasilitasi gaya reaktif, StyleState bertindak sebagai antarmuka stabil dan hanya baca yang melacak status aktif elemen (seperti status diaktifkan, ditekan, atau difokuskan). Dalam StyleScope, Anda dapat mengaksesnya melalui properti state untuk menerapkan logika kondisional langsung dalam definisi Gaya.
Interaksi berbasis status: Diarahkan, difokuskan, ditekan, dipilih, diaktifkan, diubah
Gaya dilengkapi dukungan bawaan untuk interaksi umum:
- Ditekan
- Diarahkan
- Dipilih
- Diaktifkan
- Diubah
Anda juga dapat mendukung status kustom. Lihat bagian Gaya Visual Status Kustom dengan StyleState untuk mengetahui informasi selengkapnya.
Menangani status interaksi dengan parameter Gaya
Contoh berikut menunjukkan cara mengubah background dan borderColor sebagai respons terhadap status interaksi, khususnya beralih ke ungu saat diarahkan dan biru saat difokuskan:
@Preview @Composable private fun OpenButton() { BaseButton( style = outlinedButtonStyle then { background(Color.White) hovered { background(lightPurple) border(2.dp, lightPurple) } focused { background(lightBlue) } }, onClick = { }, content = { BaseText("Open in Studio", style = { contentColor(Color.Black) fontSize(26.sp) textAlign(TextAlign.Center) }) } ) }
Anda juga dapat membuat definisi status bertingkat. Misalnya, Anda dapat menentukan gaya tertentu saat tombol ditekan dan diarahkan secara bersamaan:
@Composable private fun OpenButton_CombinedStates() { BaseButton( style = outlinedButtonStyle then { background(Color.White) hovered { // light purple background(lightPurple) pressed { // When running on a device that can hover, whilst hovering and then pressing the button this would be invoked background(lightOrange) } } pressed { // when running on a device without a mouse attached, this would be invoked as you wouldn't be in a hovered state only background(lightRed) } focused { background(lightBlue) } }, onClick = { }, content = { BaseText("Open in Studio", style = { contentColor(Color.Black) fontSize(26.sp) textAlign(TextAlign.Center) }) } ) }
Composable kustom dengan Modifier.styleable
Saat membuat komponen styleable Anda sendiri, Anda harus menghubungkan interactionSource ke styleState. Kemudian, teruskan status ini ke Modifier.styleable untuk menggunakannya.
Pertimbangkan skenario saat sistem desain Anda menyertakan GradientButton. Anda mungkin ingin membuat LoginButton yang mewarisi dari GradientButton, tetapi mengubah warnanya selama interaksi, seperti saat ditekan.
- Untuk mengaktifkan update gaya
interactionSource, sertakaninteractionSourcesebagai parameter dalam composable Anda. Gunakan parameter yang disediakan atau, jika tidak ada, inisialisasiMutableInteractionSourcebaru. - Inisialisasi
styleStatedengan memberikaninteractionSource. Pastikan status diaktifkanstyleStatemencerminkan nilai parameter diaktifkan yang disediakan. - Tetapkan
interactionSourceke pengubahfocusabledanclickable. Terakhir, terapkanstyleStateke parameterstyleablepengubah.
@Composable private fun GradientButton( onClick: () -> Unit, modifier: Modifier = Modifier, style: Style = Style, enabled: Boolean = true, interactionSource: MutableInteractionSource? = null, content: @Composable RowScope.() -> Unit, ) { val interactionSource = interactionSource ?: remember { MutableInteractionSource() } val styleState = rememberUpdatedStyleState(interactionSource) { it.isEnabled = enabled } Row( modifier = modifier .clickable( onClick = onClick, enabled = enabled, interactionSource = interactionSource, indication = null, ) .styleable(styleState, baseGradientButtonStyle then style), content = content, ) }
Sekarang Anda dapat menggunakan status interactionSource untuk mendorong perubahan gaya dengan opsi ditekan, difokuskan, dan diarahkan di dalam blok gaya:
@Preview @Composable fun LoginButton() { val loginButtonStyle = Style { pressed { background( Brush.linearGradient( listOf(Color.Magenta, Color.Red) ) ) } } GradientButton(onClick = { // Login logic }, style = loginButtonStyle) { BaseText("Login") } }
interactionSource.Menganimasikan perubahan gaya
Perubahan status gaya dilengkapi dukungan animasi bawaan. Anda dapat menggabungkan properti baru dalam blok perubahan status apa pun dengan animate untuk otomatis menambahkan animasi antar-status yang berbeda. Hal ini mirip dengan animate*AsState API. Contoh berikut menganimasikan borderColor dari hitam menjadi biru saat status berubah menjadi difokuskan:
val animatingStyle = Style { externalPadding(48.dp) border(3.dp, Color.Black) background(Color.White) size(100.dp) pressed { animate { borderColor(Color.Magenta) background(Color(0xFFB39DDB)) } } } @Preview @Composable private fun AnimatingStyleChanges() { val interactionSource = remember { MutableInteractionSource() } val styleState = remember(interactionSource) { MutableStyleState(interactionSource) } Box(modifier = Modifier .clickable( interactionSource, enabled = true, indication = null, onClick = { } ) .styleable(styleState, animatingStyle)) { } }
animate API menerima animationSpec untuk mengubah durasi atau bentuk kurva animasi. Contoh berikut menganimasikan ukuran kotak dengan spesifikasi spring:
val animatingStyleSpec = Style { externalPadding(48.dp) border(3.dp, Color.Black) background(Color.White) size(100.dp) transformOrigin(TransformOrigin.Center) pressed { animate { borderColor(Color.Magenta) background(Color(0xFFB39DDB)) } animate(spring(dampingRatio = Spring.DampingRatioMediumBouncy)) { scale(1.2f) } } } @Preview(showBackground = true) @Composable fun AnimatingStyleChangesSpec() { val interactionSource = remember { MutableInteractionSource() } val styleState = remember(interactionSource) { MutableStyleState(interactionSource) } Box(modifier = Modifier .clickable( interactionSource, enabled = true, indication = null, onClick = { } ) .styleable(styleState, animatingStyleSpec)) }
Gaya visual status kustom dengan StyleState
Bergantung pada kasus penggunaan composable, Anda mungkin memiliki gaya yang berbeda yang didukung oleh status kustom. Misalnya, jika Anda memiliki aplikasi media, Anda mungkin ingin memiliki gaya yang berbeda untuk tombol di composable MediaPlayer bergantung pada status pemutaran pemutar. Ikuti langkah-langkah berikut untuk membuat dan menggunakan status kustom Anda sendiri:
- Menentukan kunci kustom
- Membuat ekstensi
StyleState - Menautkan ke status kustom
Menentukan kunci kustom
Untuk membuat gaya berbasis status kustom, buat
StyleStateKey terlebih dahulu dan teruskan nilai status default. Saat aplikasi diluncurkan, pemutar media berada dalam status Stopped, sehingga diinisialisasi dengan cara ini:
enum class PlayerState { Stopped, Playing, Paused } val playerStateKey = StyleStateKey(PlayerState.Stopped)
Membuat fungsi ekstensi StyleState
Tentukan fungsi ekstensi di StyleState untuk membuat kueri playState saat ini.
Kemudian, buat fungsi ekstensi di StyleScope dengan status kustom Anda yang meneruskan playStateKey, lambda dengan status tertentu, dan gaya.
// Extension Function on MutableStyleState to query and set the current playState var MutableStyleState.playerState get() = this[playerStateKey] set(value) { this[playerStateKey] = value } fun StyleScope.playerPlaying(value: Style) { state(playerStateKey, value, { key, state -> state[key] == PlayerState.Playing }) } fun StyleScope.playerPaused(value: Style) { state(playerStateKey, value, { key, state -> state[key] == PlayerState.Paused }) }
Menautkan ke status kustom
Tentukan styleState di composable Anda dan tetapkan styleState.playState
sama dengan status masuk. Teruskan styleState ke fungsi styleable pada pengubah.
@Composable fun MediaPlayer( url: String, modifier: Modifier = Modifier, style: Style = Style, state: PlayerState = remember { PlayerState.Paused } ) { // Hoist style state, set playstate as a parameter, val styleState = remember { MutableStyleState(null) } // Set equal to incoming state to link the two together styleState.playerState = state Box( modifier = modifier.styleable(styleState, style)) { ///.. } }
Dalam lambda style, Anda dapat menerapkan gaya visual berbasis status untuk status kustom, menggunakan fungsi ekstensi yang ditentukan sebelumnya.
@Composable fun StyleStateKeySample() { // Using the extension function to change the border color to green while playing val style = Style { borderColor(Color.Gray) playerPlaying { animate { borderColor(Color.Green) } } playerPaused { animate { borderColor(Color.Blue) } } } val styleState = remember { MutableStyleState(null) } styleState[playerStateKey] = PlayerState.Playing // Using the style in a composable that sets the state -> notice if you change the state parameter, the style changes. You can link this up to an ViewModel and change the state from there too. MediaPlayer(url = "https://example.com/media/video", style = style, state = PlayerState.Stopped) }
Kode berikut adalah cuplikan lengkap untuk contoh ini:
enum class PlayerState { Stopped, Playing, Paused } val playerStateKey = StyleStateKey<PlayerState>(PlayerState.Stopped) var MutableStyleState.playerState get() = this[playerStateKey] set(value) { this[playerStateKey] = value } fun StyleScope.playerPlaying(value: Style) { state(playerStateKey, value, { key, state -> state[key] == PlayerState.Playing }) } fun StyleScope.playerPaused(value: Style) { state(playerStateKey, value, { key, state -> state[key] == PlayerState.Paused }) } @Composable fun MediaPlayer( url: String, modifier: Modifier = Modifier, style: Style = Style, state: PlayerState = remember { PlayerState.Paused } ) { // Hoist style state, set playstate as a parameter, val styleState = remember { MutableStyleState(null) } // Set equal to incoming state to link the two together styleState.playerState = state Box( modifier = modifier.styleable(styleState, Style { size(100.dp) border(2.dp, Color.Red) }, style, )) { ///.. } } @Composable fun StyleStateKeySample() { // Using the extension function to change the border color to green while playing val style = Style { borderColor(Color.Gray) playerPlaying { animate { borderColor(Color.Green) } } playerPaused { animate { borderColor(Color.Blue) } } } val styleState = remember { MutableStyleState(null) } styleState[playerStateKey] = PlayerState.Playing // Using the style in a composable that sets the state -> notice if you change the state parameter, the style changes. You can link this up to an ViewModel and change the state from there too. MediaPlayer(url = "https://example.com/media/video", style = style, state = PlayerState.Stopped) }