HarmonyOS掌上记账APP开发实践第2篇:从零搭建记账应用 — MoneyTrack 工程全景解析![

从零搭建记账应用 --- MoneyTrack 工程全景解析

概述

当开发者第一次接触一个新的鸿蒙应用工程时,面对纷繁的目录结构和配置文件,往往不知从何入手。理解工程的骨架和各个配置文件的用途,是高效开发和定位问题的第一步。MoneyTrack 基于 HarmonyOS Stage 模型构建,采用标准的多模块 HAR 工程布局,其工程结构代表了当前鸿蒙商业级应用的典型范式。本文从工程目录结构出发,逐一解析 AppScope 全局配置、products 入口模块、commons 通用库、components 业务组件和 features 特性页面的完整划分与设计思路,并深入分析关键配置文件的细节,帮助读者建立起鸿蒙应用工程的全局认知。

核心知识点

1. Stage 模型工程结构

HarmonyOS Stage 模型的工程包含三个主要区域:

  • AppScope :存放全局应用配置,包括 app.json5(bundleName、版本号、图标等)和 resources 目录(全局资源),是应用级元数据的集中管理区域。
  • 模块(modules) :每个 HAR 或 entry 模块独立存放,包含 src/main/module.json5(模块配置)、src/main/ets/(ArkTS 代码目录)、src/main/resources/(模块级资源目录)。
  • 编译配置 :根目录 build-profile.json5 定义所有模块和构建目标,hvigor/hvigor-config.json5 配置构建工具链参数。

MoneyTrack 的完整工程目录结构如下:
#mermaid-svg-XdXVUGpvuDYTtAHQ{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-XdXVUGpvuDYTtAHQ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-XdXVUGpvuDYTtAHQ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-XdXVUGpvuDYTtAHQ .error-icon{fill:#552222;}#mermaid-svg-XdXVUGpvuDYTtAHQ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-XdXVUGpvuDYTtAHQ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-XdXVUGpvuDYTtAHQ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-XdXVUGpvuDYTtAHQ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-XdXVUGpvuDYTtAHQ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-XdXVUGpvuDYTtAHQ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-XdXVUGpvuDYTtAHQ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-XdXVUGpvuDYTtAHQ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-XdXVUGpvuDYTtAHQ .marker.cross{stroke:#333333;}#mermaid-svg-XdXVUGpvuDYTtAHQ svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-XdXVUGpvuDYTtAHQ p{margin:0;}#mermaid-svg-XdXVUGpvuDYTtAHQ .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-XdXVUGpvuDYTtAHQ .cluster-label text{fill:#333;}#mermaid-svg-XdXVUGpvuDYTtAHQ .cluster-label span{color:#333;}#mermaid-svg-XdXVUGpvuDYTtAHQ .cluster-label span p{background-color:transparent;}#mermaid-svg-XdXVUGpvuDYTtAHQ .label text,#mermaid-svg-XdXVUGpvuDYTtAHQ span{fill:#333;color:#333;}#mermaid-svg-XdXVUGpvuDYTtAHQ .node rect,#mermaid-svg-XdXVUGpvuDYTtAHQ .node circle,#mermaid-svg-XdXVUGpvuDYTtAHQ .node ellipse,#mermaid-svg-XdXVUGpvuDYTtAHQ .node polygon,#mermaid-svg-XdXVUGpvuDYTtAHQ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-XdXVUGpvuDYTtAHQ .rough-node .label text,#mermaid-svg-XdXVUGpvuDYTtAHQ .node .label text,#mermaid-svg-XdXVUGpvuDYTtAHQ .image-shape .label,#mermaid-svg-XdXVUGpvuDYTtAHQ .icon-shape .label{text-anchor:middle;}#mermaid-svg-XdXVUGpvuDYTtAHQ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-XdXVUGpvuDYTtAHQ .rough-node .label,#mermaid-svg-XdXVUGpvuDYTtAHQ .node .label,#mermaid-svg-XdXVUGpvuDYTtAHQ .image-shape .label,#mermaid-svg-XdXVUGpvuDYTtAHQ .icon-shape .label{text-align:center;}#mermaid-svg-XdXVUGpvuDYTtAHQ .node.clickable{cursor:pointer;}#mermaid-svg-XdXVUGpvuDYTtAHQ .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-XdXVUGpvuDYTtAHQ .arrowheadPath{fill:#333333;}#mermaid-svg-XdXVUGpvuDYTtAHQ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-XdXVUGpvuDYTtAHQ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-XdXVUGpvuDYTtAHQ .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-XdXVUGpvuDYTtAHQ .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-XdXVUGpvuDYTtAHQ .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-XdXVUGpvuDYTtAHQ .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-XdXVUGpvuDYTtAHQ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-XdXVUGpvuDYTtAHQ .cluster text{fill:#333;}#mermaid-svg-XdXVUGpvuDYTtAHQ .cluster span{color:#333;}#mermaid-svg-XdXVUGpvuDYTtAHQ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-XdXVUGpvuDYTtAHQ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-XdXVUGpvuDYTtAHQ rect.text{fill:none;stroke-width:0;}#mermaid-svg-XdXVUGpvuDYTtAHQ .icon-shape,#mermaid-svg-XdXVUGpvuDYTtAHQ .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-XdXVUGpvuDYTtAHQ .icon-shape p,#mermaid-svg-XdXVUGpvuDYTtAHQ .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-XdXVUGpvuDYTtAHQ .icon-shape .label rect,#mermaid-svg-XdXVUGpvuDYTtAHQ .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-XdXVUGpvuDYTtAHQ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-XdXVUGpvuDYTtAHQ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-XdXVUGpvuDYTtAHQ :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} MoneyTrack 根目录
/
AppScope/
products/
commons/
components/
features/
hvigor/
build-profile.json5
hvigorfile.ts
app.json5
resources/
entry/
module.json5
src/main/ets/
src/main/resources/
pages/MainEntry.ets
entryability/EntryAbility.ets
entryformability/EntryFormAbility.ets
commonlib/
lib_network/
bill_base/
bill_card/
bill_chart/
bill_manage/
bill_data_processing/
asset_base/
asset_card/
asset_manage/
aggregated_login/
app_setting/
collect_personal_info/
feedback/
membership/
module_ui_base/
home/
statistics/
assets/
mine/

2. AppScope 配置详解

AppScope/app.json5 定义应用的全局元数据:

json5 复制代码
{
  "app": {
    "bundleName": "com.hyh.moneyTrack",
    "vendor": "example",
    "versionCode": 1000000,
    "versionName": "1.0.0",
    "icon": "$media:layered_image",
    "label": "$string:app_name"
  }
}
  • bundleName:应用唯一标识,格式通常为反域名形式,在华为应用市场发布后不可更改。
  • versionCode :整型版本号,用于应用市场的版本比较和灰度发布,此处 1000000 对应 1.0.0
  • versionName:面向用户的版本名称,显示在应用详情页。
  • icon/label :引用 resources/base/element/string.jsonresources/base/media/ 目录下的资源。

资源文件位于 AppScope/resources/base/ 目录,包含 element/string.json(字符串国际化)、media/(应用图标和分层图)。

3. products/entry 入口模块

products/entry 作为应用入口(type 为 "entry"),是唯一一个可独立安装的模块。其 module.json5 配置了完整的 Ability 体系:

json5 复制代码
{
  "module": {
    "name": "entry",
    "type": "entry",
    "description": "$string:module_desc",
    "mainElement": "EntryAbility",
    "deviceTypes": ["phone", "tablet"],
    "deliveryWithInstall": true,
    "installationFree": false,
    "pages": "$profile:main_pages",
    "metadata": [
      // todo: 修改client_id
      { "name": "client_id", "value": "******" }
    ],
    "querySchemes": [
      "weixin", "wxopensdk", "qqopenapi", "https"
    ],
    "abilities": [
      {
        "name": "EntryAbility",
        "supportWindowMode": ["fullscreen", "floating"],
        "srcEntry": "./ets/entryability/EntryAbility.ets",
        "description": "$string:EntryAbility_desc",
        "icon": "$media:layered_image",
        "label": "$string:EntryAbility_label",
        "startWindowIcon": "$media:app_icon",
        "startWindowBackground": "$color:start_window_background",
        "exported": true,
        "skills": [
          {
            "entities": ["entity.system.home"],
            "actions": ["action.system.home"]
          }
        ]
      }
    ],
    "requestPermissions": [
      { "name": "ohos.permission.PUBLISH_AGENT_REMINDER" }
    ],
    "extensionAbilities": [
      {
        "name": "EntryFormAbility",
        "srcEntry": "./ets/entryformability/EntryFormAbility.ets",
        "label": "$string:EntryFormAbility_label",
        "description": "$string:EntryFormAbility_desc",
        "type": "form",
        "metadata": [
          {
            "name": "ohos.extension.form",
            "resource": "$profile:form_config"
          }
        ]
      }
    ]
  }
}

关键配置说明:

  • mainElement :指定应用启动时加载的 Ability(EntryAbility),声明了 skillsentity.system.homeaction.system.home 表示这是桌面入口。
  • deviceTypes:支持 phone 和 tablet 两种设备,系统会根据运行设备自动适配形态。
  • querySchemes :声明应用需要查询的 URL Scheme,包括 weixin(微信支付/登录)、qqopenapi(QQ 互联)、https(通用 Web 跳转),是实现第三方登录和支付的必要配置。
  • requestPermissions :声明 PUBLISH_AGENT_REMINDER 权限,用于账单提醒和通知。
  • extensionAbilities :注册 EntryFormAbility(类型为 form),用于提供服务卡片能力。

EntryAbility 的实际代码如下------它在 onCreate 中设置应用主题色,在 onWindowStageCreate 中加载主页面:

typescript 复制代码
export default class EntryAbility extends UIAbility {
  public onCreate(want: Want): void {
    this.context.getApplicationContext()
      .setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_LIGHT);
    this.handleWantInfo(want);
  }

  public onNewWant(want: Want): void {
    this.handleWantInfo(want);
  }

  public onWindowStageCreate(windowStage: window.WindowStage): void {
    windowStage.loadContent('pages/MainEntry', (err) => {
      if (err.code) {
        Logger.error(TAG, 'Failed to load the content.' + JSON.stringify(err));
        return;
      }
      contextUtil.setUiContext(windowStage.getMainWindowSync().getUIContext());
    });
  }
}

