

一键开通华为云码道 CodeArts 代码智能体:进入活动体验页面
这不是一篇“输入一句话,AI 就替我做完一个 App”的故事。真正的过程更像做产品:先跑起来,再一轮轮盯着字重、排版、图片、保存结果和异常状态返工,直到它终于像一件可以交给别人使用的作品。
中秋主题很容易落入两个极端:要么只剩古诗摘录,要么堆满月亮、桂花、灯笼,画面很热闹,却没有真正的使用场景。我最后把目标收得很窄:用户写下一个名字或一句牵挂,应用生成四句七言藏头诗,再把它排成一张可以保存到系统图库的月夜诗笺。
这个选择也决定了「追月」不能只有一张好看的首页。它至少要把四件事做完整:本地离线生成、AI 灵感创作、诗笺渲染与保存、诗词阅读。AI 是增强项,不是应用能否使用的前提;网络不好、没有 Key,用户仍然应该能写出一张诗笺。

项目使用 ArkTS 与 ArkUI 开发,最低兼容 API 12,目标 API 26。我最初低估的是界面打磨的工作量。第一版确实能生成诗,但文字小、层级弱、底部操作区像临时拼上去的,诗题、作者和落款也没有形成完整关系。它更像功能验证,而不是作品。
后面的迭代几乎都围绕“可读性”展开:把创作流程拆成编号步骤;把标题、诗文和落款字号整体放大;让藏头首字使用朱砂色,其余文字用暖白色;在复杂插画上增加深色柔光阅读区域;去掉影响画面的黑色底框;长标题、作者和祝福语根据宽度自动适配。最终的诗笺不是简单把文字压在背景图上,而是固定插画与可变内容分层处理。

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

「追月」有两条生成路径。本地模式从内置句库按藏头字挑选四句七言诗,同一输入会得到稳定结果;如果字数不足四个,系统使用“月、花、秋、桂”补齐。AI 模式则由用户在设置页自行填写阿里云百炼 API Key、服务地址和模型名,应用不会在仓库或安装包中内置作者的 Key。
AI 返回后不能直接上屏。我在解析层增加了三道校验:必须正好四行、每行必须是七个汉字、每行首字必须依次对应藏头词。只要其中一项不通过,就返回明确错误,不把格式混乱的内容塞进诗笺。没有配置、鉴权失败、超时或服务异常时,页面会提示用户切换本地诗库,应用本身仍然可用。
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 暂时不可用时,整辆“创作列车”也不会停摆。

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

项目可以运行以后,我把 AtomGit 仓库关联到 AtomCode 工作台,在华为云码道 CodeArts Agent 会话中让它完整读取仓库,并重点检查四条链路:AI 藏头诗生成、离线回退、诗笺 Canvas 渲染、保存到系统图库。
这一步对我最有价值的地方,不是让它再写一段介绍,而是把已经分散在多个文件里的实现还原成可以核对的调用链。它识别出项目采用单 Ability、三 Tab 结构,也把 PostcardPage、BailianClient、PoemResponseParser、PostcardCanvas 和 SaveHelper 之间的关系串了起来。我据此回头检查:设置是否真的从 Preferences 读取,失败是否真的落到可理解的状态,快照是否只包含诗笺而不是页面按钮。

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

诗笺最终由三层内容组成:月夜背景、透明金桂装饰、Canvas 文字层。保存时,页面先申请写入系统图库所需权限,再使用 getComponentSnapshot() 对指定诗笺组件截图,等待渲染完成后取得 PixelMap,编码为 JPEG 并写入系统图库,最后释放 PixelMap。
pixelMap = await this.getUIContext().getComponentSnapshot().get(
'postcardCanvas',
{ scale: 1, waitUntilRenderFinished: true }
);
await SaveHelper.saveToAlbum(ctx, pixelMap);
if (pixelMap !== undefined) {
pixelMap.release();
}为什么要给组件单独起名?可以把 postcardCanvas 想成摄影棚里的取景框。快照只拍取景框内的月夜、诗句和落款,外面的输入框、按钮与底部导航都不会进入成品;waitUntilRenderFinished 则是在按快门前多等一瞬,确保背景和文字已经真正画完。
这条链路实际踩过坑:预览正常不代表导出正常,图片加载方式、组件快照时机和编码过程任何一处有问题,都可能得到空白或黑白结果。真正解决问题的方法不是继续改配色,而是逐段确认“屏幕上显示的内容”和“被快照捕获的内容”是不是同一个组件树。
assembleHap,构建成功;生成 unsigned 调试包,大小 4,791,385 字节。AI 很擅长把明确的问题快速展开,但“明确”本身需要人来完成。标题太小、落款突兀、保存结果变黑、图片边缘不贴合,这些都不是一句“帮我优化一下”能自动消失的问题。只有把现象说清楚、让工具读到真实仓库、再回到模拟器验证,迭代才会往前走。
「追月」现在仍有可以继续做的地方,例如更丰富的诗库、更细的无障碍适配、对新接口的迁移。但它已经完成了我最初想做的事:把一个具体的名字写进诗里,再把这首诗稳稳地保存成一张能够寄出去的月夜明信片。
项目已在 AtomGit 开源:https://atomgit.com/lwcwam/zhuiyue-harmonyos
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。