HarmonyOS应用开发实战:萌宠日记 - 统计卡片统一风格
📅 2026/7/22 12:11:26
👁️ 阅读次数
📝 编程学习
HarmonyOS应用开发实战:萌宠日记 - 统计卡片统一风格
前言
统计卡片统一风格是数据统计页面保持视觉一致性的关键设计。在萌宠日记的StatisticsPage中,所有统计卡片(日记数量、心情分布、活动统计)都采用相同的白色背景、16vp 圆角、轻微阴影、16vp 内边距,确保用户在不同统计项之间切换时获得一致的视觉体验。这种组件化设计不仅提升了 UI 一致性,还大幅减少了代码重复。
本文将从萌宠日记中统计卡片统一风格的实现出发,深入解析卡片样式规范、组件化封装、@Builder 复用,以及风格一致性的设计原则。
一、统计卡片现状
1.1 三张统计卡片
萌宠日记的统计页面包含三张统计卡片:
| 卡片名称 | 展示内容 | 核心组件 | 数据源 |
|---|---|---|---|
| 日记数量 | 本月记录数 + 柱状图 | Text + Column | @State diaryCount |
| 心情分布 | 心情占比 + 图例 | Circle + Text | @State moodStats |
| 活动统计 | 活动次数 + 图标 | Column + Text | @State activityStats |
1.2 当前代码中的重复样式
// 日记数量卡片 Column({ space: 8 }) { // 内容... } .width('100%') .backgroundColor('#FFFFFF') .borderRadius(16) .padding(16) .shadow({ radius: 4, color: '#10000000', offsetY: 2 }) // 心情分布卡片(重复的样式代码) Column({ space: 12 }) { // 内容... } .width('100%') .backgroundColor('#FFFFFF') .borderRadius(16) .padding(16) .shadow({ radius: 4, color: '#10000000', offsetY: 2 }) // 活动统计卡片(再次重复) Column({ space: 12 }) { // 内容... } .width('100%') .backgroundColor('#FFFFFF') .borderRadius(16) .padding(16) .shadow({ radius: 4, color: '#10000000', offsetY: 2 })提示:三张卡片的样式代码完全重复,这是典型的代码冗余问题。通过组件化封装可以消除重复,提升可维护性。
二、统计卡片统一样式规范
2.1 样式规范
// 统计卡片的统一风格定义 const CARD_STYLE = { backgroundColor: '#FFFFFF', // 白色背景 borderRadius: 16, // 16vp 圆角 padding: 16, // 16vp 内边距 shadow: { radius: 4, color: '#10000000', offsetY: 2 }, // 轻微阴影 titleSize: 16, // 标题字号 titleWeight: FontWeight.Bold, // 标题字重 titleColor: '#333333' // 标题颜色 }2.2 样式属性详解
| 属性 | 值 | 作用 |
|---|---|---|
backgroundColor | #FFFFFF白色 | 与页面背景#FFF8F0形成对比 |
borderRadius | 16vp | 柔和圆角,与全局卡片风格一致 |
padding | 16vp | 内容与卡片边缘的呼吸空间 |
shadow.radius | 4 | 轻微模糊,增加层次感 |
shadow.color | #10000000 | 10% 透明度的黑色,阴影柔和 |
shadow.offsetY | 2 | 向下偏移,模拟自然光照 |
三、@Builder 封装复用
3.1 卡片容器 Builder
// 统计卡片容器 — 统一样式封装 @Builder StatCard(title: string, content: () => void) { Column({ space: 12 }) { // 统一标题 Text(title) .fontSize(16) .fontWeight(FontWeight.Bold) .fontColor('#333333') .width('100%') // 自定义内容区 content() } .width('100%') .backgroundColor('#FFFFFF') .borderRadius(16) .padding(16) .shadow({ radius: 4, color: '#10000000', offsetY: 2 }) }3.2 使用卡片容器
// 使用统一卡片容器 Scroll() { Column({ space: 16 }) { // 日记数量卡片 this.StatCard('日记数量', () => { this.DiaryCountContent() }) // 心情分布卡片 this.StatCard('心情分布', () => { this.MoodDistributionContent() }) // 活动统计卡片 this.StatCard('活动统计', () => { this.ActivityStatsContent() }) } .padding({ left: 16, right: 16, top: 16, bottom: 24 }) }四、@Component 组件化封装
4.1 通用卡片组件
@Component export struct StatCard { @Prop title: string = '' @Prop paddingValue: number = 16 @BuilderParam content: () => void = () => {} build() { Column({ space: 12 }) { // 标题行 Text(this.title) .fontSize(16) .fontWeight(FontWeight.Bold) .fontColor('#333333') .width('100%') // 内容区 if (this.content) { this.content() } } .width('100%') .backgroundColor('#FFFFFF') .borderRadius(16) .padding(this.paddingValue) .shadow({ radius: 4, color: '#10000000', offsetY: 2 }) } }4.2 组件使用示例
// 日记数量卡片 StatCard({ title: '日记数量' }) { Row() { Column({ space: 4 }) { Text('本月记录').fontSize(13).fontColor('#999999') Row({ space: 4 }) { Text('18').fontSize(28).fontWeight(FontWeight.Bold).fontColor('#333333') Text('篇').fontSize(14).fontColor('#666666') } Text('比上月 +6篇').fontSize(12).fontColor('#4CAF50') } Blank() // 柱状图 Row({ space: 4 }) { ForEach([3, 5, 4, 7, 6, 8, 5], (h: number) => { Column().width(16).height(h * 8).backgroundColor('#F5A623').borderRadius(4) }) } .alignItems(VerticalAlign.Bottom).height(64) } .width('100%') }五、卡片标题统一规范
5.1 标题样式规范
// 所有统计卡片标题使用相同样式 Text('日记数量') .fontSize(16) // 16fp 标准标题字号 .fontWeight(FontWeight.Bold) // 加粗 .fontColor('#333333') // 深灰色 .width('100%') // 撑满宽度5.2 标题与内容间距
Column({ space: 12 }) { // 标题与内容间距 12vp Text('日记数量') // 标题 .fontSize(16).fontWeight(FontWeight.Bold) // 内容区... }六、卡片内容区的差异化设计
6.1 各卡片内容差异
| 卡片 | 内容布局 | 差异化参数 |
|---|---|---|
| 日记数量 | Row 左右分布 | 左边数字 + 右边柱状图 |
| 心情分布 | Row 左右分布 | 左边饼图占位 + 右边图例 |
| 活动统计 | Row 三列等宽 | layoutWeight(1) 等分 |
6.2 差异化通过 @BuilderParam 实现
// 相同容器 + 不同内容 = 卡片复用 StatCard({ title: '日记数量' }) { this.DiaryContent() } StatCard({ title: '心情分布' }) { this.MoodContent() } StatCard({ title: '活动统计' }) { this.ActivityContent() }七、卡片间距控制
7.1 卡片之间的间距
// 卡片之间的间距由外层 Column 的 space 控制 Column({ space: 16 }) { // 卡片间距 16vp StatCard({ title: '日记数量' }) { ... } StatCard({ title: '心情分布' }) { ... } StatCard({ title: '活动统计' }) { ... } }八、卡片组件封装收益
8.1 代码量对比
| 实现方式 | 每张卡片代码量 | 总代码量 | 维护成本 |
|---|---|---|---|
| 重复编写 | 8 行样式 + 内容 | 24 行样式 + 3×内容 | 高 |
| @Builder 封装 | 0 行样式 | 8 行样式 (Builder) | 低 |
| @Component 封装 | 0 行样式 | 20 行组件 | 最低 |
8.2 一致性收益
- 视觉统一:所有卡片使用完全相同的样式参数
- 修改方便:修改一处 Builder 或 Component,所有卡片自动更新
- 扩展简单:新增统计卡片只需调用 StatCard 组件
九、卡片阴影策略
9.1 阴影与视觉层次
// 统计卡片使用阴影突出 .shadow({ radius: 4, color: '#10000000', offsetY: 2 }) // 背景卡片不使用阴影(对比) // 内嵌卡片不使用阴影主要特点:
- 统计卡片使用阴影,从页面背景中突出
- 列表项不使用阴影,保持视觉简洁
- 阴影参数统一,确保一致性
9.2 萌宠日记中的阴影使用场景
| 使用场景 | 阴影配置 | 设计意图 |
|---|---|---|
| 统计卡片 | radius: 4, offsetY: 2 | 从页面背景中突出 |
| 宠物信息卡片 | radius: 4, offsetY: 2 | 与统计卡片一致 |
| 活动横幅卡片 | radius: 4, offsetY: 2 | 突出推广内容 |
| 列表项卡片 | 无阴影 | 保持视觉简洁 |
| 内嵌卡片 | 无阴影 | 避免阴影叠加 |
十、卡片内容的可访问性
10.1 无障碍适配
// 为统计卡片添加无障碍描述 StatCard({ title: '日记数量' }) { Column() .accessibilityText('日记数量统计卡片,本月共18篇日记,比上月增加6篇') }10.2 颜色对比度检查
统计卡片中的颜色对比度需满足 WCAG 标准:
| 颜色组合 | 用途 | 对比度 | 是否达标 |
|---|---|---|---|
#FFFFFF背景 +#333333文字 | 标题 | 10.6:1 | ✅ |
#FFFFFF背景 +#999999文字 | 辅助信息 | 4.5:1 | ✅ |
#F5A623柱状图 +#FFFFFF背景 | 图表 | 3.2:1 | ⚠️ 边缘 |
十一、卡片组件的测试
11.1 单元测试
// 统计卡片组件的测试用例 describe('StatCard', () => { it('should render title correctly', () => { // 验证标题显示 }) it('should render content correctly', () => { // 验证内容渲染 }) it('should apply correct styles', () => { // 验证样式应用 }) })十二、从设计稿到代码的映射
12.1 设计稿规范
| 设计稿规范 | 代码实现 | 对应属性 |
|---|---|---|
| 卡片背景 #FFFFFF | backgroundColor('#FFFFFF') | 背景色 |
| 圆角 16px | borderRadius(16) | 圆角 |
| 阴影 Y:2, Blur:4 | shadow({ radius: 4, offsetY: 2 }) | 阴影 |
| 内边距 16px | padding(16) | 内边距 |
| 标题 16px Bold | fontSize(16).fontWeight(Bold) | 标题 |
12.2 设计与开发协同
- 设计稿使用与代码一致的命名规范
- 设计标注的数值单位与代码的 vp 保持一致
- 设计稿提供明暗两种模式的卡片样式
十、最佳实践
10.1 卡片设计原则
有序列表 — 卡片统一风格设计的 5 个原则:
- 统一容器样式:所有同类型卡片使用相同的背景、圆角、阴影、内边距
- 组件化封装:使用 @Builder 或 @Component 封装卡片容器
- 内容差异化:通过 @BuilderParam 插槽实现内容差异化
- 统一标题规范:所有卡片标题使用相同的字号、字重、颜色
- 间距统一:卡片内外间距保持一致
10.2 萌宠日记统计卡片规范总结
| 设计要素 | 规范值 | 说明 |
|---|---|---|
| 背景色 | #FFFFFF | 白色 |
| 圆角 | 16vp | 统一圆角 |
| 内边距 | 16vp | 统一内边距 |
| 阴影 | radius: 4, offsetY: 2 | 轻微阴影 |
| 标题字号 | 16fp | 加粗 |
| 标题颜色 | #333333 | 深灰色 |
| 标题-内容间距 | 12vp | Column space |
| 卡片间距 | 16vp | 外层 Column space |
总结
本文从萌宠日记的统计卡片统一风格实现出发,深入解析了卡片风格统一的完整方案:
- 样式规范:白色背景 + 16vp 圆角 + 阴影 + 16vp 内边距
- @Builder 封装:通过 Builder 函数复用卡片容器
- @Component 封装:通用 StatCard 组件 + @BuilderParam 内容插槽
- 标题统一:16fp 加粗深灰色标题
- 内容差异化:相同容器不同内容,通过参数化实现
- 间距控制:卡片内外间距统一
- 封装收益:减少代码量、提升一致性、降低维护成本
通过组件化封装,萌宠日记的统计页面实现了一处修改、处处生效的高效开发模式,三张卡片在保持统一视觉风格的同时,又能灵活展示不同的统计内容。
如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!
相关资源:
- @Builder 装饰器:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-builder
- @BuilderParam 装饰器:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-builderparam
- @Component 自定义组件:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-create-custom-components
- 阴影属性:https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arkts-shadow-effect
- 卡片设计指南:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/design-cards
- 组件化设计:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/component-based-design
- 布局基础:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkui-layout
- 开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
编程学习
技术分享
实战经验