主入口页面 MainEntry.ets 使用 Tabs 组件组织四个 Tab 页面,每个 Tab 对应一个特性模块的视图:

typescript 复制代码
@Entry
@ComponentV2
struct MainEntry {
  vm: MainEntryVM = MainEntryVM.instance;

  build() {
    Navigation(this.vm.navStack) {
      Tabs({ barPosition: BreakpointSystem.isTablet ? BarPosition.Start : BarPosition.End,
             index: this.vm.curIndex, controller: this.vm.tabController }) {
        TabContent() { HomeView() }
          .tabBar(this.tabBarBuilder(TabIndexMap.HOME))
        TabContent() { StatisticsView() }
          .tabBar(this.tabBarBuilder(TabIndexMap.STATISTICS))
        TabContent() { AssetsView() }
          .tabBar(this.tabBarBuilder(TabIndexMap.ASSETS))
        TabContent() { MineView() }
          .tabBar(this.tabBarBuilder(TabIndexMap.MINE))
      }
    }
  }
}

4. 构建配置:hvigor-config.json5

hvigor/hvigor-config.json5 控制鸿蒙构建工具链的行为:

json5 复制代码
{
  "modelVersion": "5.0.2",
  "dependencies": {},
  "execution": {
    // "analyze": "normal",       /* 构建分析模式:normal | advanced | false */
    // "daemon": true,            /* 守护进程编译,加速重复构建 */
    // "incremental": true,       /* 增量编译,只重新编译变更文件 */
    // "parallel": true,          /* 并行编译,多模块同时构建 */
    // "typeCheck": false,        /* 是否启用类型检查 */
  },
  "logging": {
    // "level": "info"           /* 日志级别:debug | info | warn | error */
  },
  "debugging": {
    // "stacktrace": false       /* 编译错误时是否输出完整堆栈 */
  },
  "nodeOptions": {
    // "maxOldSpaceSize": 8192   /* Node.js 最大堆内存,单位 MB */
  }
}

