ToggleButton:状态不是点击动画 · AndroidX 源码指南
AAndroidX 源码指南
M3E 设计系统
ANDROIDX / COMPOSE / MATERIAL 3

ToggleButton:状态不是点击动画

理解 checked 状态、三套形状以及不同 ToggleButton 变体,不把它当成普通 Button 使用。

它和普通 Button 的根本区别

普通按钮表达一次动作;ToggleButton 表达一个可保持的二元状态。组件不会替你保存这个状态,checkedonCheckedChange 仍然由调用者连接:

var checked by remember { mutableStateOf(false) }

ToggleButton(
    checked = checked,
    onCheckedChange = { checked = it },
) {
    Text("收藏")
}

这段就是 AndroidX sample 的基本数据流。点击后能否保持选中,取决于你是否在回调中更新 checked

它有三个形状

ToggleButtonShapes 不是普通 Button 的两个 shape,而是:

属性使用时机
shape未选中、未按压
pressedShape手指或鼠标正在按压
checkedShape已选中

官方 sample 给出了方形变圆形的最小例子:

val shapes = ToggleButtonShapes(
    shape = ToggleButtonDefaults.squareShape,
    pressedShape = ToggleButtonDefaults.pressedShape,
    checkedShape = ToggleButtonDefaults.roundShape,
)

ToggleButton(
    checked = checked,
    onCheckedChange = { checked = it },
    shapes = shapes,
) {
    Text("Button")
}

当三者都是 CornerBasedShape 时,源码会做形状插值;出现其他 Shape 时则按状态切换,不保证同样的平滑 morph。

默认已经按高度选形状

ToggleButton 的默认参数不是固定 shapes(),而是:

shapes = ToggleButtonDefaults.shapesFor(ButtonDefaults.MinHeight)

如果你改成 medium、large 等高度,应同时传入对应的 shapesFor(height)contentPaddingFor(height),否则视觉尺寸变了,形状与内边距仍可能停留在 small 档。

变体怎么选

  • ToggleButton:主要与 tonal 颜色之间切换。
  • ElevatedToggleButton:需要阴影层级。
  • FilledTonalToggleButton:更柔和的填充强调。
  • OutlinedToggleButton:未选中时保留边框;源码默认在 checked 时返回 null 边框。
  • IconToggleButton:内容只有图标时优先使用,语义和尺寸更匹配。

单选和多选语义

ToggleButton 实现默认给 Surface 设置的是 Role.Checkbox。如果一组按钮只能选一个,AndroidX 的 connected group sample 会额外设置 Role.RadioButton。视觉上连在一起不等于无障碍语义已经正确。

复制即用

收藏开关的最小实现:

import androidx.compose.material3.ToggleButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue

@Composable
fun FavoriteToggle() {
    var checked by remember { mutableStateOf(false) }

    ToggleButton(
        checked = checked,
        onCheckedChange = { checked = it },
    ) {
        Text(if (checked) "已收藏" else "收藏")
    }
}

方形选中变圆形(三套形状):

import androidx.compose.material3.ToggleButton
import androidx.compose.material3.ToggleButtonDefaults
import androidx.compose.material3.ToggleButtonShapes
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue

@Composable
fun ShapeMorphToggle() {
    var checked by remember { mutableStateOf(false) }
    val shapes = ToggleButtonShapes(
        shape = ToggleButtonDefaults.squareShape,
        pressedShape = ToggleButtonDefaults.pressedShape,
        checkedShape = ToggleButtonDefaults.roundShape,
    )

    ToggleButton(
        checked = checked,
        onCheckedChange = { checked = it },
        shapes = shapes,
    ) {
        Text("Button")
    }
}

改大高度时记得同步 shapesFor(height)contentPaddingFor(height)