首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >用 HarmonyOS 做一张会写诗的月夜明信片:追月的完整开发复盘

用 HarmonyOS 做一张会写诗的月夜明信片:追月的完整开发复盘

原创
作者头像
LucianaiB
发布于 2026-09-29 18:54:38
发布于 2026-09-29 18:54:38
390
举报
文章被收录于专栏:AIAI

一键开通华为云码道 CodeArts 代码智能体:进入活动体验页面

仓库地址:lwcwam/zhuiyue-harmonyos

这不是一篇“输入一句话,AI 就替我做完一个 App”的故事。真正的过程更像做产品:先跑起来,再一轮轮盯着字重、排版、图片、保存结果和异常状态返工,直到它终于像一件可以交给别人使用的作品。

一、我想做的不是诗词展示页,而是一张可以寄出去的诗笺

中秋主题很容易落入两个极端:要么只剩古诗摘录,要么堆满月亮、桂花、灯笼,画面很热闹,却没有真正的使用场景。我最后把目标收得很窄:用户写下一个名字或一句牵挂,应用生成四句七言藏头诗,再把它排成一张可以保存到系统图库的月夜诗笺。

这个选择也决定了「追月」不能只有一张好看的首页。它至少要把四件事做完整:本地离线生成、AI 灵感创作、诗笺渲染与保存、诗词阅读。AI 是增强项,不是应用能否使用的前提;网络不好、没有 Key,用户仍然应该能写出一张诗笺。

二、从“能运行”到“愿意多看一眼”

项目使用 ArkTS 与 ArkUI 开发,最低兼容 API 12,目标 API 26。我最初低估的是界面打磨的工作量。第一版确实能生成诗,但文字小、层级弱、底部操作区像临时拼上去的,诗题、作者和落款也没有形成完整关系。它更像功能验证,而不是作品。

后面的迭代几乎都围绕“可读性”展开:把创作流程拆成编号步骤;把标题、诗文和落款字号整体放大;让藏头首字使用朱砂色,其余文字用暖白色;在复杂插画上增加深色柔光阅读区域;去掉影响画面的黑色底框;长标题、作者和祝福语根据宽度自动适配。最终的诗笺不是简单把文字压在背景图上,而是固定插画与可变内容分层处理。

背景中的月亮、桂花、云气、亭台和湖面使用完整的月夜插画呈现,文字则由 Canvas 动态绘制。这样既保留了画面的细节,也能根据用户输入替换诗题、四句诗、作者、祝福和日期。

三、AI 创作可以有,但不能把应用绑在网络上

「追月」有两条生成路径。本地模式从内置句库按藏头字挑选四句七言诗,同一输入会得到稳定结果;如果字数不足四个,系统使用“月、花、秋、桂”补齐。AI 模式则由用户在设置页自行填写阿里云百炼 API Key、服务地址和模型名,应用不会在仓库或安装包中内置作者的 Key。

AI 返回后不能直接上屏。我在解析层增加了三道校验:必须正好四行、每行必须是七个汉字、每行首字必须依次对应藏头词。只要其中一项不通过,就返回明确错误,不把格式混乱的内容塞进诗笺。没有配置、鉴权失败、超时或服务异常时,页面会提示用户切换本地诗库,应用本身仍然可用。

核心代码:让本地创作和 AI 创作各走各的路

代码语言:typescript
复制
if (!this.useAI) {
  this.lines = generateAcrostic(this.nameInput);
  this.statusText = '已从本地诗库生成 · 无网络消耗';
  return;
}

const settings: AISettings = await AISettingsStore.load(context);
const input: AIPoemInput = {
  acrostic: this.nameInput.trim(),
  recipient: this.recipientInput.trim() || '重要的人',
  mood: this.moodInput.trim() || '温柔',
  wish: this.wishInput.trim() || '平安团圆'
};
this.lines = (await BailianClient.generate(settings, input)).lines;

用一句大白话解释:这段代码像给创作入口铺了两条轨道。本地轨道不需要网络,按下按钮马上从内置诗库取句;AI 轨道先读取用户自己的配置,再把藏头词、赠予对象、情绪和祝福一起交给模型。两条轨道互不拖累,所以 AI 暂时不可用时,整辆“创作列车”也不会停摆。

四、诗历不是凑页面,而是把“月下阅读”补完整

创作之外,我保留了一页诗历。十二首月夜诗词按日期轮换,用户可以查看前一日、今日和后一日。这里不只展示名句,而是补齐题目、作者、完整原文、诗意今译和月下赏析。这个页面让我意识到:传统文化应用不该只负责“摆一句古诗”,还应该帮助用户理解诗人在什么处境下望月、为什么写下这句话。

