引言

在移动应用开发中,日历与日程管理是一个高频需求场景。无论是企业协作工具中的会议安排、个人效率应用中的任务规划,还是社交平台中的活动提醒,都离不开日历组件的支撑。HarmonyOS NEXT 为我们提供了一个开箱即用的日历选择器组件——CalendarPicker,它封装了完整的日历交互逻辑,开发者无需从零构建日期网格、处理月份切换、计算农历节气,只需几行代码就能获得一个功能完备的日历视图。

本文将深入解析 CalendarPicker 组件的用法,并通过一个完整的"日程管理"Demo,展示如何将 CalendarPicker 与实际业务数据结合,打造一个实用的日程查看与管理界面。读完本文,你将掌握:

  1. CalendarPicker 的基本属性与事件回调
  2. 如何绑定选中日期并响应日期切换
  3. 如何将日历与日程数据模型进行联动
  4. 日程列表的分类展示与色彩编码
  5. 近期日程聚合展示的实现方式
  6. 美化界面的布局技巧与视觉设计

CalendarPicker 组件概述

CalendarPicker 是 ArkUI 提供的一款日历选择器组件,它以月视图的形式展示完整的日历网格,支持用户通过左右滑动手势或顶部的月份切换按钮来浏览不同的月份。与 DatePicker(日期滚轮选择器)不同,CalendarPicker 更适合需要展示整月视图、让用户直观看到日期分布的场景。

基本用法

CalendarPicker 的最简用法只需要两行代码:

CalendarPicker({ selected: this.selectedDate })
  .onChange((value: Date) => {
    this.selectedDate = value;
  })

selected 参数接收一个 Date 对象,用于指定当前被选中的日期。当用户点击日历中的某个日期时,onChange 回调会被触发,回调参数 value 就是用户新选择的日期。开发者需要在回调中手动更新状态变量,以实现选中日期的同步。

CalendarPicker 的核心特性

从交互体验来看,CalendarPicker 提供了以下内置功能:

  • 月份切换:顶部左右箭头按钮用于前后翻月,中间显示当前年/月文字
  • 日期网格:标准的7列(日一二三四五六)× 5~6行的月视图布局
  • 今日标记:当天日期会以特殊样式高亮显示
  • 选中状态:用户点击的日期会有选中态背景色
  • 滑动切换:支持左右滑动手势切换月份
  • 农历显示:默认显示农历信息(可通过配置关闭)

CalendarPicker 的高度通常在 300-400vp 之间,能够很好地嵌入到各种页面布局中。在我们的 Demo 中,我们将它放置在页面顶部,下方使用 Scroll 区域展示日程信息。

Demo:日程管理应用

接下来,我们通过一个完整的日程管理 Demo 来深入理解 CalendarPicker 的实际应用。这个 Demo 模拟了一个典型的团队协作场景,包含会议、任务、提醒和生日四种日程类型,分布在2026年6月的不同日期中。

页面整体结构

页面采用垂直布局,从上到下分为三个区域:

  1. 顶部信息栏:深色背景,展示年份、月份、当月日程总数,以及选中日期和星期
  2. 日历选择器:白色背景的 CalendarPicker,占据约340vp高度
  3. 日程详情区:可滚动的浅灰背景区域,包含两个卡片
    • 选中日期的日程列表(或空状态提示)
    • 近期日程汇总列表

这种三段式布局信息层级清晰:先看整体(月份概览)→ 再选具体(日历选日)→ 最后深入(日程详情),符合用户从宏观到微观的认知路径。

数据模型设计

在编写 UI 之前,我们先定义日程的数据模型。一个日程事件包含以下属性:

class CalendarEvent {
  id: number;
  title: string;
  type: string;        // 'meeting' | 'task' | 'reminder' | 'birthday'
  time: string;         // '09:00' 或 '全天'
  dateStr: string;      // '2026-06-15'
  color: string;

