news 2026/10/3 11:40:52

鸿蒙原生应用实战:明信片制作页的实时预览卡与背景选择

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
鸿蒙原生应用实战:明信片制作页的实时预览卡与背景选择

鸿蒙原生应用实战:明信片制作页的实时预览卡与背景选择

App 43「校园电子明信片」制作页(Func1Tab),主题色#00B894绿色(green),4 个 Tab 分别为首页(📮)、制作(🎨)、发送(✉️)、我的(👤)。制作页采用"Header + 预览 + 背景 + 祝福语 + 字体 + 按钮"六区布局——白色单行 Header("制作明信片"20 号加粗)+明信片实时预览卡(200 高绿色渐变 + 64 号背景 emoji + 祝福语文字实时渲染,空时显示占位"在此预览你的祝福语...")+背景选择卡(🎓🎂🌸🏖️🍁 5 个 54×54 方块,选中绿色描边 + 浅绿底)+祝福语卡(90 高浅底 TextArea"写下你的祝福...")+字体样式卡(手写体/楷体/圆体/宋体 4 等分胶囊)+全宽绿色"下一步 · 发送"按钮(Toast"前往发送")。本篇基于43-e-postcard/entry/src/main/ets/pages/Func1Tab.ets(共 104 行)逐段拆解,附 4 张实机截图。

一、整体结构:六区"Header + 预览 + 背景 + 祝福 + 字体 + 按钮"布局

制作页是"品牌头部 + 实时预览 + 四项定制 + 下一步"的六区布局:

build() { Column() { this.Header() Scroll() { Column({ space: 16 }) { this.Preview() this.BgCard() this.MsgCard() this.FontCard() Button('下一步 · 发送').fontSize(15).fontColor('#FFFFFF').backgroundColor(C.primary) .width('100%').height(48).borderRadius(D.rMd) .onClick(() => { promptAction.showToast({ message: '前往发送' }); }) } .width('100%') .padding({ left: D.pad, right: D.pad, top: 16, bottom: D.pad + this.safeBottom + 20 }) } .layoutWeight(1).scrollBar(BarState.Off).align(Alignment.Top) } .width('100%').height('100%').backgroundColor(C.bg) }

6 块结构:

  1. Header— 单行品牌头("制作明信片")
  2. Preview— 明信片实时预览,@State message驱动祝福语即时渲染
  3. BgCard— 5 个背景 emoji 选择(@State activeBg)
  4. MsgCard— 祝福语 TextArea(@State message)
  5. FontCard— 4 种字体选择(@State activeFont)
  6. 下一步按钮— 全宽绿色主按钮,Toast"前往发送"

三个@State(message/activeBg/activeFont)贯穿全页——预览卡同时消费 message 与 activeBg,实现"改背景即时变预览、打字即时变预览"的所见即所得,这是制作页的核心交互模式。

项目源码开源:https://gitee.com/codenestFlow/HarmonyOSHub

二、Preview:明信片实时预览

Preview() { Stack({ alignContent: Alignment.BottomStart }) { Column() { Text(this.bgs[this.activeBg]).fontSize(64) } .width('100%').height(200).borderRadius(D.rLg).justifyContent(FlexAlign.Center) .linearGradient({ angle: 135, colors: [[C.primary, 0.0], [C.accent, 1.0]] }) Text(this.message.length > 0 ? this.message : '在此预览你的祝福语...') .fontSize(16).fontColor('#FFFFFF').padding(16).maxLines(2) .textOverflow({ overflow: TextOverflow.Ellipsis }) } .width('100%') }

