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
baca-saja yang stabil dan melacak status aktif elemen (seperti status diaktifkan,
ditekan, atau difokuskan). Dalam StyleScope, Anda dapat mengaksesnya melalui
properti state untuk menerapkan logika bersyarat langsung dalam definisi
Gaya.
Interaksi berbasis status: Diarahkan kursor, difokuskan, ditekan, dipilih, diaktifkan, diubah
Gaya dilengkapi dengan dukungan bawaan untuk interaksi umum:
- Ditekan
- Diarahkan
- Dipilih
- Aktif
- Diubah
Anda juga dapat mendukung status kustom. Lihat bagian Penataan Gaya 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 di-hover
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 kursor 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 sendiri, Anda harus menghubungkan
interactionSource ke styleState. Kemudian, teruskan status ini ke
Modifier.styleable untuk memanfaatkannya.
Pertimbangkan skenario saat sistem desain Anda menyertakan GradientButton. Anda
mungkin ingin membuat LoginButton yang diwarisi dari GradientButton, tetapi
mengubah warnanya selama interaksi, seperti saat ditekan.
- Untuk mengaktifkan pembaruan gaya
interactionSource, sertakaninteractionSourcesebagai parameter dalam composable Anda. Gunakan parameter yang disediakan atau, jika tidak ada yang diberikan, inisialisasiMutableInteractionSourcebaru. - Lakukan inisialisasi
styleStatedengan memberikaninteractionSource. Pastikan status aktifstyleStatemencerminkan nilai parameter aktif yang diberikan. - 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 = remember(interactionSource) { MutableStyleState(interactionSource) } styleState.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 modifikasi gaya dengan opsi ditekan, difokuskan, dan diarahkan kursor 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 dengan dukungan animasi bawaan. Anda dapat membungkus properti
baru dalam blok perubahan status dengan animate untuk otomatis menambahkan
animasi di antara berbagai status. Hal ini mirip dengan API animate*AsState. Contoh berikut menganimasikan borderColor dari hitam menjadi biru saat
status berubah menjadi fokus:
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 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
- Buat ekstensi
StyleState - Link ke status kustom
Menentukan kunci kustom
Untuk membuat gaya berbasis status kustom, pertama-tama buat
StyleStateKey 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 mengkueri playState saat ini.
Kemudian, buat fungsi ekstensi di StyleScope dengan meneruskan status kustom Anda
di 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 }) }
Link 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 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) }