open harmony 项目实战:学习打卡功能如何设计更有激励感

学习类 App 想让用户持续打开,不能只靠内容堆叠,还要给用户一点“我正在进步”的反馈。

“语文视界”的学习中心里做了每日打卡、本周进度、连续学习天数和今日任务展示。这个功能不复杂,但很适合作为教育类 App 的留存入口。☀️

一、学习中心展示什么

学习中心第一屏包含:

  • 连续学习天数
  • 今日学习状态
  • 本周打卡进度
  • 今日背诵任务
  • 诗词背诵、默写、配对、朝代排序模块

这些内容组合起来,会让用户一打开页面就知道:今天有没有学、已经坚持多久、接下来可以做什么。

二、核心状态

@State currentStreak: number = 0;
@State todayCompleted: boolean = false;
@State private checkInDatesStr: string = '';

currentStreak 是连续天数,todayCompleted 表示今天是否完成,checkInDatesStr 记录已打卡日期。

项目里用 | 分隔日期:

2026-6-28|2026-6-29|2026-6-30

这种做法简单,适合轻量本地状态。

三、读取本地数据

进入页面时会加载持久化数据:

private loadPersistedData(): void {
  this.migrateLegacyCheckInIfNeeded();
  const raw = AppStorage.get<string>('learn_checkin_dates');
  this.checkInDatesStr = raw !== undefined && raw.length > 0 ? raw : '';
  const today = this.getTodayDate();
  const lastCheckIn = AppStorage.get<string>('learn_last_checkin_date') || '';
  this.todayCompleted = this.isDateChecked(today);
}

这里还调用了 migrateLegacyCheckInIfNeeded,说明项目考虑到了旧版本数据兼容。

四、旧数据迁移

如果旧版本只保存了 learn_checkin_todaylearn_streak_date,新版本会迁移到日期列表:

private migrateLegacyCheckInIfNeeded(): void {
  const hasNew = AppStorage.get<string>('learn_checkin_dates');
  if (hasNew !== undefined && hasNew.length > 0) {
    return;
  }
  const oldDate = AppStorage.get<string>('learn_streak_date');
  const oldDone = AppStorage.get<boolean>('learn_checkin_today');
  if (oldDate !== undefined && oldDate.length > 0 && oldDone === true) {
    AppStorage.setOrCreate('learn_checkin_dates', oldDate);
    AppStorage.setOrCreate('learn_last_checkin_date', oldDate);
  }
}

这个细节很适合写到文章里,因为它比普通 Demo 更像真实项目。

五、防止重复打卡

用户点击打卡时,先判断今天是否已经打过:

if (this.isDateChecked(today)) {
  promptAction.showToast({ message: '今日已打卡 ♡', duration: 1500 });
  return;
}

已经打卡就提示,不重复增加天数。

六、连续天数计算

项目会比较上次打卡日期和昨天:

const yesterday = this.getYesterdayDate();
let streak = AppStorage.get<number>('learn_streak');
if (streak === undefined) {
  streak = 0;
}
if (last === yesterday) {
  streak = streak + 1;
} else if (last !== today) {
  streak = 1;
}

如果昨天也打卡,就连续天数 +1;如果中断了,就从 1 开始。

七、保存打卡状态

private savePersistedData(): void {
  AppStorage.setOrCreate('learn_streak', this.currentStreak);
  AppStorage.setOrCreate('learn_streak_date', this.getTodayDate());
  AppStorage.setOrCreate('learn_checkin_today', this.todayCompleted);
  AppStorage.setOrCreate('learn_checkin_dates', this.checkInDatesStr);
  AppStorage.setOrCreate('learn_last_checkin_date', this.getTodayDate());
}

保存字段比较多,是为了兼容当前页面展示和旧版本逻辑。

八、本周进度展示

页面通过 7 个格子展示周一到周日:

ForEach([0, 1, 2, 3, 4, 5, 6], (index: number) => {
  Column() {
    Text(this.getWeekDayName(index));
    Column() {
      if (this.isWeekSlotChecked(index)) {
        Text('✓');
      } else {
        Text('○');
      }
    }
  }
});

这类视觉反馈能让用户快速感知自己的学习节奏。

九、激励感来自哪里

这个功能的激励感主要来自四点:

  • 数字反馈:连续多少天。
  • 状态反馈:今日已完成/进行中。
  • 视觉反馈:本周格子点亮。
  • 即时反馈:Toast 提示打卡成功。

学习类产品不一定要做复杂游戏化,简单而稳定的反馈就很有效。

总结

每日打卡功能本身不难,真正值得注意的是状态设计:日期怎么存、连续天数怎么算、重复打卡怎么拦、旧数据怎么迁移、本周进度怎么展示。

这个项目用 AppStorage 就实现了一套完整的本地打卡方案,轻量但够用,很适合 OpenHarmony 教育类 App 借鉴。🌟

img

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