技术拆解:

  1. 背景层:绿色渐变(#00B894 → #4BC9A8,135°)200 高圆角卡 + 64 号背景 emoji 居中——this.bgs[this.activeBg]随背景选择实时变化,选中 🎂 预览即显示 🎂。
  2. 祝福语层:Stack({ alignContent: Alignment.BottomStart })把文字左下对齐叠在渐变上(明信片祝福语居下的版式)——this.message.length > 0 ? this.message : '在此预览你的祝福语...'三元切换:有内容显示真实祝福语、无内容显示灰色占位提示。
  3. 文字样式:16 号白色、maxLines(2)+ 省略号防溢出(长祝福语截断)、padding(16)与边缘留距。
  4. 所见即所得:@State message与@State activeBg双驱动——在祝福语框打字、在背景区选 emoji,预览卡即时更新,用户不需要"预览"按钮,所见即所得是本页最大的交互亮点。

三、BgCard:5 背景 emoji 选择

BgCard() { Column({ space: 10 }) { Text('选择背景').fontSize(14).fontWeight(FontWeight.Bold).fontColor(C.text).width('100%') Row({ space: 10 }) { ForEach(this.bgs, (b: string, idx: number) => { Column() { Text(b).fontSize(26) } .width(54).height(54).justifyContent(FlexAlign.Center) .backgroundColor(this.activeBg === idx ? C.primarySoft : C.cardSoft).borderRadius(D.rSm) .border({ width: this.activeBg === idx ? 2 : 0, color: C.primary }) .onClick(() => { this.activeBg = idx; }) }, (b: string, idx: number) => b + idx) }.width('100%') } .width('100%').padding(16).backgroundColor(C.card).borderRadius(D.rLg).border({ width: 1, color: C.stroke }) }

5 个背景(15 行):['🎓', '🎂', '🌸', '🏖️', '🍁']——与首页模板的毕业季/生日快乐/春日物语/夏日海滩/秋日私语对应(去掉圣诞),制作页背景即首页模板 emoji 的复用。

技术要点:

  1. 选中态双重表达:选中背景浅绿底(primarySoft)+2 宽绿色描边(border({ width: 2, color: C.primary })),未选中浅灰底无描边——底色 + 描边双重强化,比单一生效更醒目(App 42 心情卡用大小/透明度、本页用底色/描边,选中态表达手段丰富)。
  2. 方块尺寸:54×54 固定方块,26 号 emoji 居中,borderRadius(D.rSm)小圆角。
  3. key 防冲突:(b: string, idx: number) => b + idx——emoji 数组可能重复时用索引拼接保证唯一(系列标准写法)。
  4. 间距:Row({ space: 10 })横向 10 间距,5 个方块恰好一行排布。

四、MsgCard:祝福语 TextArea

MsgCard() { Column({ space: 10 }) { Text('祝福语').fontSize(14).fontWeight(FontWeight.Bold).fontColor(C.text).width('100%') TextArea({ placeholder: '写下你的祝福...' }) .backgroundColor(C.cardSoft).borderRadius(D.rSm).height(90) .onChange((v: string) => { this.message = v; }) } .width('100%').padding(16).backgroundColor(C.card).borderRadius(D.rLg).border({ width: 1, color: C.stroke }) }
  • 浅底圆角输入:C.cardSoft浅灰底 + 圆角 10 + 90 高——与 App 41/42 的"透明沉浸式"不同,本页用"可见输入框"(浅底圆角),因为祝福语是"填表"性质(短文本 + 有预览联动),不需要沉浸感,明确的输入框边界反而提示"这里填祝福"。
  • 占位文案:"写下你的祝福..."直接、无歧义。
  • 实时联动:onChange写入@State message,Preview 的祝福语层即时更新——打字即预览。

五、FontCard:4 字体选择

FontCard() { Column({ space: 10 }) { Text('字体样式').fontSize(14).fontWeight(FontWeight.Bold).fontColor(C.text).width('100%') Row({ space: 10 }) { ForEach(this.fonts, (f: string, idx: number) => { Text(f).fontSize(13) .fontColor(this.activeFont === idx ? '#FFFFFF' : C.textSub) .textAlign(TextAlign.Center).layoutWeight(1).padding({ top: 9, bottom: 9 }) .backgroundColor(this.activeFont === idx ? C.primary : C.cardSoft).borderRadius(D.rSm) .onClick(() => { this.activeFont = idx; }) }, (f: string) => f) }.width('100%') } .width('100%').padding(16).backgroundColor(C.card).borderRadius(D.rLg).border({ width: 1, color: C.stroke }) }

4 种字体(16 行):['手写体', '楷体', '圆体', '宋体']——中文场景的四类字体风格。

技术要点:

  1. 等分胶囊:每个 TextlayoutWeight(1)四等分整行,textAlign(TextAlign.Center)居中,选中绿底白字、未选浅底灰字——与背景选择"方块"不同,字体选择是"胶囊条",两种选择器形态在同页并存(方块 = 视觉选择、胶囊 = 文本选择),选型贴合内容类型。
  2. Demo 简化:当前切换仅高亮、预览文字字体未实际变化——真实产品应给 Preview 的祝福语 Text 应用fontFamily属性(手写体/楷体等),Demo 保留了选择结构、字体渲染需系统字体支持。
  3. key:(f: string) => f(字体名唯一)。

六、下一步按钮

Button('下一步 · 发送').fontSize(15).fontColor('#FFFFFF').backgroundColor(C.primary) .width('100%').height(48).borderRadius(D.rMd) .onClick(() => { promptAction.showToast({ message: '前往发送' }); })
  • 文案"下一步 · 发送":明确"这是向导的下一步",比"完成"更引导流程——制作页与发送页构成"两步向导",按钮衔接两页。
  • Toast"前往发送":Demo 占位,产品化后router跳转发送页(发送页卡片预览展示"毕业季明信片 · 已完成制作"承接制作成果)。
  • 无校验:祝福语可空(预览显示占位)——明信片允许只发背景图,设计上不强制填字。

七、跨页数据自洽:制作页承上启下

制作页是"选模板 → 制作 → 发送"链路的中间环节:

  • 承接首页:首页点"制作: 毕业季"应跳转本页并预选 🎓 背景——bgs[0] = '🎓'与首页毕业季模板 emoji 一致,数据同源。
  • 衔接发送页:制作成果(背景 + 祝福语 + 字体)随"下一步 · 发送"传递到发送页——发送页卡片预览"毕业季明信片 · 已完成制作"正是制作页默认成果的展示。
  • 背景与首页模板:5 个背景 emoji(🎓🎂🌸🏖️🍁)= 首页 6 模板中的 5 个 emoji(去 🎄)——制作页背景即模板的"定制层"。
  • 我的页"我的作品"菜单↔ 制作页成果——制作完成的明信片保存在"我的作品"中可复用/再编辑。

八、实机截图与交互演示

本节结合 4 张实机截图,逐张还原制作页的交互链路。

1. 制作页首屏:预览卡 + 背景选择

第一张截图是制作页默认首屏:顶部"制作明信片"标题;下方 200 高绿色渐变预览卡——64 号 🎓 居中、左下角白色占位文字"在此预览你的祝福语...";再往下是"选择背景"卡——5 个 54×54 方块(🎓🎂🌸🏖️🍁),第一个 🎓 为浅绿底 + 绿色描边选中态;"祝福语"卡(90 高浅底输入框)与"字体样式"卡(手写体/楷体/圆体/宋体,第一个选中)部分可见。所见即所得的预览卡是整页视觉焦点。

2. 点击"🎂"背景:预览 + 选中切换

第二张截图是点击背景选择第二格"🎂"后的状态:预览卡中央的 emoji 从 🎓 变为 🎂(64 号即时更新),背景方块的选择态从第一格切换到第二格(浅绿底 + 绿描边)——@State activeBg驱动"预览 emoji + 选择框"双处同步更新,所见即所得的核心交互得到验证。

3. 点击"楷体":字体选中切换

第三张截图是点击"字体样式"第二格"楷体"后的状态:绿色选中态从"手写体"切换到"楷体"——@State activeFont由 0 变 1。四个字体胶囊等分整行(各约 1/4 屏宽),选中绿底白字与未选浅底灰字对比清晰。

4. 点击"下一步 · 发送":流程衔接 Toast

第四张截图是滚动到底部、点击全宽绿色"下一步 · 发送"按钮后的反馈:弹出"前往发送"Toast。按钮位于页面底部(祝福语卡与字体卡之后),"下一步"的向导语义明确——产品化后此处跳转发送页,承接制作成果。

九、扩展思考:所见即所得的编辑器设计

制作页的"预览实时联动"是编辑器类页面的核心模式,值得展开:

  1. 状态单一来源:@State message/activeBg/activeFont三个状态是编辑器的"文档模型",预览卡只是它们的"渲染投影"——任何一处修改,预览自动更新,无需手动"刷新预览"按钮。这是 MVVM 思想的最小实践。
  2. 预览的性能:每次打字(onChange 高频触发)都重绘预览——Demo 内容轻量无压力;若未来预览含大图/动画,需防抖(300ms)合并渲染。
  3. 字体的真实渲染:Demo 只切换高亮,产品化后 Preview 的祝福语fontFamily应跟随activeFont——需系统安装对应字体(手写体/楷体等),或使用内置字体资源。
  4. 背景的扩展:背景从 5 emoji 扩展为"渐变库 + 图片库 + 贴纸库"——activeBg从索引升级为对象({ type: 'emoji'|'gradient'|'image', value: ... }),预览层按 type 分支渲染。
  5. 撤销重做:编辑器标配 Ctrl+Z 撤销——把三个 @State 收进"历史栈"(每次修改 push 快照),是编辑器产品化的进阶能力。

十、开发者视角:制作页的调试与验证技巧

  1. 预览与状态的一致性:改背景/打字后,用 dumpLayout 确认预览卡的 emoji 与文字确实更新——"状态变了但预览没变"通常是 onChange 未正确写入 @State(检查赋值)。
  2. maxLines 的预览截断:祝福语超过 2 行时预览省略——验证长文本的省略位置与"占位 → 真实文字"的切换边界(空内容显示占位、非空显示内容)。
  3. 背景选中态的描边:border({ width: 2, color: C.primary })只在选中时存在——切换时描边出现/消失会引起 1-2px 的视觉跳动(方块大小变化),若介意可改为"始终透明描边 + 选中变色"。
  4. emoji key 的稳定性:b + idx的 key 在静态数组下正确;若未来支持增删背景,改用 id 防重排重建。
  5. 键盘遮挡:点击 TextArea 弹键盘压缩可视区,"下一步 · 发送"按钮被顶出——expandSafeArea或滚动适配(系列高频坑),真机验证键盘弹出场景。

十一、系列横向对比:编辑器类页面的四层架构

制作页是"编辑器"类页面,其架构可作为系列乃至所有编辑器产品的参考:

层级本页实现产品化演进
画布(预览)Preview 渐变卡可缩放画布/多模板
属性面板Bg/Font 选择卡颜色/贴纸/滤镜库
输入层MsgCard TextArea富文本/表情/模板句
动作层下一步按钮保存草稿/导出图片/分享

编辑器的核心是"状态即文档":本页三个 @State 就是明信片文档的全部字段(背景/祝福/字体),预览卡是文档的渲染——任何编辑器(哪怕是 Photoshop)的本质都是"操作状态 → 渲染预览",本页用 100 行代码演示了这个最小模型。产品化时把三个 @State 收拢为@Observed class Postcard对象(含 id/模板/背景/祝福/字体/创建时间),即可支撑"草稿保存、作品列表、继续编辑"等完整功能——制作页是编辑器的最小可行示例。

与系列其他表单页的对比:App 41/42 的表单是"填完提交"(一次性),制作页是"边填边看"(实时预览)——编辑器的差别在于"预览层"的存在。若 App 41 的发布页也加一个"帖子预览卡"(所见即所得),即可升级为"编辑器型发布",这是 UGC 产品提升体验的常见路径。

十二、开发者视角:制作页的调试与验证技巧

  1. 预览联动验证:改背景后确认预览 emoji 更新、打字后确认预览文字更新——用 dumpLayout 检查两个状态是否同时反映;若预览不更新,第一排查onChange是否写入 @State(检查赋值与引用)。
  2. 占位/真实文字的边界:message.length > 0切换占位与真实文字——测试空、1 字、超 2 行三种情况,确认省略号与占位消失的边界正确。
  3. 选中描边的视觉跳动:border({ width: 2 })选中出现/未选消失会引起 1-2px 尺寸变化——若视觉敏感,改为"始终 2 宽透明描边 + 选中变色"(尺寸恒定)。
  4. emoji key:b + idx静态数组下正确;若未来支持用户自定义背景(增删数组),改用 id 防重排重建。
  5. 字体渲染的真实性:Demo 只切高亮未应用字体——产品化时确认系统字体资源(手写体/楷体等)是否可用,不可用则用内置字体文件,避免预览与实际不符。

十三、扩展思考:明信片制作的情感化与模板句

制作页的祝福语输入可进一步情感化:

  1. 模板句库:提供常用祝福句一键插入("祝你生日快乐!""毕业快乐,前程似锦!")——降低"不知写什么"的门槛,模板句是明信片产品的情感效率工具。
  2. 祝福语的字数体验:当前 TextArea 无字数上限——明信片讲究"短而真挚",产品化可设 100 字上限 + 计数(参考 App 42 树洞的 0/1000),引导克制表达。
  3. 字体与情绪的匹配:手写体适合温情(祝福)、楷体适合正式(师恩)、圆体适合可爱(朋友)、宋体适合雅致(风景)——字体选择与情感场景的匹配建议在产品中给出"场景推荐"。
  4. 背景与祝福的联动:选 🎂 背景时推荐生日祝福模板句、选 🎓 推荐毕业祝福——背景 emoji 已隐含场景,联动推荐降低选择成本。
  5. 预览的情感反馈:明信片制作完成后可加"寄出前的心动确认"(预览 + "就它了"),强化"心意已就绪"的仪式感——与发送页"已完成制作"绿色状态衔接。

十四、FAQ 与一句话总结

Q1:为什么祝福语不强制填写?明信片允许"只发背景 + emoji"的极简形式(预览占位提示而非拦截),降低发送门槛;App 41/42 的"请输入内容"拦截是针对"内容为主体"的 UGC,明信片的主体是模板+心意,填不填字是用户自由。

Q2:预览卡的字体为什么没变?Demo 的字体选择只切高亮,fontFamily未实际应用(需系统字体支持)。产品化后给预览 Text 绑定字体即可,选择结构已就绪。

Q3:背景选择为什么是方块而不是胶囊?背景是"视觉内容"(emoji 图案),方块(54×54)像"色卡/图案块"更贴合视觉选择;字体是"文本选项",胶囊(等分条)更像按钮。选择器形态贴合选项的感知类型,是本页的设计细节。

Q4:制作页和首页模板什么关系?首页是"选模板"(6 个渐变模板),制作页是"做定制"(在模板基础上换背景/写祝福/选字体)——首页点"制作"跳转本页并预选对应背景,制作成果流向发送页。三页构成"选 → 做 → 发"的完整向导。

Q5:为什么按钮叫"下一步 · 发送"而不叫"完成"?制作只是流程的第一步,后面还有发送页——"下一步"明确指示流程方向(向导语义),比"完成"更准确,引导用户继续而非结束。

一句话总结:制作页以"实时预览 + 背景选择 + 祝福语输入 + 字体选择"完成了明信片定制的最小闭环。所见即所得的预览联动(@State双驱动)、背景的"底色 + 描边"双重选中态、字体/背景两种选择器形态的差异化设计,都是编辑器类页面的标准手法——104 行代码,一个"改什么都能即时看到"的明信片制作台,也是系列中"实时预览"体验最完整的页面。

最后从验收视角确认:四张实机截图覆盖了首屏(预览卡+背景选择)、背景切换(🎂 预览即时更新)、字体切换(楷体选中)、下一步按钮(前往发送 Toast)四个状态,所见即所得的预览联动(背景 emoji 与预览同步、祝福语实时渲染)、背景"底色+描边"双重选中态、字体胶囊等分选择均验证正常。跨页数据自洽通过核对——制作页 5 背景 emoji(🎓🎂🌸🏖️🍁)与首页 6 模板中的 5 个 emoji 一致,默认背景 🎓 对应首页毕业季模板与发送页成果卡,制作成果(背景+祝福语+字体)随"下一步 · 发送"流向发送页。整页 104 行代码完成"实时预览 + 四项定制"的编辑器最小闭环,验收通过,可作为所见即所得编辑器的参考范本。

补充开发问答:Q1 预览卡为什么要放在最上方?——编辑器"先看结果再改参数"的心智模型,预览置顶让用户每次修改都即时看到效果;若参数在预览上方,改完要滚动回顶部看效果,体验割裂。Q2 背景选择的选中态为什么用描边?——描边(2 宽绿边)在浅色方块上醒目且不改变底色语义(primarySoft 浅绿底 + 绿描边双重提示),比纯底色更清晰。Q3 祝福语最多显示几行?——预览maxLines(2)两行省略,长祝福语截断但完整内容在明信片中仍完整(预览是"效果示意"非"内容截断")。Q4 下一步按钮为什么不做校验?——明信片允许只发背景(祝福可空),预览占位已提示"在此预览你的祝福语",无需强制;接收人校验在发送页负责(发送页"请填写接收人")。Q5 字体选择产品化后如何真正生效?——给预览 Text 绑定fontFamily并在资源目录放置四种字体文件(手写体/楷体/圆体/宋体),选择即切换,当前结构已预留。所见即所得的核心,就是状态与渲染的即时绑定。

最后以一句收尾:制作者的点滴定制,都在预览中即时绽放。 回望整页,从实时预览到背景/祝福/字体三项定制,从状态驱动的所见即所得到下一步的流程衔接,每个设计都在让"把心意做出来"变得简单而愉悦——这就是制作页的全部价值,也是编辑器类页面的最小范本。

每一次定制都是心意的具象化,每一张预览都是温暖的彩排。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/3 11:39:47

从零搭建AI工程能力:先跑通最小闭环,再谈优化

1. 从零搭建AI工程能力,为什么大多数人卡在第一步就放弃了“ai-engineering-from-scratch”这个标题,我第一次看到的时候,脑子里蹦出来的不是某个具体框架或者工具,而是一个很现实的问题:一个完全没有AI工程背景的人&a…

作者头像 李华
网站建设 2026/10/3 11:38:50

从零构建大语言模型:AI工程实战路径与核心技术拆解

不是所有人都需要从零手搓一个神经网络,但如果你真的想搞懂 AI 工程里那些“调参”、“过拟合”、“显存爆炸”到底是怎么回事,从零开始把一个大语言模型造一遍,是最快、也最扎实的路。这篇内容就是围绕“ai-engineering-from-scratch”这条学…

作者头像 李华
网站建设 2026/10/3 11:38:31

superpowers:用技能文件系统让Codex驾驭复杂编程任务

说实话,第一次听说superpowers这个词的时候,我以为又是哪个效率工具搞的中二营销。直到我在GitHub上翻到obra/superpowers这个项目,认真读了一遍文档,才发现自己之前对Codex这类AI编程助手的用法,一直停留在很浅的层面…

作者头像 李华
网站建设 2026/10/3 11:38:10

面渣逆袭:Java基础高频面试题深度解析与底层原理

面渣这个称呼,第一次看到的时候我愣了几秒,然后苦笑——这不就是当年的自己吗。面试Java基础岗,被面试官从 HashMap 问到 String ,再从集合问到多线程,每个问题都“看着眼熟、说着卡壳”,笔试能写&…

作者头像 李华
网站建设 2026/10/3 11:36:55

OpenCV实战项目全解析:从环境搭建到物体识别与图像处理

1. 项目全景图谱:52个项目的分级与选型 如果你和我一样,是看了某个"52个OpenCV实战项目"合集却不知道从哪下手才开始接触图像处理的,我特别理解你现在的状态:收藏了、下载了、然后就没有然后了。这里面有相当大一部分原…

作者头像 李华
网站建设 2026/10/3 11:36:53

把DeepSeek装进WPS:JS宏直连API实现AI润色翻译摘要

以前我在WPS里改方案,最烦的就是在浏览器和编辑器之间来回切。选中一段文字,复制到网页对话框,等AI结果,再复制回来,重新调格式……一天下来,这种机械操作能占掉大把时间。后来DeepSeek开放了API&#xff0…

作者头像 李华