五、我怎样使用华为云码道 CodeArts Agent

项目可以运行以后,我把 AtomGit 仓库关联到 AtomCode 工作台,在华为云码道 CodeArts Agent 会话中让它完整读取仓库,并重点检查四条链路:AI 藏头诗生成、离线回退、诗笺 Canvas 渲染、保存到系统图库。

这一步对我最有价值的地方,不是让它再写一段介绍,而是把已经分散在多个文件里的实现还原成可以核对的调用链。它识别出项目采用单 Ability、三 Tab 结构,也把 PostcardPage、BailianClient、PoemResponseParser、PostcardCanvas 和 SaveHelper 之间的关系串了起来。我据此回头检查:设置是否真的从 Preferences 读取,失败是否真的落到可理解的状态,快照是否只包含诗笺而不是页面按钮。

第二轮分析专门看离线回退和图库保存。CodeArts Agent 把错误码、本地句库、权限声明、组件快照和图库写入路径放到同一张图里。对参赛文章来说,这张截图比一句“我使用了 AI 编程工具”更有说服力,因为它展示了工具确实理解了仓库中的具体实现。

六、保存不是一个按钮,而是一条需要跑通的链路

诗笺最终由三层内容组成:月夜背景、透明金桂装饰、Canvas 文字层。保存时,页面先申请写入系统图库所需权限,再使用 getComponentSnapshot() 对指定诗笺组件截图,等待渲染完成后取得 PixelMap,编码为 JPEG 并写入系统图库,最后释放 PixelMap。

核心代码:只截诗笺,不截按钮和页面导航

代码语言:typescript
复制
pixelMap = await this.getUIContext().getComponentSnapshot().get(
  'postcardCanvas',
  { scale: 1, waitUntilRenderFinished: true }
);
await SaveHelper.saveToAlbum(ctx, pixelMap);

if (pixelMap !== undefined) {
  pixelMap.release();
}

为什么要给组件单独起名?可以把 postcardCanvas 想成摄影棚里的取景框。快照只拍取景框内的月夜、诗句和落款,外面的输入框、按钮与底部导航都不会进入成品;waitUntilRenderFinished 则是在按快门前多等一瞬,确保背景和文字已经真正画完。

这条链路实际踩过坑:预览正常不代表导出正常,图片加载方式、组件快照时机和编码过程任何一处有问题,都可能得到空白或黑白结果。真正解决问题的方法不是继续改配色,而是逐段确认“屏幕上显示的内容”和“被快照捕获的内容”是不是同一个组件树。

七、当前结果与验证

  • DevEco Studio 26.0.0.851、HarmonyOS SDK 26.0.0.105、HarmonyOS 7 / API 26 模拟器完成运行验证。
  • 本地藏头诗在无网络、无 API Key 时仍可使用。
  • AI 结果经过行数、字数和藏头顺序校验。
  • 诗笺可通过组件快照保存到系统图库。
  • 2026 年 9 月 29 日重新执行 assembleHap,构建成功;生成 unsigned 调试包,大小 4,791,385 字节。

八、做完之后,我对 AI 编程有了一个更朴素的判断

AI 很擅长把明确的问题快速展开,但“明确”本身需要人来完成。标题太小、落款突兀、保存结果变黑、图片边缘不贴合,这些都不是一句“帮我优化一下”能自动消失的问题。只有把现象说清楚、让工具读到真实仓库、再回到模拟器验证,迭代才会往前走。

「追月」现在仍有可以继续做的地方,例如更丰富的诗库、更细的无障碍适配、对新接口的迁移。但它已经完成了我最初想做的事:把一个具体的名字写进诗里,再把这首诗稳稳地保存成一张能够寄出去的月夜明信片。

项目已在 AtomGit 开源:https://atomgit.com/lwcwam/zhuiyue-harmonyos

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

目录
  • 一、我想做的不是诗词展示页,而是一张可以寄出去的诗笺
  • 二、从“能运行”到“愿意多看一眼”
  • 三、AI 创作可以有,但不能把应用绑在网络上
    • 核心代码:让本地创作和 AI 创作各走各的路
  • 四、诗历不是凑页面,而是把“月下阅读”补完整
  • 五、我怎样使用华为云码道 CodeArts Agent
  • 六、保存不是一个按钮,而是一条需要跑通的链路
    • 核心代码:只截诗笺,不截按钮和页面导航
  • 七、当前结果与验证
  • 八、做完之后,我对 AI 编程有了一个更朴素的判断
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档