Compose 修饰符 - 样式 Style API(状态动画篇)

一、概念

Styles API 提供了一种声明式且简化的方法,可以显著减少使用 Modifier 时的样板代码。

二、使用

2.1 自带交互的组件

当组件处于对应状态时,用指定的 Style 覆盖当前设置。这些状态可以嵌套使用,如悬停再按下。这些扩展函数(条件函数)底层都是从 StyleState 中读取的,受支持的组件底层已经做了更新状态的逻辑。

StyleScope

fun StyleScope.pressed(value: Style)

按下时。

fun StyleScope.hovered(value: Style)

悬停时。

fun StyleScope.focused(value: Style)

聚焦时。

fun StyleScope.selected(value: Style)

选择时。

fun StyleScope.disabled(value: Style)

禁用时。

fun StyleScope.triStateToggleOn(value: Style)
fun StyleScope.triStateToggleOff(value: Style)
fun StyleScope.triStateToggleIndeterminate(value: Style)

切换时。

fun StyleScope.checked(value: Style)

为开时。

BaseButton(
    onClick = { },
    style = {
        // 单独使用
        focused { background(AppColor.Blue) }
        // 嵌套使用:悬停并按下
        hovered {
            border(3.dp, Color.Black)
            pressed { background(AppColor.Green) }
        }
    }
) { Text("点击") }

2.2 自定义组件

通过交互源 InteractionSource 来更新 StyleState。

MutableStyleState构造

constructor(override val interactionSource: InteractionSource?) : StyleState()

根据交互源创建样式状态。

是否按下

var isPressed: Boolean

是否悬停var isHovered: Boolean
是否聚焦var isHovered: Boolean
是否启用var isEnabled: Boolean
是否选择var isSelected: Boolean
开关状态

var triStateToggle: ToggleableState

值分别为:开 On、关 Off、无法确定 Indeterminate。

是否为开

var isChecked: Boolean

是否为 ToggleableState.On。

rememberUpdatedStyleState()

inline fun rememberUpdatedStyleState(
    interactionSource: InteractionSource?,
    block: @Composable (MutableStyleState) -> Unit = {},
): StyleState

创建、记忆并更新一个 StyleState。底层就是对 remember() + 构造函数 方式创建的包装,相比之下更适用于有函数参数更新样式状态时,如 enable 属性。

使用 remember() 创建:

  • 通过构造将 InteractionSource 传递给 MutableInteractionSource,将返回的 StyleState 传递给 Modifier.styleable()。
  • 将 InteractionSource 传递给 Modifier.focusable()、Modifier.clickable() 等。
@Composable
fun Demo(
    onClick: () -> Unit,
    modifier: Modifier = Modifier,
    style: Style = Style,
) {
    val interactionSource = remember { MutableInteractionSource() }
    val styleState = remember(interactionSource) { MutableStyleState(interactionSource) }
    Box(
        modifier = modifier
            .styleable(styleState, style)
            .clickable(interactionSource = interactionSource, onClick = onClick)
    ) { }
}

使用 rememberUpdatedStyleState() 创建:

  • 通过 rememberUpdatedStyleState() 将 InteractionSource 连接到 StyleState,将返回的 StyleState 传递给 Modifier.styleable()。
  • 将 InteractionSource 传递给 Modifier.focusable()、Modifier.clickable() 等。
@Composable
fun Demo(
    onClick: () -> Unit,
    modifier: Modifier = Modifier,
    style: Style = Style,
    enable: Boolean = true,    // 有参数更新样式状态
) {
    val interactionSource = remember { MutableInteractionSource() }
    val styleState = rememberUpdatedStyleState(interactionSource) { mutableStyleState ->
        // 通过参数更新状态
        mutableStyleState.isEnabled = enable
    }
    Box(
        modifier = modifier
            .styleable(styleState, style)
            .clickable(interactionSource = interactionSource, onClick = onClick)
    ) {...}
}

2.2 交互源联动

将 InteractionSource 添加为组件参数,如果外部未提供则在内部初始化。

@Composable
fun Demo(
    interactionSource: MutableInteractionSource? = null,
) {
    val interactionSource = interactionSource ?: remember { MutableInteractionSource() }
}

三、动画

将属性变更用 animate{} 块包裹,这与 aninmate***AsState() 类似。

animate()

fun animate(value: Style)

动画默认为 spring 效果。

fun animate(spec: AnimationSpec<Float>, value: Style)

指定动画规格。

fun animate(toSpec: AnimationSpec<Float>, fromSpec: AnimationSpec<Float>, value: Style)

分别指定进出场的动画规格。

Modifier.styleable = {
    animate {
        pressed { background(Color.Red) }
    }
    animate(spring(dampingRatio = Spring.DampingRatioMediumBouncy)) {
        scale(1.2f)
    }
}

四、自定义 StyleState

  1. 创建自定义键 StyleStateKey:提供默认值。
  2. 为 StyleState 添加扩展属性:自定义状态,获取和更新状态。
  3. 为 StyleScope 添加扩展函数:自定义条件函数,用来设置样式。
  4. 连接状态:自定义状态连接到扩展属性。

4.1 创建自定义键

enum class PlayerState {
    Stopped, Playing, Paused
}

val playerStateKey = StyleStateKey(PlayerState.Stopped)

4.2 为 StyleState 添加扩展属性

var MutableStyleState.playerState
    get() = this[playerStateKey]
    set(value) { this[playerStateKey] = value }

4.3 为 StyleScope 添加扩展函数

state()

fun <T> state(
    key: StyleStateKey<T>,        //自定义的key
    value: Style,        // 要应用的样式
    active: (key: StyleStateKey<T>, state: StyleState) -> Boolean,        //执行条件
)

用于创建条件函数。

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

4.4 连接状态

@Composable
fun Player(
    modifier: Modifier = Modifier,
    style: Style = Style,
    // 将自定义状态作为参数传入
    playerState: PlayerState =  PlayerState.Stopped
) {
    val styleState = remember { MutableStyleState(null) }
    // 将自定义状态连接到样式状态
    // 不要通过 key 来设置(styleState[playerStateKey] = playerState)
    // key 属于 API 提供方使用,不应该是调用方使用
    styleState.playerState = playerState
    Box(
        modifier = modifier.styleable(styleState, style)
    ) {...}
}

4.5 使用

@Composable
fun Demo() {
    Player(
        style = Style {
            playerPaused {...}
            playerPlaying {...}
        }
    )
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值