这些配置项对构建性能有直接影响:

  • 并行编译(parallel):21 个模块的工程开启并行编译后,构建时间可从数分钟缩短至数十秒。
  • 增量编译(incremental):开发阶段只编译发生变化的文件,大幅提升热重载效率。
  • 类型检查(typeCheck):Release 构建建议开启,Debug 构建可关闭以加快编译速度。

5. 各层目录结构详析

commons 通用层

commons/commonlib 是项目的"工具箱",其内部结构展示了通用模块的最佳组织方式:

复制代码
commons/commonlib/src/main/ets/
├── components/           # 通用 UI 组件
│   ├── CommonButton.ets
│   ├── CommonDayPicker.ets
│   ├── CommonDivider.ets
│   ├── CommonHeader.ets
│   ├── CommonMonthPicker.ets
│   ├── ContainerColumn.ets
│   └── ContainerRow.ets
├── constants/            # 常量与枚举
│   ├── CommonConstants.ets
│   └── CommonEnums.ets
├── dialogs/              # 通用弹窗
│   └── CommonConfirmDialog.ets
└── utils/                # 工具类
    ├── eventbus/         # 事件总线
    ├── framework/        # 框架工具(Tab 切换等)
    ├── logger/           # 日志封装
    ├── router/           # 路由管理(RouterModule)
    ├── userinfo/         # 用户信息管理
    ├── window/           # 窗口工具
    ├── BreakpointSystem.ets  # 断点适配系统
    ├── ContextUtil.ets       # Context 工具
    └── ReminderUtil.ets      # 提醒工具

