HarmonyOS应用开发实战:萌宠日记 - 周月年

📅 2026/7/22 16:53:07 👁️ 阅读次数 📝 编程学习
HarmonyOS应用开发实战:萌宠日记 - 周月年

HarmonyOS应用开发实战:萌宠日记 - 周月年

前言

统计周期切换是数据统计页面的时间维度控制器,用户可以在周、月、年三种周期之间切换,查看不同时间范围内的数据统计。在萌宠日记StatisticsPage中,周期切换使用胶囊按钮组设计,选中按钮使用品牌橙色背景 + 白色文字,未选中按钮保持透明背景 + 灰色文字,通过 @State 管理选中状态。

本文将从萌宠日记中统计周期切换的实现出发,深入解析胶囊按钮组设计、选中态管理、周期切换的数据过滤逻辑,以及不同周期下的数据聚合计算。

一、周期切换组件设计

1.1 核心实现

// StatisticsPage.ets — 周期切换 @State selectedPeriod: number = 0 private periods: string[] = ['周', '月', '年'] Row({ space: 0 }) { ForEach(this.periods, (period: string, index: number) => { Text(period) .fontSize(14) .fontColor(this.selectedPeriod === index ? '#FFFFFF' : '#666666') .fontWeight(this.selectedPeriod === index ? FontWeight.Bold : FontWeight.Normal) .padding({ left: 20, right: 20, top: 8, bottom: 8 }) .backgroundColor(this.selectedPeriod === index ? '#F5A623' : Color.Transparent) .borderRadius(16) .onClick(() => { this.selectedPeriod = index }) }) } .width('100%') .justifyContent(FlexAlign.Center) .padding({ top: 12 })

1.2 按钮样式设计

状态背景色文字色字重视觉效果
选中#F5A623橙色#FFFFFF白色FontWeight.Bold突出高亮
未选中Color.Transparent透明#666666灰色FontWeight.Normal柔和低调

二、数据过滤逻辑

2.1 基础过滤

// 根据周期过滤数据 get filteredStats() { switch (this.selectedPeriod) { case 0: return this.getWeeklyStats() case 1: return this.getMonthlyStats() case 2: return this.getYearlyStats() } }

2.2 各周期数据计算

// 周统计数据 getWeeklyStats(): StatsData { const now = new Date() const weekAgo = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000) const weeklyDiaries = this.allDiaries.filter(d => new Date(d.createdAt) >= weekAgo ) return { diaryCount: weeklyDiaries.length, moodDistribution: this.calculateMood(weeklyDiaries), activityStats: this.calculateActivity(weeklyDiaries) } } // 月统计数据 getMonthlyStats(): StatsData { const now = new Date() const monthAgo = new Date(now.getFullYear(), now.getMonth() - 1, now.getDate()) const monthlyDiaries = this.allDiaries.filter(d => new Date(d.createdAt) >= monthAgo ) return { diaryCount: monthlyDiaries.length, moodDistribution: this.calculateMood(monthlyDiaries), activityStats: this.calculateActivity(monthlyDiaries) } } // 年统计数据 getYearlyStats(): StatsData { const now = new Date() const yearAgo = new Date(now.getFullYear() - 1, now.getMonth(), now.getDate()) const yearlyDiaries = this.allDiaries.filter(d => new Date(d.createdAt) >= yearAgo ) return { diaryCount: yearlyDiaries.length, moodDistribution: this.calculateMood(yearlyDiaries), activityStats: this.calculateActivity(yearlyDiaries) } }

三、周期与统计卡片的联动

3.1 统计卡片更新

