MaterialRadiusScale - Material 圆角系统¶
MaterialRadiusScale 实现 Material Design 3 的圆角规范。Material 用 7 个标准圆角值覆盖几乎所有 UI 场景,从完全方形到超大圆角都有定义。我们把它们存在嵌入式 Token 注册表中,每个圆角规格实例独立管理。
圆角规格¶
Material Design 3 定义了 7 个标准圆角:
| Token | 值 (dp) | 用途 |
|---|---|---|
| cornerNone | 0dp | 无圆角(矩形) |
| cornerExtraSmall | 4dp | Chip、小卡片 |
| cornerSmall | 8dp | 文本框、复选框 |
| cornerMedium | 12dp | 卡片 |
| cornerLarge | 16dp | 警告对话框 |
| cornerExtraLarge | 28dp | FAB、模态框 |
| cornerExtraExtraLarge | 32dp | 抽屉 |
这个设计很有意思——只要记住 7 个数字,就能保证整个 UI 的形状一致性。不需要每个组件单独设计圆角。
基本用法¶
通过工厂函数创建默认圆角系统:
#include "material_factory.hpp"
// 创建默认圆角规格
auto radiusScale = cf::ui::core::material::defaultRadiusScale();
// 查询圆角
float smallRadius = radiusScale.queryRadiusScale("md.shape.cornerSmall"); // 8.0f
float mediumRadius = radiusScale.queryRadiusScale("md.shape.cornerMedium"); // 12.0f
float largeRadius = radiusScale.queryRadiusScale("md.shape.cornerLarge"); // 16.0f
圆角名称采用 md.shape. 前缀,后跟 Material 官方定义的规格名称。
在 Qt 中使用¶
获取的圆角值可以直接用于 Qt 组件:
void MyCard::setupAppearance() {
auto* theme = getTheme();
auto* radius = static_cast<MaterialRadiusScale*>(theme->radius_scale());
float cardRadius = radius->queryRadiusScale("md.shape.cornerMedium");
// 应用到样式表
QString style = QString("QWidget { border-radius: %1px; }").arg(cardRadius);
setStyleSheet(style);
// 或者用于 QPainter
m_borderRadius = cardRadius;
}
void MyCard::paintEvent(QPaintEvent*) {
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
QRectF rect = this->rect().adjusted(1, 1, -1, -1);
painter.drawRoundedRect(rect, m_borderRadius, m_borderRadius);
}
组件对应关系¶
不同组件对应不同圆角规格,Material 有推荐值:
// 按钮用小圆角
float buttonRadius = radiusScale.queryRadiusScale("md.shape.cornerSmall"); // 8dp
// 卡片用中圆角
float cardRadius = radiusScale.queryRadiusScale("md.shape.cornerMedium"); // 12dp
// 对话框用大圆角
float dialogRadius = radiusScale.queryRadiusScale("md.shape.cornerLarge"); // 16dp
// FAB 用超大圆角
float fabRadius = radiusScale.queryRadiusScale("md.shape.cornerExtraLarge"); // 28dp
// 侧边栏用超大超大圆角
float drawerRadius = radiusScale.queryRadiusScale("md.shape.cornerExtraExtraLarge"); // 32dp
自定义圆角¶
如果需要自定义圆角,可以直接修改 registry:
MaterialRadiusScale radiusScale = material::defaultRadiusScale();
// 添加自定义圆角
radiusScale.registry().set("md.shape.cornerCustom", 20.0f);
// 覆盖现有圆角
radiusScale.registry().set("md.shape.cornerMedium", 16.0f); // 改成和 Large 一样
这个设计让系统既支持标准 Material 规范,也允许根据品牌需求调整。
缓存机制¶
圆角查询有缓存层,避免重复解析字符串:
// 第一次查询会查找并缓存
float r1 = radiusScale.queryRadiusScale("md.shape.cornerLarge");
// 后续查询从缓存返回
float r2 = radiusScale.queryRadiusScale("md.shape.cornerLarge");
缓存在 MaterialRadiusScale 对象生命周期内有效。
单位说明¶
圆角值以 dp(Density-independent Pixels)为单位。在我们的实现中直接返回浮点数,调用方负责根据 DPI 缩放:
// 获取 dp 值
float radiusDp = radiusScale.queryRadiusScale("md.shape.cornerMedium");
// 转换为物理像素
qreal scaleFactor = devicePixelRatioF();
float radiusPx = radiusDp * scaleFactor;
大多数情况下直接用 dp 值传入 Qt 函数就可以,Qt 会自动处理 DPI 缩放。
高 DPI 注意事项¶
在高 DPI 屏幕上,圆角值可能需要特殊处理:
// 确保 QPainter 开启抗锯齿
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
// 使用 float 精度的绘制函数
painter.drawRoundedRect(rect, radius, radius);
小圆角值(如 4dp 的 ExtraSmall)在高 DPI 下可能会被平滑成几乎直线,这是正常行为。