commons/lib_network 封装了网络层:

复制代码
commons/lib_network/src/main/ets/
├── constants/Enums.ets           # 网络相关枚举
├── https/
│   ├── apis/                     # 业务 API
│   │   ├── Asset.ets
│   │   ├── Bill.ets
│   │   └── User.ets
│   └── Request.ets               # Axios 请求封装
├── httpsmock/                    # Mock 数据
│   ├── mockapis/                 # Mock API 实现
│   └── MockAdapter.ets
└── types/responses/              # 响应类型定义
    ├── BaseResponse.ets
    └── UserResponse.ets
components 组件层(共 14 个模块)

每个组件模块遵循一致的内部结构。以 bill_base 为例:

复制代码
components/bill_base/src/main/ets/
├── commons/
│   ├── BillConstants.ets   # 常量
│   ├── Enums.ets           # 枚举(BalanceChangeType 等)
│   ├── FamilyModels.ets    # 家庭账本数据模型
│   ├── Models.ets          # 核心数据模型
│   └── Types.ets           # TypeScript 类型定义
└── utils/
    ├── BreakpointSystem.ets # 断点适配
    ├── Logger.ets           # 日志
    └── ResourceUtil.ets     # 资源工具

collect_personal_info 为例的复杂组件模块:

复制代码
components/collect_personal_info/src/main/ets/
├── common/
│   ├── ConfigModel.ets
│   ├── Constant.ets
│   ├── CustomModifier.ets
│   ├── ErrorMsgMap.ets
│   ├── FormModel.ets
│   ├── GridRowColSetting.ets
│   └── Types.ets
├── components/              # 表单子组件
│   ├── ChooseAvatar.ets
│   ├── ChooseDate.ets
│   ├── EnterPhoneNumber.ets
│   ├── EnterTextArea.ets
│   ├── EnterTextInput.ets
│   └── SelectDialog.ets
├── viewmodels/
│   └── PersonalInfoViewVM.ets
└── views/
    ├── ListCard.ets
    └── PersonalInfoView.ets