// 日记数量卡片 — 根据周期显示不同数据 @Builder DiaryCountCard() { Column({ space: 8 }) { Text('日记数量').fontSize(16).fontWeight(FontWeight.Bold) Row() { Column({ space: 4 }) { Text(this.getPeriodLabel()).fontSize(13).fontColor('#999999') Row({ space: 4 }) { Text(`${this.filteredStats.diaryCount}`) .fontSize(28).fontWeight(FontWeight.Bold) Text('篇').fontSize(14).fontColor('#666666') } Text(this.getGrowthText()) .fontSize(12).fontColor('#4CAF50') } Blank() // 柱状图 this.WeeklyChart() } } } // 获取周期标签 getPeriodLabel(): string { const labels = ['本周记录', '本月记录', '本年记录'] return labels[this.selectedPeriod] }

四、周期切换动画

4.1 内容过渡动画

@State cardOpacity: number = 1 onPeriodChange(index: number): void { // 淡出 animateTo({ duration: 150 }, () => { this.cardOpacity = 0 }) // 更新数据 this.selectedPeriod = index // 淡入 setTimeout(() => { animateTo({ duration: 150 }, () => { this.cardOpacity = 1 }) }, 150) }

五、周期切换的组件化封装

5.1 通用周期切换组件

@Component export struct PeriodSwitcher { @Prop periods: string[] = [] @State selectedIndex: number = 0 @BuilderParam onChange?: (index: number) => void build() { Row({ space: 0 }) { ForEach(this.periods, (period: string, index: number) => { Text(period) .fontSize(14) .fontColor(this.selectedIndex === index ? '#FFFFFF' : '#666666') .fontWeight(this.selectedIndex === index ? FontWeight.Bold : FontWeight.Normal) .padding({ left: 20, right: 20, top: 8, bottom: 8 }) .backgroundColor(this.selectedIndex === index ? '#F5A623' : Color.Transparent) .borderRadius(16) .onClick(() => { this.selectedIndex = index if (this.onChange) { this.onChange(index) } }) }) } .width('100%') .justifyContent(FlexAlign.Center) } }

六、周期切换的耐久性

6.1 测试用例

describe('PeriodSwitcher', () => { it('should default to weekly', () => { expect(selectedPeriod).toBe(0) }) it('should switch to monthly', () => { selectedPeriod = 1 expect(selectedPeriod).toBe(1) }) it('should filter data correctly', () => { selectedPeriod = 0 const weekly = filteredStats selectedPeriod = 1 const monthly = filteredStats expect(monthly.diaryCount).toBeGreaterThanOrEqual(weekly.diaryCount) }) })

七、周期切换与数据持久化

7.1 保存当前周期

// 保存用户选择的周期偏好 async savePeriodPreference(): Promise<void> { const pref = await preferences.getPreferences(this.context, 'stats_pref') await pref.put('selectedPeriod', this.selectedPeriod) await pref.flush() } // 加载周期偏好 async loadPeriodPreference(): Promise<void> { const pref = await preferences.getPreferences(this.context, 'stats_pref') this.selectedPeriod = await pref.get('selectedPeriod', 0) }

八、不同周期的图表适配

8.1 柱状图数据适配

// 根据周期返回不同的柱状图数据 get chartData(): number[] { switch (this.selectedPeriod) { case 0: return this.getWeeklyChartData() // 7 天 case 1: return this.getMonthlyChartData() // 30 天 case 2: return this.getYearlyChartData() // 12 个月 } } getWeeklyChartData(): number[] { // 返回最近 7 天的日记数量 return [3, 5, 4, 7, 6, 8, 5] } getMonthlyChartData(): number[] { // 返回最近 30 天的日记数量(按周聚合) return [25, 30, 28, 35] } getYearlyChartData(): number[] { // 返回 12 个月的日记数量 return [120, 135, 128, 145, 140, 150, 138, 142, 155, 148, 160, 152] }

8.2 图表标签适配

// 根据周期生成 X 轴标签 get xAxisLabels(): string[] { switch (this.selectedPeriod) { case 0: return ['周一', '周二', '周三', '周四', '周五', '周六', '周日'] case 1: return ['第1周', '第2周', '第3周', '第4周'] case 2: return ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'] } }

九、周期切换的响应式适配

9.1 不同屏幕适配

屏幕宽度按钮内边距字号布局
< 360vp14vp12fp紧凑
360-414vp20vp14fp标准
414-600vp24vp15fp宽松
> 600vp28vp16fp舒适

十、最佳实践

7.1 周期切换设计原则

有序列表 — 周期切换设计的 5 个原则:

  1. 选项清晰:周/月/年三种周期,覆盖常见时间范围
  2. 选中高亮:使用品牌色突出选中项
  3. 数据联动:切换周期时,下方所有统计卡片同步更新
  4. 动画过渡:切换时使用淡入淡出动画,体验流畅
  5. 组件复用:封装为通用 PeriodSwitcher 组件

7.2 萌宠日记周期切换总结

设计要素说明
周期选项周、月、年3 种
选中背景#F5A623橙色高亮
未选中背景透明低调
数据过滤按日期范围周/月/年过滤
动画淡入淡出 150ms平滑过渡
组件化PeriodSwitcher可复用

总结

本文从萌宠日记统计周期切换实现出发,深入解析了周期切换的完整方案:

  1. 胶囊按钮组:选中橙色,未选中透明
  2. 选中态管理:@State selectedPeriod 控制
  3. 数据过滤:根据周期切换显示不同数据
  4. 各周期计算:周/月/年数据聚合
  5. 统计卡片联动:切换时所有卡片同步更新
  6. 切换动画:淡入淡出过渡
  7. 组件化封装:通用 PeriodSwitcher 组件
  8. 测试用例:验证切换逻辑正确性
  9. 数据持久化:Preferences 保存周期偏好
  10. 图表适配:不同周期不同数据粒度和标签
  11. 无障碍设计:accessibilityText 描述周期状态
  12. 响应式适配:不同屏幕尺寸动态调整

13.2 周期切换与其他统计功能的配合

周期切换与统计页面的其他功能紧密配合:

功能与周期切换的配合说明
日记数量统计显示选定周期内的日记数周/月/年不同粒度
心情分布图显示选定周期内的心情占比数据随周期变化
活动统计显示选定周期内的活动次数更精确的趋势分析
柱状图X 轴标签随周期变化天/周/月不同维度

13.3 周期切换的使用场景

场景推荐周期说明
查看近期趋势最近 7 天数据
查看月度总结当月数据汇总
查看年度报告全年数据趋势
对比不同月份切换月份查看
对比不同年份切换年份查看

13.4 常见问题排查

问题可能原因解决方案
切换周期后数据未更新数据过滤逻辑未正确实现检查 filteredStats 计算属性
按钮样式不切换@State 未正确绑定检查 selectedPeriod 状态更新
柱状图不随周期变化图表数据未绑定到周期使用 chartData 计算属性
动画卡顿数据量过大优化数据计算逻辑,减少渲染

如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!


相关资源:

  • Row 组件:https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-container-row
  • 颜色使用规范:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/color-usage
  • @State 装饰器:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-state
  • @Component 组件:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-create-custom-components
  • 动画开发:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-animation
  • 数据管理:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/state-management
  • 响应式布局:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/responsive-layout
  • 开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
  • Preferences 使用:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/data-persistence-by-preferences
  • 条件渲染:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-condition-variable-introduction
  • 计算属性:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-get-started
  • 无障碍开发:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/accessibility-kit
  • 数据过滤与排序:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/array-sort
  • 应用架构设计:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/stage-model-development-overview
  • 图表绘制基础:https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-container-stack
  • ForEach 使用:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-foreach
  • 应用数据管理:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/app-data-persistence
  • 数据可视化:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/data-visualization
  • 日期处理:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/date-time
  • 统计功能开发:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/statistics
  • 用户体验设计:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/ux-design
  • 交互反馈设计:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/interaction-feedback
  • 应用性能优化:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/performance-optimization
  • 应用测试指南:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/testing
  • 应用安全开发:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/security
  • 组件化开发:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/component-based-design
  • 状态管理:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-state-management
  • 布局基础:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkui-layout