Material Token 字面量¶
cfmaterial_token_literals.h 定义了 Material Design 3 色彩系统的全部 Token 字面量。Material You 的核心思想是让 UI 从用户的壁纸中提取颜色来生成主题,这些 Token 就是这套动态色彩系统的基础锚点。
我们没有直接用字符串字面量,而是把这些常量抽出来统一管理,原因有两个:一是避免拼写错误("md.primary" 和 "md.pirmary" 编译器不会报错,但运行时就找不到了),二是方便 IDE 自动补全和重构。
色彩角色体系¶
Material Design 3 的色彩不是简单的调色板,而是基于"角色"(Role)的概念。每个颜色都有它特定的使用场景:
#include "ui/core/token/material_scheme/cfmaterial_token_literals.h"
using namespace cf::ui::core::token::literals;
// Primary 色系 - 主品牌色,用于最重要的组件
const char* primary = PRIMARY; // "md.primary"
const char* onPrimary = ON_PRIMARY; // "md.onPrimary"
const char* primaryContainer = PRIMARY_CONTAINER; // "md.primaryContainer"
const char* onPrimaryContainer = ON_PRIMARY_CONTAINER; // "md.onPrimaryContainer"
// Secondary 色系 - 次要强调色
const char* secondary = SECONDARY;
const char* onSecondary = ON_SECONDARY;
// Tertiary 色系 - 第三强调色,用于对比和平衡
const char* tertiary = TERTIARY;
const char* onTertiary = ON_TERTIARY;
// Error 色系 - 错误状态和危险操作
const char* error = ERROR;
const char* onError = ON_ERROR;
注意那个 "On" 前缀——这不是"打开"的意思,而是"绘制在...之上"(On)。ON_PRIMARY 就是绘制在 Primary 颜色上的文字和图标的颜色,Material 的配色算法会自动计算对比度,保证可读性。
Container 色系¶
Container 色是 Material You 新增的概念。它们是基色的"调色版本"——更浅或更深,用来承载对应颜色的内容:
// 场景:一个卡片容器
// card_bg 使用 primaryContainer(浅调的主色)
// card_content 使用 primary(深调的主色)
// 场景:一个按钮
// button_bg 使用 primary
// button_text 使用 onPrimary(高对比度)
// 场景:一个强调区域
// area_bg 使用 tertiaryContainer
// area_icon 使用 onTertiaryContainer
这样设计的好处是,组件的颜色关系由语义决定,而不是由具体的颜色值决定。动态换肤时,整个应用的颜色关系依然保持一致。
Surface 色系¶
Surface 色系用于各种"表面"——背景、卡片、菜单等:
// 基础背景
const char* background = BACKGROUND; // 应用背景
const char* onBackground = ON_BACKGROUND; // 背景上的文字
// 表面颜色
const char* surface = SURFACE; // 卡片、表单等表面
const char* onSurface = ON_SURFACE; // 表面上的文字
// 表面变体
const char* surfaceVariant = SURFACE_VARIANT; // 微差分的表面色
const char* onSurfaceVariant = ON_SURFACE_VARIANT; // 表面变体上的文字
// 边框颜色
const char* outline = OUTLINE; // 边框和分割线
const char* outlineVariant = OUTLINE_VARIANT; // 微差分的边框色
surfaceVariant 和 outlineVariant 这两个名字确实有点拗口。它们的作用是在深色模式下提供"不那么突兀"的边框和背景,避免视觉噪音过多。
特殊场景色系¶
还有几个用于特殊场景的颜色:
// 阴影和遮罩
const char* shadow = SHADOW; // 投影颜色
const char* scrim = SCRIM; // 模态背景遮罩
// 反色场景(用于对话框、抽屉等浮层)
const char* inverseSurface = INVERSE_SURFACE; // 反转的表面色
const char* inverseOnSurface = INVERSE_ON_SURFACE; // 反转表面上的文字
const char* inversePrimary = INVERSE_PRIMARY; // 反转的主色
scrim 是当弹出模态对话框时,后面那层半透明的黑色遮罩。深色模式下它可能是半透明的白色,取决于主题的动态生成逻辑。
在主题系统中使用¶
这些字面量最终会被主题系统解析成实际的颜色值:
// 伪代码:主题系统如何使用这些 Token
class MaterialTheme {
Color resolve(const char* token) const {
// 实际实现会从动态生成的颜色表中查找
return colorTable.at(token);
}
};
// 使用示例
MaterialTheme theme;
auto buttonBg = theme.resolve(PRIMARY);
auto buttonText = theme.resolve(ON_PRIMARY);
auto cardBg = theme.resolve(SURFACE);
auto cardBorder = theme.resolve(OUTLINE);
批量遍历¶
如果你需要遍历所有 Token(比如在主题编辑器中),提供了两个辅助:
// 获取所有 Token 的数组
const char* const* allTokens = ALL_TOKENS;
size_t count = TOKEN_COUNT; // 26 个
// 遍历所有 Token
for (size_t i = 0; i < TOKEN_COUNT; ++i) {
printf("Token %zu: %s\n", i, ALL_TOKENS[i]);
}
Token 命名规则¶
所有 Token 遵循 md. 前缀的命名规则。这个 md 是 Material Design 的缩写,用来和其他设计系统(比如未来可能引入的自定义设计 Token)做区分。如果你要扩展自己的 Token,建议用不同的前缀避免冲突。