7、打造鸿蒙原生日历组件:自定义 UI + 数据交互(附实操案例与效果图)

传统 App 的日历大多笨重难用,鸿蒙 ArkTS 声明式开发让构建一个轻量、高颜值、可交互的日历变得异常简单。今天,我们从零手写一个支持切换月份的原生鸿蒙日历组件!


🎯 一、目标功能

  • ✅ 展示当前月份的日历网格
  • ✅ 点击左右按钮切换上/下个月
  • ✅ 高亮当前日期
  • ✅ 动态生成每月的天数布局

最终效果:

markdown 复制代码
< 2025年4月 >
日 一 二 三 四 五 六
      1  2  3  4  5
6  7  8  9 10 11 12
...

🧩 二、数据准备:生成日历网格

先封装一个生成指定年月的日历数据方法:

typescript 复制代码
function generateCalendar(year: number, month: number): Array<number|null> {
  const date = new Date(year, month - 1, 1);
  const firstDay = date.getDay(); // 当月第一天是星期几
  const daysInMonth = new Date(year, month, 0).getDate(); // 当月天数

  const calendar: Array<number|null> = Array(firstDay).fill(null); // 填充空白
  for (let i = 1; i <= daysInMonth; i++) {
    calendar.push(i);
  }
  return calendar;
}
  • null 表示空白格子
  • 之后我们用 ForEach 渲染日历

🛠️ 三、编写日历组件 CalendarView

scss 复制代码
@Entry
@Component
struct CalendarView {
  @State currentYear: number = new Date().getFullYear()
  @State currentMonth: number = new Date().getMonth() + 1
  @State calendarData: Array<number|null> = []

  aboutToAppear() {
    this.updateCalendar();
  }

  updateCalendar() {
    this.calendarData = generateCalendar(this.currentYear, this.currentMonth);
  }

  prevMonth() {
    if (this.currentMonth === 1) {
      this.currentYear -= 1;
      this.currentMonth = 12;
    } else {
      this.currentMonth -= 1;
    }
    this.updateCalendar();
  }

  nextMonth() {
    if (this.currentMonth === 12) {
      this.currentYear += 1;
      this.currentMonth = 1;
    } else {
      this.currentMonth += 1;
    }
    this.updateCalendar();
  }

  build() {
    Column() {
      // 顶部标题与切换按钮
      Row() {
        Button('<').onClick(() => this.prevMonth())
        Text(`${this.currentYear}年${this.currentMonth}月`)
          .fontSize(22)
          .fontWeight(FontWeight.Bold)
          .margin({ left: 12, right: 12 })
        Button('>').onClick(() => this.nextMonth())
      }
      .justifyContent(FlexAlign.Center)
      .margin({ bottom: 20 })

      // 星期标题
      Row() {
        ['日','一','二','三','四','五','六'].forEach(day => {
          Text(day)
            .width('13%')
            .textAlign(TextAlign.Center)
            .fontSize(16)
            .fontWeight(FontWeight.Medium)
        })
      }
      .margin({ bottom: 10 })

      // 渲染日历网格
      Grid({
        columns: 7,
        space: 6
      }) {
        ForEach(this.calendarData, (day, index) => {
          if (day === null) {
            Text('')
              .width('13%')
              .height(40)
          } else {
            const isToday = day === new Date().getDate()
              && this.currentMonth === new Date().getMonth() + 1
              && this.currentYear === new Date().getFullYear();

            Text(`${day}`)
              .width('13%')
              .height(40)
              .textAlign(TextAlign.Center)
              .backgroundColor(isToday ? '#aaddff' : 'transparent')
              .borderRadius(8)
          }
        }, item => `${item}-${Math.random()}`)
      }
    }
    .padding(20)
    .alignItems(HorizontalAlign.Center)
  }
}

📸 四、实际效果图(示意)

运行效果👇(真实效果更丝滑)

  • 当前日期高亮
  • 支持上下切换月份
  • 布局自适应屏幕宽度

(如果需要,我可以给你配高清自制示意图)


📌 五、实操亮点总结

技术点 说明
@State 状态管理 切换月份后,自动更新日历
动态列表渲染 使用 ForEachGrid 实现网格布局
响应式更新 无需手动刷新界面,状态变化即自动重绘
组件复用 CalendarView 可直接插入任何页面

📖 六、扩展思考

  • ✅ 支持点击某天触发回调(例如签到、打卡)
  • ✅ 加入节假日、农历显示
  • ✅ 集成天气预报在日历格子中(超酷)

📘 下篇预告

《鸿蒙动画开发实战:做一个会跳舞的按钮》------深入鸿蒙动画系统,打造灵动交互体验!


相关推荐
编程乐学(Arfan开发工程师)44 分钟前
10、底层注解-@Conditional条件装配
java·spring boot·后端·架构
11054654011 小时前
23、电网数据管理与智能分析 - 负载预测模拟 - /能源管理组件/grid-data-smart-analysis
前端·能源
开发者小天1 小时前
React中startTransition的使用
前端·react.js·c#
帮帮志2 小时前
vue3与springboot交互-前后分离【验证element-ui输入的内容】
spring boot·后端·ui
@PHARAOH2 小时前
WHAT - 缓存命中 Cache Hit 和缓存未命中 Cache Miss
前端·缓存
计算机学姐2 小时前
基于SpringBoot的小型民营加油站管理系统
java·vue.js·spring boot·后端·mysql·spring·tomcat
海天胜景2 小时前
无法加载文件 E:\Program Files\nodejs\npm.ps1,因为在此系统上禁止运行脚本
前端·npm·node.js
MingT 明天你好!3 小时前
在vs code 中无法运行npm并报无法将“npm”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。请检查
前端·npm·node.js·visual studio code
老兵发新帖3 小时前
pnpm 与 npm 的核心区别
前端·npm·node.js
超级土豆粉3 小时前
怎么打包发布到npm?——从零到一的详细指南
前端·npm·node.js