API استایلها یک رویکرد اعلانی و ساده برای مدیریت تغییرات رابط کاربری در طول حالتهای تعاملی مانند hovered ، focused و pressed ارائه میدهد. با استفاده از این API، میتوانید کد تکراری که معمولاً هنگام استفاده از اصلاحکنندهها مورد نیاز است را به میزان قابل توجهی کاهش دهید.
برای تسهیل استایلدهی واکنشی، StyleState به عنوان یک رابط پایدار و فقط خواندنی عمل میکند که وضعیت فعال یک عنصر (مانند وضعیت فعال، فشرده یا متمرکز) را ردیابی میکند. در یک StyleScope ، میتوانید از طریق ویژگی state به این دسترسی داشته باشید تا منطق شرطی را مستقیماً در تعاریف Style خود پیادهسازی کنید.
تعامل مبتنی بر وضعیت: معلق ماندن روی ماوس، فوکوس، فشردن، انتخاب شدن، فعال شدن، تغییر وضعیت
استایلها با پشتیبانی داخلی برای تعاملات رایج ارائه میشوند:
- فشرده شده
- معلق
- انتخاب شده
- فعال شده
- فعال/غیرفعال
همچنین میتوان از حالتهای سفارشی پشتیبانی کرد. برای اطلاعات بیشتر به بخش «طراحی حالت سفارشی با StyleState» مراجعه کنید.
مدیریت حالتهای تعامل با پارامترهای Style
مثال زیر تغییر background و borderColor در پاسخ به حالتهای تعامل نشان میدهد، به طور خاص تغییر به بنفش هنگام قرار گرفتن موس روی صفحه و آبی هنگام فوکوس:
@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) }) } ) }
شما همچنین میتوانید تعاریف حالت تو در تو ایجاد کنید. برای مثال، میتوانید یک سبک خاص برای زمانی که یک دکمه همزمان فشرده و نگه داشته میشود، تعریف کنید:
@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
هنگام ایجاد کامپوننتهای styleable خودتان، باید یک interactionSource به یک styleState متصل کنید. سپس، این state را به Modifier.styleable ارسال کنید تا از آن استفاده شود.
سناریویی را در نظر بگیرید که سیستم طراحی شما شامل یک GradientButton باشد. ممکن است بخواهید یک LoginButton ایجاد کنید که از GradientButton ارثبری کند، اما رنگهای آن در طول تعاملات، مانند فشرده شدن، تغییر کند.
- برای فعال کردن بهروزرسانیهای سبک
interactionSource، یکinteractionSourceبه عنوان پارامتر در composable خود وارد کنید. از پارامتر ارائه شده استفاده کنید یا اگر پارامتری ارائه نشده است، یکMutableInteractionSourceجدید را مقداردهی اولیه کنید. - با ارائه
interactionSourcestyleStateمقداردهی اولیه کنید. مطمئن شوید که وضعیت enabled درstyleState، مقدار پارامتر enabled ارائه شده را نشان میدهد. -
interactionSourceبه اصلاحکنندههایfocusableوclickableاختصاص دهید. در نهایت،styleStateبه پارامترstyleableاصلاحکننده اعمال کنید.
@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, ) }
اکنون میتوانید از حالت interactionSource برای اعمال تغییرات استایل با گزینههای فشردهشده، متمرکز و معلقشده در داخل بلوک استایل استفاده کنید:
@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 .تغییرات سبک متحرک
تغییرات حالت استایلها با پشتیبانی از انیمیشن داخلی ارائه میشوند. میتوانید ویژگی جدید را درون هر بلوک تغییر حالت با animate قرار دهید تا به طور خودکار انیمیشنها بین حالتهای مختلف اضافه شوند. این مشابه APIهای animate*AsState است. مثال زیر وقتی حالت به focus تغییر میکند، borderColor را از سیاه به آبی متحرک میکند:
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)) { } }
API animate یک animationSpec برای تغییر مدت زمان یا شکل منحنی انیمیشن میپذیرد. مثال زیر اندازه جعبه را با یک spring spec متحرک میکند:
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
بسته به مورد استفادهی Composable شما، ممکن است سبکهای مختلفی داشته باشید که توسط حالتهای سفارشی پشتیبانی میشوند. برای مثال، اگر یک برنامهی رسانهای دارید، ممکن است بخواهید بسته به حالت پخش پخشکننده، سبکبندی متفاوتی برای دکمههای موجود در MediaPlayer Composable خود داشته باشید. برای ایجاد و استفاده از حالت سفارشی خود، این مراحل را دنبال کنید:
- تعریف کلید سفارشی
- ایجاد افزونه
StyleState - پیوند به حالت سفارشی
تعریف کلید سفارشی
برای ایجاد یک استایل سفارشی مبتنی بر وضعیت، ابتدا یک StyleStateKey ایجاد کنید و مقدار پیشفرض وضعیت را به آن بدهید. وقتی برنامه اجرا میشود، پخشکننده رسانه در وضعیت Stopped قرار دارد، بنابراین به این روش مقداردهی اولیه میشود:
enum class PlayerState { Stopped, Playing, Paused } val playerStateKey = StyleStateKey(PlayerState.Stopped)
ایجاد توابع افزونه StyleState
یک تابع افزونه روی StyleState تعریف کنید تا playState فعلی را جستجو کند. سپس، توابع افزونهای روی StyleScope ایجاد کنید که حالتهای سفارشی شما را در playStateKey ، یک لامبدا با حالت خاص و سبک مورد نظر ارسال میکنند.
// 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 }) }
پیوند به حالت سفارشی
styleState در composable خود تعریف کنید و styleState.playState را برابر با وضعیت ورودی قرار دهید. styleState به تابع styleable روی اصلاحکننده ارسال کنید.
@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 لامبدا، میتوانید با استفاده از توابع افزونهی تعریفشدهی قبلی، استایلبندی مبتنی بر حالت را برای حالتهای سفارشی اعمال کنید.
@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) }
کد زیر قطعه کد کامل این مثال است:
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) }