  constructor(id: number, title: string, type: string, time: string,
    dateStr: string, color: string) {
    this.id = id;
    this.title = title;
    this.type = type;
    this.time = time;
    this.dateStr = dateStr;
    this.color = color;
  }
}

每个日程事件的关键字段说明:

  • id:唯一标识,用于 ForEach 的 key 生成
  • title:日程标题,如"产品迭代评审会"
  • type:日程分类,包括 meeting(会议)、task(任务)、reminder(提醒)、birthday(生日)
  • time:具体时间,如"09:00"表示上午9点,"全天"表示全天事件
  • dateStr:日期字符串,格式为"YYYY-MM-DD",方便与 CalendarPicker 返回的 Date 进行比较
  • color:该类型对应的主题色,用于左侧色条和类型标签

这种扁平化的模型设计足够灵活,既能支持按日期筛选,也能支持按类型分组统计,同时保持代码简洁。

模拟数据

为了展示效果,我们准备了18条模拟日程,覆盖了2026年6月中的14天:

const EVENTS: CalendarEvent[] = [
  new CalendarEvent(1, '产品迭代评审会', 'meeting', '09:00',
    '2026-06-08', '#1677FF'),
  new CalendarEvent(2, 'UI组件库升级讨论', 'meeting', '14:30',
    '2026-06-08', '#1677FF'),
  new CalendarEvent(3, '完成状态管理文章', 'task', '17:00',
    '2026-06-09', '#52C41A'),
  new CalendarEvent(4, '提交月度总结报告', 'task', '全天',
    '2026-06-10', '#52C41A'),
  new CalendarEvent(5, '张工程师生日', 'birthday', '全天',
    '2026-06-10', '#FF4D4F'),
  new CalendarEvent(6, '团队代码Review', 'meeting', '10:00',
    '2026-06-12', '#1677FF'),
  new CalendarEvent(7, 'HarmonyOS技术分享', 'meeting', '15:00',
    '2026-06-13', '#1677FF'),
  new CalendarEvent(8, '购买服务器SSL证书', 'reminder', '全天',
    '2026-06-13', '#FAAD14'),
  new CalendarEvent(9, '优化首页加载性能', 'task', '全天',
    '2026-06-15', '#52C41A'),
  new CalendarEvent(10, '季度OKR制定', 'meeting', '09:30',
    '2026-06-16', '#1677FF'),
  new CalendarEvent(11, '备份数据库', 'reminder', '03:00',
    '2026-06-18', '#FAAD14'),
  new CalendarEvent(12, '李设计师生日', 'birthday', '全天',
    '2026-06-19', '#FF4D4F'),
  new CalendarEvent(13, '技术方案评审', 'meeting', '14:00',
    '2026-06-20', '#1677FF'),
  new CalendarEvent(14, '发布v3.2版本', 'task', '10:00',
    '2026-06-22', '#52C41A'),
  new CalendarEvent(15, '更新API文档', 'task', '全天',
    '2026-06-24', '#52C41A'),
  new CalendarEvent(16, '前端架构讨论会', 'meeting', '16:00',
    '2026-06-25', '#1677FF'),
  new CalendarEvent(17, '续费域名', 'reminder', '全天',
    '2026-06-26', '#FAAD14'),
  new CalendarEvent(18, '月度团建活动', 'meeting', '全天',
    '2026-06-28', '#1677FF'),
];

