Harmony os 技术实战|拼豆制图01:用状态机收口 ArkUI 单页导航 Harmony os 技术实战拼豆制图01用状态机收口 ArkUI 单页导航拼豆制图这种工具型应用入口看起来只有几个 Tab但真正麻烦的是入口之间会共享图纸、搜索词、收藏状态和生成结果。若每个页面都自己维护一套跳转和数据后期加详情页、放大页、导出页时很容易出现“返回到了错误页面”或者“收藏状态没同步”的问题。这篇文章会把问题拆到能落地的层面重点解决用一个入口状态承接多页面切换而不是让页面彼此直接互相控制。让图纸详情、放大查看和底部导航之间的关系更清晰。把 UI 编排和图片转换、导出等业务服务拆开。为后续拆成多 Page 或 Navigation 路由保留迁移空间。为什么导航先要收口在当前项目中Index.ets承担了入口页职责用户可以从首页看热门图纸从图库筛选 50 张内置图纸从制作页上传图片生成编号图也可以进入收藏和个人页。真正的风险不是页面多而是这些页面都会碰到同一个图纸对象。例如首页卡片点击、图库卡片点击、收藏列表点击最终都应该进入编号图纸页并且读到同一个selectedPatternId。如果把每个入口都写成独立跳转短期能跑长期会出现两类问题一类是页面状态散在多个地方另一类是返回路径无法推断。工具型应用的交互频率高用户会反复在“找图纸、看图纸、做图纸、保存图纸”之间切换因此入口状态要像一个小型状态机一样可读、可改、可验证。落地时可以先抓三个信号ArkUI的职责是否只在一个层级里被定义而不是页面、服务和资源各写一份。activeTab相关状态是否能从用户入口一路追到结果页中间没有隐式副作用。遇到空数据、取消操作或边界输入时页面是否还能停在可继续操作的状态。页面边界不要和业务逻辑混在一起单页导航并不是把所有代码塞进一个文件而是在当前阶段先把会话状态统一放在顶层。页面 Builder 负责展示和触发事件业务服务负责转换、导出、查询。这样做的好处是当项目还在快速迭代 UI 时入口关系稳定当功能变重后也可以把某个 Builder 提升成独立页面或组件。决策点推荐做法避免的问题入口状态activeTab保存当前主入口Tab 图标变化了但内容没变图纸状态selectedPatternId指向当前图纸详情页拿到过期对象底部导航只在非放大页显示放大查看时底部栏遮挡图纸业务能力转换和导出放在 Service页面 Builder 写入大量异步逻辑这张表真正约束的是变更顺序先固定“入口状态”的归属再处理“图纸状态”的输入输出最后围绕“底部导航”做回归。只要这三处没有漂移后续增加页面、资源或参数时改动就不会一路扩散到无关模块。实际排查时也建议按这个顺序记录结论哪一层接收输入、哪一层生成结果、哪一层负责失败后的恢复。记录得越具体下一轮迭代越不容易把已经稳定的路径改坏。先定义导航和图纸状态导航状态不需要一开始就设计得很复杂但命名必须稳定。TabItem是底部导航的数据形态Pattern是页面之间共享的图纸对象这两个模型只描述数据不夹带页面行为。1. 用稳定模型描述导航入口底部导航不直接写死在 UI 里先收成一个小模型后续增加入口或替换图标时改动面会小很多。exportinterfaceTabItem{key:home|gallery|create|favorite|profile;label:string;icon:string;}exportinterfacePatternRouteState{activeTab:TabItem[key]|numbered;selectedPatternId:string;isChartExpanded:boolean;}这个边界只负责说明“现在在哪”和“正在看哪张图纸”。它不关心图纸如何生成也不关心卡片如何渲染因此可以被首页、图库和收藏列表共同复用。build 分支只做视图选择页面分支应该像目录一样清楚。build中只根据activeTab决定显示哪个视图复杂计算放到 private 方法避免在 Builder 内写临时变量和业务判断。2. build 分支保持声明式顶层build的职责是选择页面别把筛选、导出、图片解析等逻辑塞进来。build(){Column(){if(this.activeTabnumberedthis.isChartExpanded){this.ExpandedChartPage(this.getSelectedPattern());}elseif(this.activeTabhome){this.HomePage();}elseif(this.activeTabgallery){this.GalleryPage();}elseif(this.activeTabcreate){this.CreatePage();}elseif(this.activeTabnumbered){this.NumberedPage();}elseif(this.activeTabfavorite){this.FavoritePage();}else{this.ProfilePage();}if(!this.isChartExpanded){this.BottomNavigation();}}}这段代码的意图是让视图关系一眼可见。放大页优先判断是因为它会改变底部导航可见性普通主入口按稳定顺序排列后续排查时能快速定位分支。点击入口要同时更新业务上下文用户点击图纸卡片时不只是在切换页面还在更新当前图纸上下文。把这两个动作放在同一个事件里可以避免详情页依赖上一轮的图纸状态。3. 图纸点击同时更新上下文和入口从首页、图库、收藏进入详情时事件处理要写成同一套动作。privateopenPattern(patternId:string):void{this.selectedPatternIdpatternId;this.isChartExpandedfalse;this.activeTabnumbered;this.exportStatus;}selectedPatternId是业务上下文activeTab是页面上下文二者必须一起更新。顺手清空导出状态是为了避免上一张图纸的保存提示留到下一张图纸上。返回和放大页要有明确出口放大编号图是一个特殊状态它仍然属于编号图纸能力但需要隐藏底部导航并保持返回路径。这里不需要新增一个主 Tab用布尔状态表达更直接。4. 给放大页一个独立出口放大查看不是新的主入口它只是编号图纸页的展开状态。privateshowExpandedChart():void{this.isChartExpandedtrue;}privatecloseExpandedChart():void{this.isChartExpandedfalse;this.activeTabnumbered;}这个写法避免了返回时猜测来源页面。无论用户从首页还是收藏进入图纸一旦关闭放大图都回到编号图纸视图页面关系更可预测。模块配置给入口一个稳定身份入口模块本身也要在module.json5里有稳定身份。应用的主 Ability、图标、启动页和页面 profile 都应该指向明确资源否则调试时容易把 UI 问题误判成页面逻辑问题。5. 入口 Ability 配置保持单一配置层只暴露一个主入口业务页面由 ArkUI 状态控制。{ module: { name: entry, type: entry, pages: $profile:main_pages, abilities: [{ name: EntryAbility, srcEntry: ./ets/entryability/EntryAbility.ets, exported: true }] } }Stage 模型下入口 Ability 负责启动应用应用内部的首页、图库、制作页不必都暴露成系统入口。这样能降低配置噪声也方便后续统一处理启动参数。验证这条导航链路实际项目里验证不能只看页面有没有打开。更稳的做法是把入口、状态、边界数据和失败路径都走一遍尤其是拼豆图纸这种“看起来能显示放大后才暴露问题”的功能。验证前建议准备一组固定样本一条正常路径、一条空数据路径、一条失败路径再加一条连续操作路径。这样每次改动都能比较同一批场景不会只凭当前页面肉眼感觉判断。如果涉及屏幕尺寸、资源替换或系统能力还要保留修改前后的截图和关键输入方便回退时确认差异来自哪里。hvigor assembleHap--no-daemonWrite-Host真机或模拟器验证首页 - 图库 - 编号图纸 - 放大图 - 返回 - 收藏从首页热门图纸进入编号图纸页确认标题、色号、图例属于同一张图纸。从图库筛选后进入编号图纸页再返回首页确认底部导航没有停在错误状态。打开放大图后确认底部导航隐藏关闭后仍回到编号图纸页。连续点击两张不同图纸确认导出状态不会串到下一张图纸。把activeTab改成未知值时要有兜底页面或回到首页。常见问题和处理现象先看哪里处理方式Tab 高亮变了但内容没变是否只更新了图标状态把内容分支统一绑定到activeTab进入详情后仍显示上一张图selectedPatternId更新顺序点击事件里先写图纸 ID 再切换页面放大页底部被遮住isChartExpanded判断位置放大状态下不渲染底部导航返回路径混乱是否存在多个返回入口集中到closeExpandedChart或activeTabhome后续可以怎样演进当页面继续增多时可以把activeTab迁移成更正式的路由枚举并把openPattern、closeExpandedChart这类动作收进一个轻量的导航服务。若后续需要从系统分享、通知或桌面快捷方式进入某张图纸再在 Ability 启动参数里解析目标图纸 ID然后写入同一套页面状态。继续推进时建议保持三条约束先让现有主路径可回归再拆更细的组件或服务。新增状态必须能说明来源、更新时机和失败后的保留策略。新增资源或配置要能从页面反查到生成来源避免后期只靠人工记忆维护。小结单页导航的关键不是“少建页面”而是先把入口状态和业务上下文放在一个可推断的位置。拼豆制图当前阶段用activeTab selectedPatternId isChartExpanded就能覆盖主要路径页面负责展示Service 负责业务后续拆分也不会伤到用户路径。