前言
Material You 出来那年,"取色器"成了设计圈最热的功能——壁纸一变,整个 App 跟着变。两年过去,Google 在 Android 16 同期推出了 Material 3 Expressive,把 Material You 的"温柔"推到了"表情包"级别:
更大的字号、更长的圆角、更大的可点击区域、更夸张的弹性动画。
很多工程师觉得这跟咱没关系,但你看一眼 Jetpack Compose 1.8 起新增的 ExpressiveTheme API 就知道——这次跟咱们写代码的人绑得很死。
一、为什么我们需要新设计语言
Material You 解决了"千人千面"的颜色问题,但没有解决两个真正的痛点:
可发现性差:FAB、菜单、按钮大量使用时,用户根本分不清主次。
节奏感:Material You 的动画还是"该有就有",没有"故意停顿"让用户感觉到层级。
Expressive 的解决思路:用 物理弹性(spring-based motion)和 留白压迫(spacing scale v2)强迫设计师重新审视信息密度。
二、字号与间距:写一次就够
新规范里最大的变化是 Type Scale v3:
关键:headline 这一档全线加大 4sp,是大多数 App 页面立刻改的项。
间距也改了,引入 4dp grid 的 1.5× 同步规则:
val spacing = ExpressiveSpacing(
compactPadding = 8.dp,
defaultPadding = 16.dp,
dramaticPadding = 32.dp
)
dramaticPadding 用在引导页、空状态、错误页——故意让元素之间拉开距离,让用户有"看清再做决定"的时间。
三、弹性动画:ExpressiveTheme 的核心
Expressive 把 Material motion spec 整合进 Compose API:
@Composable
fun ExpressiveButton(text: String, onClick: () -> Unit) {
val interaction = remember { MutableInteractionSource() }
val pressed by interaction.collectIsPressedAsState()
val scale by animateFloatAsState(
if (pressed) 0.96f else 1f,
animationSpec = spring(
dampingRatio = Spring.DampingRatioMediumBouncy,
stiffness = Spring.StiffnessMedium
), label = "scale"
)
Button(
modifier = Modifier.scale(scale),
onClick = onClick,
interactionSource = interaction
) {
Text(text)
}
}
spring API 取代了大部分 tween 用例。视觉上的差别:以前按钮按下是"立即缩小",现在像皮球一样"按下去弹一下"。
四、可发现性:把 FAB 换成 Expressive Cluster
Material You 时代 FAB 是"一坨悬浮按钮",Expressive 推 Expressive Cluster(中文名"动作簇")——把多个相关动作放进一个可展开区域:
@Composable
fun PrimaryCluster(onSend: () -> Unit, onAttach: () -> Unit) {
var expanded by remember { mutableStateOf(false) }
ExpressiveCluster(
mainAction = ClusterAction("发送", Icons.Default.Send, onSend),
secondaryActions = listOf(
ClusterAction("附件", Icons.Default.AttachFile, onAttach),
ClusterAction("草稿", Icons.Save, {})
),
expanded = expanded,
onExpandChange = { expanded = it }
)
}
用户长按主按钮,主按钮轻微退后,副按钮绕主按钮 60° 扇形展开。比起"悬浮按钮 + 弹菜单",可见动作从 1 个变成 3 个,且无需进入二级页面。
五、Expressiveness 不只是 UI:触感反馈与音效
新规范要求:
每个按钮按下都得有 HapticFeedback:长按中等强度、短按轻强度、错误反馈重冲击。
关键操作有 Sonic Branding:从购物车"加入"到付款"成功"使用一组专属音效。
Compose 里直接调:
val haptic = LocalHapticFeedback.current
val view = LocalView.current
Button(onClick = {
haptic.performHapticFeedback(HapticFeedbackType.LongPress)
view.playSoundEffect(SoundEffectConstants.CLICK)
}) { Text("下一步") }
但不要每个按钮都加——Expressive 规范里写得很清楚:50% 的页面元素"沉默"才能让另外 50% 的元素"被听见"。
六、在 Compose 里接入 Material 3 Expressive
implementation("androidx.compose.material3:material3:1.8.0")
implementation("androidx.compose.material3:material3-window-size-class:1.8.0")
// 完整主题
@Composable
fun AppTheme(content: @Composable () -> Unit) {
MaterialTheme(
colorScheme = expressiveColorScheme(dynamicColor = true),
typography = expressiveTypography(),
shapes = expressiveShapes(),
motion = expressiveMotionScheme()
) {
content()
}
}
expressiveMotionScheme() 是新增的——把 spring 的 dampingRatio / stiffness 抽出来作为 token,跨页面复用,避免每个项目自己定义一套。
七、落地建议
先改字号:headline 普遍加 4sp,是风险最低、收益最快的动作。
再改间距:空状态、引导页用 dramaticPadding,让用户感受到"喘气"。
弹性动画:仅在 FAB、主按钮、Switch 上启用,其它地方保守使用。
音频与触感:先在旗舰机和 Pixel 上做 A/B 测试,老机型 fallback 到点击震动即可。
小结
Expressive 不是"换个皮肤"——它要求工程和设计重新谈一遍节奏。下一篇文章我们换赛道:聊聊可折叠设备的工程化适配,跟 Material 3 Expressive 是一对固定搭配。
参考阅读
Material Design 3 Expressive(m3.material.io)
Compose Material 3 1.8 升级指引
「物理弹性」与 Compose spring API