四种类型各有不同的颜色编码:会议使用蓝色(#1677FF),任务使用绿色(#52C41A),提醒使用金色(#FAAD14),生日使用红色(#FF4D4F)。这种色彩体系贯穿整个界面——日程列表的左侧色条、类型标签文字、近期日程的日期文字,都使用对应颜色,形成了视觉上的类型识别系统。
在这里插入图片描述

状态管理

页面的状态变量设计如下:

@State selectedDate: Date = new Date();
@State events: CalendarEvent[] = [...EVENTS];
@State currentYear: number = new Date().getFullYear();
@State currentMonth: number = new Date().getMonth() + 1;
@State currentDay: number = new Date().getDate();

四个状态变量各司其职:

  • selectedDate:当前日历选中的日期,与 CalendarPicker 双向绑定
  • events:日程数据源。虽然本 Demo 使用静态数据,但使用 [...EVENTS] 创建副本是良好的习惯,为将来从网络或本地存储加载数据留出空间
  • currentYearcurrentMonthcurrentDay:分别缓存年、月、日信息,它们会在 CalendarPicker 的 onChange 回调中同步更新

aboutToAppear 生命周期中,我们根据 selectedDate 的初始值进行一次状态同步:

aboutToAppear(): void {
  this.currentYear = this.selectedDate.getFullYear();
  this.currentMonth = this.selectedDate.getMonth() + 1;
  this.currentDay = this.selectedDate.getDate();
}

在这里插入图片描述

日期格式化与日程筛选

dateToStr 方法将 Date 对象转换为 “YYYY-MM-DD” 格式的字符串,这是连接日历组件与日程数据的桥梁:

dateToStr(date: Date): string {
  const y = date.getFullYear();
  const m = (date.getMonth() + 1).toString().padStart(2, '0');
  const d = date.getDate().toString().padStart(2, '0');
  return `${y}-${m}-${d}`;
}

有了日期格式化方法,筛选选中日期的日程只需一行过滤:

getSelectedEvents(): CalendarEvent[] {
  const dateStr = this.dateToStr(this.selectedDate);
  return this.events.filter((e: CalendarEvent) => e.dateStr === dateStr);
}

hasEvents 方法用于判断某个日期是否有日程(虽然在当前 Demo 中未直接用于日历标注,但为将来扩展留下了接口):

hasEvents(dateStr: string): boolean {
  return this.events.some((e: CalendarEvent) => e.dateStr === dateStr);
}

当月与近期统计

两个辅助方法提供了更宏观的数据视图:

getMonthEventsCount(): number {
  const prefix = `${this.currentYear}-${this.currentMonth.toString().padStart(2, '0')}`;
  return this.events.filter((e: CalendarEvent) => e.dateStr.startsWith(prefix)).length;
}

getUpcomingEvents(): CalendarEvent[] {
  const today = this.dateToStr(new Date());
  return this.events
    .filter((e: CalendarEvent) => e.dateStr >= today)
    .sort((a: CalendarEvent, b: CalendarEvent) =>
      a.dateStr.localeCompare(b.dateStr))
    .slice(0, 5);
}

getMonthEventsCount 统计当前月份的总日程数,展示在顶部信息栏中,让用户对当月的日程密度有一个快速把握。它通过构造月份前缀(如"2026-06")进行 startsWith 匹配来实现。

getUpcomingEvents 获取最近的5个未来日程,按日期升序排列,展示在"近期日程"卡片中。这个方法非常实用——即使用户当前查看的是6月12日,卡片仍会展示从今天开始(包括今天)的未来日程,帮助用户快速了解接下来要做的事情。

类型映射与视觉标识

为了在 UI 中展示友好的中文标签和视觉图标,我们定义了两个映射方法:

getTypeLabel(type: string): string {
  switch (type) {
    case 'meeting': return '会议';
    case 'task': return '任务';
    case 'reminder': return '提醒';
    case 'birthday': return '生日';
    default: return '';
  }
}

getTypeIcon(type: string): string {
  switch (type) {
    case 'meeting': return '📋';
    case 'task': return '✅';
    case 'reminder': return '🔔';
    case 'birthday': return '🎂';
    default: return '';
  }
}

图标使用 emoji 而非 Symbol Glyph,因为 emoji 在 ArkUI 中渲染效果更好,且更具表现力。每种类型有独特的 emoji:会议用剪贴板、任务用对勾、提醒用铃铛、生日用蛋糕。
在这里插入图片描述

CalendarPicker 的选中与回调

这是 CalendarPicker 集成到页面中的核心代码段:

CalendarPicker({ selected: this.selectedDate })
  .width('100%')
  .height(340)
  .borderRadius(BorderRadius.MD)
  .onChange((value: Date) => {
    this.selectedDate = value;
    this.currentYear = value.getFullYear();
    this.currentMonth = value.getMonth() + 1;
    this.currentDay = value.getDate();
  })

注意几个关键点:

  1. selected 绑定:通过 { selected: this.selectedDate } 将状态变量传入,CalendarPicker 会根据这个值高亮对应日期

  2. onChange 回调:用户点击日期或切换月份后,CalendarPicker 会触发此回调。回调中的 value 是用户新点击的日期,我们用它来更新三个状态:

    • this.selectedDate = value:更新选中日期,这个赋值又会触发 CalendarPicker 的重新渲染,形成双向绑定
    • this.currentYearthis.currentMonththis.currentDay 同步更新,驱动顶部信息栏的显示
  3. 样式配置.width('100%') 让日历撑满父容器宽度,.height(340) 给日历一个适中的固定高度,.borderRadius(BorderRadius.MD) 添加圆角

onChange 触发时机

CalendarPicker 的 onChange 在以下两种情况下触发:

  • 用户点击某个日期:此时 value 就是被点击的那个日期
  • 用户切换月份后点击日期:月份切换本身不会触发 onChange,只有日期点击才会触发。日历会始终保持一个"当前选中日"——即使切换到其他月份,选中日也保留在上次点击的日期上

这意味着如果你需要在月份切换时也执行某些逻辑(比如刷新当月日程统计),需要额外处理。不过在当前 Demo 中,月份切换不影响任何数据,所以不需要额外逻辑。

选中日期的日程展示

列表头部

Row() {
  Text(`📅 ${this.dateToStr(this.selectedDate)} ${this.getWeekday(this.selectedDate)}`)
    .fontSize(FontSize.BODY)
    .fontColor(AppColors.TEXT_PRIMARY)
    .fontWeight(FontWeight.Medium)
    .layoutWeight(1)

  Text(`${this.getSelectedEvents().length}`)
    .fontSize(FontSize.CAPTION)
    .fontColor(AppColors.TEXT_TERTIARY)
}
.width('100%')
.margin({ bottom: Spacing.SM })

列表头部左边显示完整日期和星期(如"📅 2026-06-15 周日"),右边显示当天的日程数量(如"3项")。这种左右对称的信息排布清晰而紧凑。

空状态处理

当选中日期没有日程时,展示友好的空状态提示:

if (this.getSelectedEvents().length === 0) {
  Column() {
    Text('🎉')
      .fontSize(32)
      .margin({ bottom: Spacing.SM })
    Text('当天暂无日程')
      .fontSize(FontSize.CAPTION)
      .fontColor(AppColors.TEXT_TERTIARY)
  }
  .width('100%')
  .padding({ top: Spacing.XXL, bottom: Spacing.XXL })
}

空状态使用大号 emoji 配合提示文字,视觉上不会显得空旷。这里的条件渲染采用的是 ArkUI 中 if 语句而非三元表达式,因为 if 在 ArkUI 中具有响应式能力——当条件变化时,框架会自动添加或移除对应的 UI 节点。

日程卡片设计

每条日程使用 Row 横向布局,左侧是一条4px宽的有色竖条,中间是标题和时间信息,右侧是类型图标:

Row() {
  Row()
    .width(4)
    .height('100%')
    .backgroundColor(evt.color)
    .borderRadius(2)

  Column() {
    Text(evt.title)
      .fontSize(FontSize.BODY)
      .fontColor(AppColors.TEXT_PRIMARY)
      .fontWeight(FontWeight.Medium)
      .maxLines(1)
      .textOverflow({ overflow: TextOverflow.Ellipsis })

    Row() {
      Text(evt.time)
        .fontSize(FontSize.CAPTION)
        .fontColor(AppColors.TEXT_TERTIARY)
      Text(` · ${this.getTypeLabel(evt.type)}`)
        .fontSize(FontSize.CAPTION)
        .fontColor(evt.color)
    }
    .margin({ top: 2 })
  }
  .layoutWeight(1)
  .margin({ left: Spacing.MD })

  Text(this.getTypeIcon(evt.type))
    .fontSize(20)
}
.width('100%')
.height(56)
.padding({ left: 0, right: Spacing.MD })
.backgroundColor(Color.White)
.borderRadius(BorderRadius.SM)
.margin({ bottom: Spacing.SM })

这里有几个值得注意的设计细节:

  1. 左侧色条:使用一个独立的 Row 组件,宽度仅4px,高度撑满父容器,通过 evt.color 设置背景色。这个色条是类型识别的最强视觉信号——用户不需要阅读文字,仅凭颜色就能判断这是会议(蓝)、任务(绿)、提醒(金)还是生日(红)

  2. 信息层级:标题使用 FontWeight.Medium 加粗和 TEXT_PRIMARY 主色,确保最高可读性;时间和类型使用 FontSize.CAPTION 小字号和 TEXT_TERTIARY 弱色,形成清晰的主次关系

  3. 类型标签着色:类型文字"会议"、"任务"等使用 evt.color 作为字体颜色,与左侧色条保持一致,形成双重的类型关联

  4. 文本溢出处理:标题设置了 maxLines(1)textOverflow,即使日程标题较长也不会破坏布局

  5. 固定卡片高度:每条日程卡片高度固定为56vp,配合统一的圆角和间距,形成整齐的列表韵律

近期日程聚合

在选中日期日程卡片下方,是一个"近期日程"卡片,展示当前日期之后的5个最近日程:

Column() {
  Row() {
    Text('📌 近期日程')
      .fontSize(FontSize.BODY)
      .fontColor(AppColors.TEXT_PRIMARY)
      .fontWeight(FontWeight.Medium)
  }
  .width('100%')
  .margin({ bottom: Spacing.MD })

  ForEach(this.getUpcomingEvents(), (evt: CalendarEvent) => {
    Row() {
      Column() {
        Text(evt.dateStr.substring(5))
          .fontSize(11)
          .fontColor(evt.color)
          .fontWeight(FontWeight.Bold)
      }
      .width(44)
      .alignItems(HorizontalAlign.Start)

      Column() {
        Text(evt.title)
          .fontSize(FontSize.BODY)
          .fontColor(AppColors.TEXT_PRIMARY)
          .maxLines(1)
          .textOverflow({ overflow: TextOverflow.Ellipsis })

        Text(`${evt.time} · ${this.getTypeLabel(evt.type)}`)
          .fontSize(FontSize.CAPTION)
          .fontColor(AppColors.TEXT_TERTIARY)
          .margin({ top: 1 })
      }
      .layoutWeight(1)
    }
    .width('100%')
    .height(48)
    .border({ width: { bottom: 1 }, color: '#F5F6FA' })
  })
}
.width('100%')
.padding(Spacing.LG)
.backgroundColor(Color.White)
.borderRadius(BorderRadius.MD)
.margin({ left: Spacing.LG, right: Spacing.LG, top: Spacing.LG, bottom: Spacing.XXL })

近期日程的布局与当日日程有所不同:

  • 左侧日期列:固定44vp宽度的区域,展示"MM-DD"格式的日期(通过 substring(5) 截断年份),使用日程类型的主题色作为字体颜色。这是近期日程与当日日程最明显的视觉区别——日期本身成为色彩标识的载体
  • 分隔线:使用 .border({ width: { bottom: 1 }, color: '#F5F6FA' }) 为每条日程添加底部分割线,颜色与页面背景色一致,只在视觉上起到分隔作用
  • 紧凑高度:每条48vp,比当日日程的56vp更紧凑,适合列表式快速浏览

getUpcomingEvents 返回的是按日期升序排列的未来日程,只取前5条。这种"向前看"的设计让用户能够快速把握接下来的日程安排,而不用在日历中逐个日期点击查看。

顶部信息栏设计

Row() {
  Column() {
    Text('📅 日程管理')
      .fontSize(FontSize.HEADLINE)
      .fontWeight(FontWeight.Bold)
      .fontColor(Color.White)
    Text(`${this.currentYear}${this.currentMonth}月 · ${this.getMonthEventsCount()}个日程`)
      .fontSize(10)
      .fontColor('#FFFFFF88')
      .margin({ top: 2 })
  }
  .alignItems(HorizontalAlign.Start)
  .layoutWeight(1)

  Column() {
    Text(`${this.currentDay}`)
      .fontSize(32)
      .fontWeight(FontWeight.Bold)
      .fontColor(Color.White)
    Text(this.getWeekday(this.selectedDate))
      .fontSize(10)
      .fontColor('#FFFFFF88')
  }
  .alignItems(HorizontalAlign.Center)
}
.width('100%')
.height(110)
.padding({ left: Spacing.XXL, right: Spacing.XXL })
.backgroundColor('#1a1a2e')

顶部信息栏采用左右对称的布局,深色背景(#1a1a2e)形成视觉焦点:

  • 左侧:应用标题"📅 日程管理"用大号白色粗体字,下方是当前年月和当月日程总数,半透明白色营造层次感
  • 右侧:大号数字显示当前选中日(32号字体,醒目突出),下方是对应星期

这种布局给人一种"日历小部件"的感觉——左侧是全局信息,右侧是当日焦点。深色背景与下方白色日历区域形成对比,增强了页面的视觉层次。

完整页面布局

整个页面的外层结构:

build() {
  Column() {
    // 1. 顶部信息栏
    Row() { /* ... */ }
    .backgroundColor('#1a1a2e')

    // 2. 日历选择器
    Column() {
      CalendarPicker({ selected: this.selectedDate })
        .onChange((value: Date) => { /* ... */ })
    }
    .backgroundColor(Color.White)

    // 3. 可滚动的日程详情
    Scroll() {
      Column() {
        // 3a. 当日日程卡片
        Column() { /* ... */ }

        // 3b. 近期日程卡片
        Column() { /* ... */ }
      }
    }
    .layoutWeight(1)
    .backgroundColor('#F5F6FA')
  }
  .width('100%')
  .height('100%')
}

关键布局技术点:

  1. Column 主容器:使用 width('100%')height('100%') 填满全屏
  2. Scroll 弹性区域:通过 .layoutWeight(1) 让日程区域占据剩余空间,当内容超出时可滚动
  3. 颜色分区:顶部深色(#1a1a2e)、日历白色、底部浅灰(#F5F6FA),三段颜色清晰划分功能区域
  4. 卡片式设计:日程卡片使用白色背景 + 圆角 + margin,悬浮在浅灰背景上,形成 Material Design 风格的层级关系

交互流程

用户在页面中的典型操作路径:

  1. 进入页面:默认选中今天的日期,顶部信息栏显示当前年月和当月日程总数,下方展示今天的日程列表(如果有)和近期日程
  2. 点击日历中的日期onChange 触发,更新 selectedDate 和相关状态,当日日程列表和顶部日期显示同步刷新
  3. 滑动切换月份:日历切换显示不同月份,但选中日期不变,直到用户点击新月份中的某个日期
  4. 浏览近期日程:在可滚动区域中查看未来5条日程,快速了解接下来要做的事情
  5. 空日期浏览:点击没有日程的日期,看到友好的"🎉 当天暂无日程"提示

这5个交互步骤覆盖了日程管理的核心场景:选择日期、查看日程、浏览未来安排、应对空状态。

扩展思路

当前的 Demo 已经具备了日程管理的基础框架,你可以在这个基础上进行多方面的扩展:

1. 日历标记点

在日历网格中,有日程的日期下方显示彩色小圆点,让用户在不点击的情况下就能看到哪些天有日程。这需要自定义 CalendarPicker 或者使用更底层的日历绘制能力。在 HarmonyOS NEXT 中,可以通过 CalendarPicker 的 selected 状态配合自定义样式来实现日期标记——不过这超出了 CalendarPicker 原生功能的范围,需要使用其他方案。

2. 添加/编辑日程

在页面中添加一个悬浮按钮(FAB),点击后弹出表单让用户创建新日程。表单可以包含标题输入、类型选择、日期选择、时间设置等字段。使用 State 数组的不可变更新模式来添加新数据:

addEvent(newEvent: CalendarEvent) {
  this.events = [...this.events, newEvent];
}

3. 日程提醒通知

结合 HarmonyOS 的通知能力(NotificationManager),为即将到来的日程设置系统通知提醒。可以在日程开始前15分钟弹出通知,提醒用户会议即将开始。

4. 周视图切换

除了月视图,增加周视图模式,让用户可以按周查看日程安排。这可以通过封装多个 CalendarPicker 或使用其他日期相关组件来实现。

5. 日程搜索与过滤

当日程数量增多时,增加搜索功能和类型过滤器。用户可以通过关键词搜索日程标题,或按类型(会议/任务/提醒/生日)筛选显示。

6. 数据持久化

将日程数据存储到本地(使用 Preferences 或 RelationalStore),让用户在关闭应用后再次打开时仍能看到之前添加的日程。

CalendarPicker 使用的注意事项

在实际开发中,使用 CalendarPicker 需要注意以下几点:

  1. selected 必须绑定状态变量:CalendarPicker 本身不维护内部选中状态,必须通过外部的 @State 或 @Link 变量进行双向绑定。如果你忘记在 onChange 中更新状态,日历的选中高亮将不会生效

  2. 月份切换不触发 onChange:只有点击具体日期才会触发 onChange。如果你需要感知月份切换,可以考虑使用其他方式(比如监听 CalendarPicker 内置的月份变化,但当前 API 版本未直接提供该回调)

  3. 高度控制:CalendarPicker 的最小可用高度大约在300vp左右,否则日历网格会过于拥挤。推荐设置在 320-400vp 之间

  4. 农历显示:CalendarPicker 默认显示农历信息。如果你的应用面向非中文用户或不需要农历,可以关闭该功能以节省空间

  5. 性能考虑:CalendarPicker 本身是原生组件,性能表现优秀。但如果你在 onChange 中执行了较重操作(如大量数据过滤、网络请求),建议加入防抖或节流逻辑

总结

CalendarPicker 是 HarmonyOS NEXT 提供的一个功能完备、开箱即用的日历选择器组件。通过本文的 Demo,我们展示了如何将 CalendarPicker 与日程数据模型结合,构建一个实用的日程管理界面。

本文涉及的关键技术点包括:

  • CalendarPicker 的 selected 绑定与 onChange 回调机制
  • 通过 dateToStr 格式化方法实现 Date 对象与日期字符串的互转
  • 使用 filter + dateStr 实现按日期筛选日程
  • 使用 prefix + startsWith 实现按月份统计
  • 使用 sort + slice 实现最近日程聚合
  • 四种日程类型的色彩编码体系(蓝/绿/金/红)
  • 左侧有色竖条 + 类型标签着色 + 类型图标的三重标识设计
  • 空状态的友好提示
  • 深色/白色/浅灰三段式视觉分区
  • Scroll + layoutWeight 实现弹性滚动布局

CalendarPicker 大大降低了日历交互的开发成本,开发者可以将精力更多地放在业务逻辑和 UI 体验的打磨上。希望本文能帮助你在 HarmonyOS 应用开发中更好地运用日历组件,构建出优秀的日程管理体验。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