features 特性层(共 4 个模块)

home 为例------它是最核心的特性模块:

复制代码
features/home/src/main/ets/
├── commons/
│   ├── Constants.ets        # 首页常量
│   ├── Enums.ets            # 首页枚举(弹窗类型等)
│   ├── Models.ets           # 首页数据模型
│   └── Types.ets            # 类型定义
├── components/              # 首页子组件
│   ├── AccountBookSelector.ets
│   ├── MonthlyBudgetCard.ets
│   └── TypeFilterSheet.ets
├── viewmodels/              # ViewModel 层
│   ├── AccountBookVM.ets
│   ├── BillDetailVM.ets
│   └── HomeVM.ets
└── views/                   # 页面视图
    ├── AccountBookDetailPage.ets
    ├── AccountBookManagePage.ets
    ├── AccountBookPage.ets
    ├── BillDetailPage.ets
    ├── HomeView.ets
    └── ResourceManagePage.ets

assets 为例:

复制代码
features/assets/src/main/ets/
├── commons/Constants.ets
├── components/
│   ├── AssetFilterSheet.ets
│   ├── AssetLineChart.ets
│   └── BalanceTypeSelection.ets
├── viewmodels/
│   ├── AssetAnalysisVM.ets
│   ├── AssetDetailVM.ets
│   └── AssetsVM.ets
└── views/
    ├── AddAssetPage.ets
    ├── AssetAnalysisPage.ets
    ├── AssetDetailPage.ets
    └── AssetsView.ets

6. 关键配置文件速查表

配置文件 路径 核心作用 关键字段
app.json5 AppScope/app.json5 应用级元数据 bundleNameversionCodeversionName
module.json5 products/entry/src/main/module.json5 入口模块配置 abilitiesextensionAbilitiesdeviceTypesquerySchemes
build-profile.json5 ./build-profile.json5 模块注册与构建目标 modules[].srcPathproducts[].targetSdkVersion
hvigor-config.json5 hvigor/hvigor-config.json5 构建工具链参数 execution.parallelexecution.incremental
oh-package.json5 每个模块根目录 模块元数据与依赖声明 namemaindependencies
main_pages.json products/entry/src/main/resources/base/profile/main_pages.json 页面路由注册 src 数组列出所有页面路径
router_map.json 各模块 resources/base/profile/ 模块内路由映射 NavDestination 名字与页面的映射关系
form_config.json products/entry/src/main/resources/base/profile/ 服务卡片配置 卡片类型、更新周期、支持尺寸

最佳实践与注意事项

1. 统一包名与版本管理

bundleName 一旦发布即不可更改,因此要确保签名证书的 bundleName 与 app.json5 中一致。建议在项目启动阶段就确定最终的包名,并在 CI/CD 流水线中统一管理 versionCode 的自动递增逻辑。

2. Ability 配置的最小化原则

module.json5 只应包含应用模块级别的配置。Ability 内部的环境初始化逻辑应放在对应的 Ability 实现类中,而非集中写在配置文件中。例如,EntryAbility 中通过 handleWantInfo 处理外部拉起参数,通过 onWindowStageCreate 加载首页,逻辑清晰且便于测试。

3. querySchemes 的按需声明

