Styles API, hovered, focused ve pressed gibi etkileşim durumları sırasında kullanıcı arayüzü değişikliklerini yönetmek için bildirim temelli ve kolaylaştırılmış bir yaklaşım sunar. Bu API ile, değiştiriciler kullanılırken genellikle gerekli olan standart kodu önemli ölçüde azaltabilirsiniz.
Reaktif stil oluşturmayı kolaylaştırmak için StyleState, bir öğenin etkin durumunu (ör. etkin, basılı veya odaklanılmış durumu) izleyen kararlı ve salt okunur bir arayüz görevi görür. Bir StyleScope içinde, koşullu mantığı doğrudan stil tanımlarınızda uygulamak için state özelliği üzerinden bu özelliğe erişebilirsiniz.
Duruma dayalı etkileşim: Fareyle üzerine gelme, odaklanma, basma, seçme, etkinleştirme, değiştirme
Stiller, yaygın etkileşimler için yerleşik destekle birlikte gelir:
- Basıldı
- Fareyle üzerine gelindiğinde
- Seçili
- Etkin
- Açma/kapatma
Özel durumları da desteklemek mümkündür. Daha fazla bilgi için StyleState ile Özel Durum Stili Oluşturma bölümüne bakın.
Stil parametreleriyle etkileşim durumlarını işleme
Aşağıdaki örnekte, etkileşim durumlarına yanıt olarak background ve borderColor öğelerinin nasıl değiştirileceği gösterilmektedir. Özellikle fareyle üzerine gelindiğinde mor, odaklanıldığında ise mavi renge geçiş yapılmaktadır:
@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) }) } ) }
İç içe yerleştirilmiş durum tanımları da oluşturabilirsiniz. Örneğin, bir düğmeye hem basıldığında hem de fareyle üzerine gelindiğinde kullanılacak belirli bir stil tanımlayabilirsiniz:
@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) }) } ) }
Modifier.styleable ile özel composable'lar
Kendi styleable bileşenlerinizi oluştururken bir interactionSource öğesini styleState öğesine bağlamanız gerekir. Ardından, bu durumu kullanmak için Modifier.styleable'a aktarın.
Tasarım sisteminizin GradientButton içerdiği bir senaryoyu ele alalım. GradientButton öğesinden devralan ancak etkileşimler sırasında (ör. basıldığında) renklerini değiştiren bir LoginButton oluşturmak isteyebilirsiniz.
interactionSourcestil güncellemelerini etkinleştirmek için composable'ınıza parametre olarakinteractionSourceekleyin. Sağlanan parametreyi kullanın veya parametre sağlanmamışsa yeni birMutableInteractionSourcebaşlatın.interactionSourcedeğerini sağlayarakstyleStatedeğerini başlatın.styleState'nın etkinleştirilmiş durumunun, sağlanan etkinleştirilmiş parametrenin değerini yansıttığından emin olun.interactionSourceöğesinifocusableveclickabledeğiştiricilerine atayın. Son olarak,styleStatedeğerini değiştiricininstyleableparametresine uygulayın.
@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, ) }
Artık stil bloğundaki basılı, odaklanılmış ve fareyle üzerine gelinen seçenekleriyle stil değişikliklerini yönlendirmek için interactionSource durumunu kullanabilirsiniz:
@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 değerine göre özel bir composable durumunu değiştirme.Stil değişikliklerine animasyon uygulama
Stil durumu değişiklikleri, yerleşik animasyon desteğiyle birlikte gelir. Yeni özelliği, farklı durumlar arasında otomatik olarak animasyon eklemek için animate ile herhangi bir durum değişikliği bloğuna sarabilirsiniz. Bu, animate*AsState API'lerine benzer. Aşağıdaki örnekte, durum odaklanmış olarak değiştiğinde borderColor siyah renkten mavi renge animasyonla dönüştürülür:
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, animasyon eğrisinin süresini veya şeklini değiştirmek için animationSpec kabul eder. Aşağıdaki örnekte, spring spesifikasyonuyla kutunun boyutu animasyonlu hale getiriliyor:
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)) }
StyleState ile özel durum stili
Birleştirilebilir kullanım alanınıza bağlı olarak, özel durumlarla desteklenen farklı stilleriniz olabilir. Örneğin, bir medya uygulamanız varsa oynatıcının oynatma durumuna bağlı olarak MediaPlayer composable'ınızdaki düğmeler için farklı stiller kullanmak isteyebilirsiniz. Kendi özel durumunuzu oluşturmak ve kullanmak için aşağıdaki adımları uygulayın:
- Özel anahtar tanımlama
StyleStateuzantısı oluşturma- Özel duruma bağlantı
Özel anahtar tanımlama
Özel duruma dayalı stil oluşturmak için önce bir StyleStateKey oluşturun ve varsayılan durum değerini iletin. Uygulama başlatıldığında medya oynatıcı Stopped durumunda olur. Bu nedenle, aşağıdaki şekilde başlatılır:
enum class PlayerState { Stopped, Playing, Paused } val playerStateKey = StyleStateKey(PlayerState.Stopped)
StyleState uzantısı işlevleri oluşturma
Mevcut playState sorgulamak için StyleState üzerinde bir uzantı işlevi tanımlayın.
Ardından, StyleScope üzerinde uzantı işlevleri oluşturun. Bu işlevlerde, playStateKey içinde özel durumlarınız, belirli bir duruma sahip lambda ve stil iletilir.
// 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 }) }
Özel duruma bağlantı
Composable'ınızda styleState öğesini tanımlayın ve styleState.playState öğesini gelen duruma eşit olacak şekilde ayarlayın. styleState öğesini değiştiricideki styleable işlevine iletin.
@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)) { ///.. } }
style lambda'sında, daha önce tanımlanmış uzantı işlevlerini kullanarak özel durumlar için duruma dayalı stil uygulayabilirsiniz.
@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) }
Aşağıdaki kod, bu örneğin tam snippet'idir:
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) }