桌面卡片工具首页

做 HarmonyOS 应用时,最容易低估的不是某个页面怎么写,而是页面、数据、资源、扩展能力之间的边界怎么放。这个项目是一个“桌面卡片工具”:用户可以浏览模板、创建卡片、收藏、归档、备份,并把指定卡片同步到 HarmonyOS 桌面卡片。

这篇先讲整体架构,后面的文章再拆本地持久化、分类模板、Form Kit、备份恢复和统计页面。

项目结构先按能力分层

工程的主模块是 entry,页面、组件、服务和公共模型都放在 entry/src/main/ets 下:

entry/src/main/ets
├── common
│   ├── AppModels.ets
│   ├── AppSizes.ets
│   ├── AppTheme.ets
│   ├── CardImages.ets
│   ├── DesignData.ets
│   └── Routes.ets
├── components
│   ├── BottomNavBar.ets
│   ├── ShowcaseCard.ets
│   ├── MenuListItem.ets
│   └── PageHeader.ets
├── pages
│   ├── Index.ets
│   ├── CategoryPage.ets
│   ├── CardDetailPage.ets
│   ├── CardEditPage.ets
│   ├── CardManagePage.ets
│   ├── StatisticsPage.ets
│   └── BackupPage.ets
├── services
│   ├── AppDataService.ets
│   └── DesktopFormService.ets
├── entryability
├── entryformability
└── entrybackupability

这个拆法的重点是:页面只负责展示和轻量交互,真正的数据读写放在 AppDataService.ets。这样首页、分类页、详情页、管理页和桌面 Form 都能拿到同一份状态,避免每个页面自己维护一套临时数据。

页面注册:不要让路由散落在页面里

页面列表在 main_pages.json 里集中声明:

{
  "src": [
    "pages/Index",
    "pages/MarketPage",
    "pages/CategoryPage",
    "pages/CardDetailPage",
    "pages/CardEditPage",
    "pages/CardStylePage",
    "pages/StatisticsPage",
    "pages/ReminderPage",
    "pages/ThemeStorePage",
    "pages/CardManagePage",
    "pages/BackupPage",
    "pages/ProfilePage",
    "pages/AboutPage"
  ]
}

应用内跳转再通过 RoutePaths 统一管理。这样有两个好处:

  1. 页面路径变化时,只需要更新常量层。
  2. 共享组件和服务模型只保存稳定 route,不硬编码字符串。

在 ArkTS 严格模式下,裸对象和动态 key 访问都容易引发类型问题。把路由集中成静态类,也能减少编译器误判。

Ability 入口:先初始化数据,再加载主页面

应用入口是 EntryAbility.ets。这里做了两件事:初始化数据服务,加载 pages/Index

onWindowStageCreate(windowStage: window.WindowStage): void {
  this.windowStage = windowStage;
  appDataService.initialize(this.context);

  windowStage.loadContent('pages/Index', (err) => {
    if (err.code) {
      return;
    }
    this.registerIconFontIfNeeded();
  });
}

这段代码看起来简单,但顺序很关键:

  • initialize(),页面渲染时才能读到持久化状态。
  • loadContent() 成功后再注册 iconfont,规避窗口状态未就绪导致的图标注册异常。
  • onForeground() 中也补一次 registerIconFontIfNeeded(),保证恢复前台后图标仍然稳定。

项目以前遇到过 window state abnormal 一类问题,所以没有把 iconfont 注册写在更早的生命周期里。

主入口不是单页堆叠,而是“首页 + 分类 + 我的”的本地 Tab

Index.ets 里没有通过 router 切三个一级页,而是用 currentTab 控制首页、分类、个人中心三个主视图:

@State currentTab: string = 'home';
@State categoryQuery: string = '';
@State selectedCategoryId: CardCategoryId = 'countdown';
private scroller: Scroller = new Scroller();

底部导航切换时,重置搜索和分类状态,再滚动回顶部:

private handleBottomNavChange(id: string): void {
  if (id === 'create') {
    router.pushUrl({ url: RoutePaths.cardEdit });
    return;
  }
  this.currentTab = id;
  this.categoryQuery = '';
  this.hasSelectedCategory = false;
  this.scrollToTop();
}

这种设计适合轻量工具类应用:一级入口切换速度快,状态刷新集中,底部导航也不用反复创建页面栈。

数据服务是项目的核心

AppDataService.ets 是整个项目最重要的文件。它负责:

  • preferences 读取和写入本地状态。
  • 维护模板目录、主题目录、样式目录。
  • 生成首页摘要、分类卡片、详情视图、统计数据。
  • 支持卡片创建、编辑、收藏、归档、恢复、删除。
  • 处理本地备份、系统备份和桌面 Form 数据。

页面拿到的不是原始状态,而是视图模型:

const detailView = appDataService.getCardDetailView(params.cardId, params.templateId);
this.card = detailView.card;
this.isTemplate = detailView.isTemplate;

这让页面不需要知道“模板卡片”和“用户卡片”的底层差异,只根据 isTemplate 决定按钮文案和动作。

扩展能力:Form 和 Backup 都复用同一服务层

项目里除了主应用,还有两个扩展能力:

"extensionAbilities": [
  {
    "name": "EntryBackupAbility",
    "type": "backup"
  },
  {
    "name": "EntryFormAbility",
    "type": "form"
  }
]

EntryFormAbility 通过 DesktopFormService 读取当前桌面卡片数据,EntryBackupAbility 通过 AppDataService 导出和恢复快照。这样主应用、桌面卡片、系统备份三条链路不会各自维护状态。

这套架构适合什么项目

如果你的 HarmonyOS 应用符合下面几个特征,可以参考这个拆法:

  • 页面多,但核心状态只有一份。
  • 有本地编辑、收藏、归档、恢复一类操作。
  • 需要桌面卡片或备份扩展能力读取同一份业务数据。
  • UI 需要大量复用卡片、列表、导航、搜索栏等组件。
  • ArkTS 严格模式下希望减少动态对象和松散字段。

验证建议

架构完成后,不要只看首页。建议按这个顺序跑一遍:

D:\dev\command-line-tools\bin\hvigorw.bat assembleHap --no-daemon --stacktrace

然后手工检查:

  1. 启动首页,确认 AppDataService.initialize() 后首屏能显示。
  2. 切换首页、分类、我的,确认底部导航状态不串。
  3. 从分类进入详情,再进入编辑,确认路由参数不丢。
  4. 新建卡片后回到管理页,确认列表刷新。
  5. 设置桌面卡片后,确认 Form 数据能同步。

小结

这个项目的架构核心不是“页面很多”,而是“状态只有一份,能力围着状态走”。页面负责展示,服务层负责业务状态,资源层负责图片和主题,Ability/ExtensionAbility 只做入口和桥接。

对 HarmonyOS ArkTS 工程来说,这种边界比一开始写出漂亮页面更重要。边界清楚之后,后续加模板、加桌面卡片、加统计和备份,都不会把页面逻辑越写越散。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