querySchemes 数组声明的 Scheme 会影响应用市场审核。只需声明确实需要的 Scheme(如微信登录的 weixin),不需要的 Scheme 不应添加。同时要注意,querySchemes 仅允许查询,实际跳转还需要配合 openLink 等 API。

4. 资源管理的层级策略

全局共享的资源(如应用名称、图标)放在 AppScope/resources 中;模块内部独享的资源(如账单分类图标、字符串)放在各自模块的 resources 目录中。这样可以避免资源名冲突,也便于模块的独立发布和消费。

5. 构建配置的针对性调优

Debug 构建建议关闭类型检查、开启并行和增量编译以提升开发效率;Release 构建应开启所有检查项并关闭不必要的调试日志。在 hvigor-config.json5 中可根据构建模式做差异化配置。

总结

MoneyTrack 的工程全景展现了鸿蒙 Stage 模型下多模块应用的标准布局。从 AppScope 的全局配置到每个模块的自包含结构,从 module.json5 的 Ability 注册到 hvigor-config.json5 的构建优化,每个配置文件都有其明确的职责。理解这一工程结构,不仅能帮助开发者快速上手 MoneyTrack 项目,更能为构建自己的鸿蒙应用提供可复用的蓝图。核心思路是:将关注点分离原则贯彻到工程的每个角落------资源按层级归属、配置按模块自治、依赖按方向控制,这样才能构建出真正可维护、可扩展的鸿蒙应用。

参考文档

  • HarmonyOS 应用工程结构指南
  • Stage 模型开发说明
  • module.json5 配置文件参考
  • 项目源文件:d:\HarmonyOS\WorkSpace\MoneyTrack1.0.3\build-profile.json5
  • 入口配置:d:\HarmonyOS\WorkSpace\MoneyTrack1.0.3\products\entry\src\main\module.json5
  • 构建配置:d:\HarmonyOS\WorkSpace\MoneyTrack1.0.3\hvigor\hvigor-config.json5
  • 入口页面:d:\HarmonyOS\WorkSpace\MoneyTrack1.0.3\products\entry\src\main\ets\pages\MainEntry.ets
  • Ability 实现:d:\HarmonyOS\WorkSpace\MoneyTrack1.0.3\products\entry\src\main\ets\entryability\EntryAbility.ets
相关推荐
春卷同学1 小时前
HarmonyOS掌上记账APP开发实践第1篇:HarmonyOS 多模块 HAR 架构最佳实践 — MoneyTrack 多模块拆分之道
华为·架构·harmonyos
g_z_x_2 小时前
鸿蒙 ArkTS 实战:Quiz Arena 从状态建模到交互闭环完整解析
华为·交互·harmonyos
用户0934077735143 小时前
HarmonyOS WPS Open SDK 实践:最小打开闭环与 enableEdit 分支处理
typescript·harmonyos
绝世番茄3 小时前
鸿蒙原生 ArkTS 布局方式之 Stack 悬浮按钮布局(HarmonyOS NEXT / API 24)
华为·harmonyos·鸿蒙
2301_768103493 小时前
HarmonyOS趣味相机实战第5篇:CameraKit资源释放、PixelMap生命周期与预览黑屏排查
性能优化·harmonyos·arkts·camerakit·pixelmap
花开彼岸天~4 小时前
鸿蒙应用开发实战【01】— 号码助手项目概述与开发环境搭建
华为·harmonyos·鸿蒙系统
不才难以繁此生4 小时前
HarmonyOS 图片列表滑动卡顿怎么办:中式美食菜谱封面怎么做占位、缓存和失败兜底
harmonyos·image·arkui·中式美食·列表性能
心中有国也有家4 小时前
AtomGit Flutter 鸿蒙客户端:零外部资源的应用打包策略
android·javascript·flutter·华为·harmonyos
心中有国也有家5 小时前
AtomGit Flutter 鸿蒙客户端:为 E-Brufen 注入情感节律的文案设计哲学
javascript·学习·flutter·华为·harmonyos