HarmonyOS应用《玄象》开发实战:LunarCalendar.getTodayHeavenlyStems 干支计算在首页的接入
阅读时长:约 19 分钟 | 难度:★★★★☆ | 篇章:第 3 篇 · 首页与功能导航 对应源码:
entry/src/main/ets/common/utils/LunarCalendar.ets、HeavenlyStems.ets
前言
玄象项目首页顶部标题栏的“甲辰年│肖龙“不是硬编码文本,而是通过LunarCalendar.getTodayGanZhi()实时计算的今日干支。这一功能背后是玄象项目LunarCalendar工具类与HeavenlyStems工具类的协同。本篇将深入剖析玄象项目首页干支数据的计算与展示链路:从LunarCalendar.getTodayGanZhi方法实现、HeavenlyStems.getYearGanZhi干支推算算法、aboutToAppear数据初始化,到@State绑定与 UI 展示。掌握这套干支计算接入方法论,您就能为任何 HarmonyOS 应用接入实时数据。
提示:干支计算是玄象项目的核心算法之一,涉及天干地支组合、五行属性、生肖推算等复杂逻辑。
一、干支数据接入链路
1.1 完整数据流
用户打开首页 ↓ HomePage.aboutToAppear() ↓ LunarCalendar.getTodayGanZhi() ↓ HeavenlyStems.getYearGanZhi(year) ↓ HeavenlyStems.getShengXiao(year) ↓ 返回 "甲辰年│肖龙" ↓ @State todayGanZhi = '甲辰年│肖龙' ↓ UI 渲染 Text(this.todayGanZhi)1.2 首页接入代码
// HomePage.ets @State todayGanZhi: string = ''; aboutToAppear(): void { this.todayGanZhi = LunarCalendar.getTodayGanZhi(); const yiji = LunarCalendar.getTodayYiJi(); this.yiItems = yiji.yi; this.jiItems = yiji.ji; } build() { Column() { Row() { // ... Column({ space: 4 }) { Text('玄象') Text(this.todayGanZhi) // ← 展示干支 } } } }二、LunarCalendar.getTodayGanZhi 方法
2.1 完整源码
static getTodayGanZhi(): string { const now = new Date(); const year = now.getFullYear(); const month = now.getMonth() + 1; const day = now.getDate(); const ganZhi = HeavenlyStems.getYearGanZhi(year); const shengXiao = HeavenlyStems.getShengXiao(year); return `${ganZhi}年 | 肖${shengXiao}`; }2.2 方法分解
| 步骤 | 操作 | 结果 |
|---|---|---|
| 1 | new Date() | 获取当前时间 |
| 2 | now.getFullYear() | 2026 |
| 3 | HeavenlyStems.getYearGanZhi(2026) | “丙午” |
| 4 | HeavenlyStems.getShengXiao(2026) | “马” |
| 5 | 字符串拼接 | “丙午年│肖马” |
2.3 模板字符串
return `${ganZhi}年 | 肖${shengXiao}`;玄象项目使用 ES6 模板字符串,将干支与生肖拼接为展示格式。
三、HeavenlyStems 工具类
3.1 完整源码
export class HeavenlyStems { static readonly STEMS: string[] = ['甲', '乙', '丙', '丁', '戊', '己', '庚', '辛', '壬', '癸']; static readonly BRANCHES: string[] = ['子', '丑', '寅', '卯', '辰', '巳', '午', '未', '申', '酉', '戌', '亥']; static readonly SHENGXIAO: string[] = ['鼠', '牛', '虎', '兔', '龙', '蛇', '马', '羊', '猴', '鸡', '狗', '猪']; static getStem(index: number): string { return HeavenlyStems.STEMS[index % 10]; } static getBranch(index: number): string { return HeavenlyStems.BRANCHES[index % 12]; } static getYearGanZhi(year: number): string { const stemIndex = (year - 4) % 10; const branchIndex = (year - 4) % 12; return `${HeavenlyStems.STEMS[stemIndex]}${HeavenlyStems.BRANCHES[branchIndex]}`; } static getShengXiao(year: number): string { const index = (year - 4) % 12; return HeavenlyStems.SHENGXIAO[index]; } static getFiveElement(stem: string): string { const map: Record<string, string> = { '甲': '木', '乙': '木', '丙': '火', '丁': '火', '戊': '土', '己': '土', '庚': '金', '辛': '金', '壬': '水', '癸': '水' }; return map[stem] || ''; } }3.2 数据常量
| 常量 | 长度 | 内容 |
|---|---|---|
STEMS | 10 | 甲、乙、丙、丁、戊、己、庚、辛、壬、癸 |
BRANCHES | 12 | 子、丑、寅、卯、辰、巳、午、未、申、酉、戌、亥 |
SHENGXIAO | 12 | 鼠、牛、虎、兔、龙、蛇、马、羊、猴、鸡、狗、猪 |
3.3 干支推算算法
getYearGanZhi 方法
static getYearGanZhi(year: number): string { const stemIndex = (year - 4) % 10; const branchIndex = (year - 4) % 12; return `${HeavenlyStems.STEMS[stemIndex]}${HeavenlyStems.BRANCHES[branchIndex]}`; }算法原理:公元 4 年是甲子年(天干索引 0 + 地支索引 0)。
year = 2026 stemIndex = (2026 - 4) % 10 = 2022 % 10 = 2 → "丙" branchIndex = (2026 - 4) % 12 = 2022 % 12 = 6 → "午" 结果:"丙午"getShengXiao 方法
static getShengXiao(year: number): string { const index = (year - 4) % 12; return HeavenlyStems.SHENGXIAO[index]; }算法原理:公元 4 年是鼠年(索引 0)。
year = 2026 index = (2026 - 4) % 12 = 2022 % 12 = 6 → "马"3.4 五行属性映射
static getFiveElement(stem: string): string { const map: Record<string, string> = { '甲': '木', '乙': '木', '丙': '火', '丁': '火', '戊': '土', '己': '土', '庚': '金', '辛': '金', '壬': '水', '癸': '水' }; return map[stem] || ''; }玄象项目使用Record<string, string>映射表存储天干与五行属性:
| 天干 | 五行 |
|---|---|
| 甲、乙 | 木 |
| 丙、丁 | 火 |
| 戊、己 | 土 |
| 庚、辛 | 金 |
| 壬、癸 | 水 |
四、LunarCalendar 工具类
4.1 农历数据编码
private static readonly LUNAR_INFO: number[] = [ 0x04bd8, 0x04ae0, 0x0a570, 0x054d5, 0x0d260, 0x0d950, 0x16554, 0x056a0, 0x09ad0, 0x055d2, // ... 1900-2100 年共 201 个 ];玄象项目使用位编码存储农历年份信息:
0x04bd8 = 0b 0000 0100 1011 1101 1000 ↑ ↑↑↑↑ ↑↑↑↑↑↑↑↑↑↑↑ 闰月位置 12 个月份天数(30/29)4.2 农历月份天数算法
static getLunarMonthDays(year: number, month: number): number { return (LunarCalendar.LUNAR_INFO[year - 1900] & (0x10000 >> month)) ? 30 : 29; }0x10000 >> month:移位到对应月份位。&操作:判断该月是否为 30 天(1 表示 30 天,0 表示 29 天)。
4.3 农历月名与日名
static readonly MONTH_NAMES: string[] = [ '正月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '冬月', '腊月' ]; static readonly DAY_NAMES: string[] = [ '初一', '初二', ..., '三十' ];五、aboutToAppear 中初始化数据
5.1 初始化时机
aboutToAppear(): void { this.todayGanZhi = LunarCalendar.getTodayGanZhi(); const yiji = LunarCalendar.getTodayYiJi(); this.yiItems = yiji.yi; this.jiItems = yiji.ji; }玄象项目在aboutToAppear中同步初始化首页数据,确保 UI 首次渲染时数据已就绪。
5.2 同步 vs 异步
玄象项目getTodayGanZhi()是同步方法,无需等待:
aboutToAppear 开始 ↓ getTodayGanZhi() ← 同步计算,毫秒级完成 ↓ 赋值给 @State ↓ build 首次渲染(数据已就绪)六、@State todayGanZhi 绑定
6.1 状态定义
@State todayGanZhi: string = '';6.2 UI 绑定
Text(this.todayGanZhi) .fontSize(14) .fontColor(Colors.TEXT_SECONDARY)@State绑定让 UI 在数据变化时自动更新。
6.3 更新时机
- 用户首次打开首页:
aboutToAppear中初始化。 - 页面从后台切到前台:
onPageShow中刷新。
onPageShow(): void { this.todayGanZhi = LunarCalendar.getTodayGanZhi(); }七、GetTodayYiJi 宜忌数据
7.1 方法调用
const yiji = LunarCalendar.getTodayYiJi(); this.yiItems = yiji.yi; this.jiItems = yiji.ji;7.2 YiJi 接口
export interface YiJi { yi: string[]; ji: string[]; }八、干支显示的视觉设计
8.1 视觉格式
丙午年│肖马- 字号:14vp
- 颜色:
Colors.TEXT_SECONDARY - 对齐:居中
8.2 符号设计
玄象项目使用│竖线分隔干支与生肖,简洁大方。
九、干支计算的扩展
9.1 月干支
static getMonthGanZhi(year: number, month: number): string { const stemIndex = ((year - 4) * 2 + month) % 10; const branchIndex = (month + 2) % 12; return `${HeavenlyStems.STEMS[stemIndex]}${HeavenlyStems.BRANCHES[branchIndex]}`; }9.2 日干支
static getDayGanZhi(year: number, month: number, day: number): string { const days = this.getDaysFromBase(year, month, day); const stemIndex = days % 10; const branchIndex = days % 12; return `${HeavenlyStems.STEMS[stemIndex]}${HeavenlyStems.BRANCHES[branchIndex]}`; }9.3 时干支
static getHourGanZhi(dayGan: string, hour: number): string { const dayStemIndex = HeavenlyStems.STEMS.indexOf(dayGan); const stemIndex = (dayStemIndex % 5 * 2 + Math.floor(hour / 2)) % 10; const branchIndex = Math.floor((hour + 1) / 2) % 12; return `${HeavenlyStems.STEMS[stemIndex]}${HeavenlyStems.BRANCHES[branchIndex]}`; }十、玄象项目首页数据接入总结
10.1 数据来源
| 数据 | 来源方法 | 类型 |
|---|---|---|
| 今日干支 | LunarCalendar.getTodayGanZhi() | 同步 |
| 今日宜忌 | LunarCalendar.getTodayYiJi() | 同步 |
| 中间状态 | @State变量 | 响应式 |
10.2 数据流
数据(LunarCalendar)→ @State → UI(Text) 数据层 ← 纯函数,无副作用 状态层 ← @State 响应式 UI 层 ← 自动更新总结
本篇以玄象项目首页干支计算为蓝本,深入剖析了数据从算法到 UI 的完整接入链路:从LunarCalendar.getTodayGanZhi方法、HeavenlyStems.getYearGanZhi干支推算算法、aboutToAppear数据初始化,到@State绑定与 UI 展示。掌握这套数据接入方法论,您就能为任何 HarmonyOS 应用接入实时计算数据。
下一篇:《30 · @State currentTab 选中态切换与图标着色》,将带您深入玄象项目首页底部导航栏的选中态交互。
如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!
相关资源:
- HarmonyOS 官方文档:@State 装饰器
- HarmonyOS 官方文档:Text 组件
- 干支纪年法:wikipedia.org/wiki/干支
- 开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net