做鸿蒙开发有一阵子了,从最开始照着官方文档写 Demo,到现在独立负责一个完整项目,中间踩了不少坑。这篇开始做一个连载,把我们团队做内容工作台项目的整个过程拆成五篇,从基础框架搭起来,一步步做到可上线。

先说一下这个项目:一个内容聚合工作台,左侧导航、中间内容流、右侧详情面板,类似那种三栏布局的工具类应用。规模不算大,但五脏俱全,正好适合用来讲 HarmonyOS 7 的实际开发流程。

工程初始化与目录结构

用 DevEco Studio 创建工程的时候,选 Empty Ability,API 版本选 HarmonyOS 7(API 12)。创建完之后,不要默认的文件结构就直接写业务代码,先把目录按模块分好,不然后面越写越乱。

我们的目录结构大概是这样的:

entry/src/main/ets/
├── pages/              # 页面入口
│   └── WorkbenchPage.ets
├── components/         # 可复用组件
│   ├── NavSideBar.ets
│   ├── ContentCard.ets
│   └── DetailPanel.ets
├── model/              # 数据模型
│   └── ContentItem.ets
├── viewmodel/          # 视图模型
│   └── WorkbenchViewModel.ets
├── utils/              # 工具类
│   └── Logger.ets
└── common/             # 公共资源和常量
    └── Constants.ets

这个结构看着普通,但实际项目中很重要。很多人一开始把所有东西都塞在 pages 目录里,写到后面一个文件几千行,改都不敢改。

三栏布局实现

内容工作台的核心是三栏布局:左侧导航、中间内容区、右侧详情。用 ArkUI 的 Row 布局就能实现,但要注意比例分配和边界处理。

下面是主页面的布局代码,文件位置在 pages/WorkbenchPage.ets

@Entry
@Component
struct WorkbenchPage {
  @State selectedNavId: number = 0;
  @State selectedContent: ContentItem | null = null;

  build() {
    Row({ space: 0 }) {
      // 左侧导航栏 - 固定宽度 200vp
      NavSideBar({
        selectedId: this.selectedNavId,
        onSelect: (id: number) => {
          this.selectedNavId = id;
        }
      })
      .width(200)
      .height('100%')
      .backgroundColor('#f5f5f7')

      // 中间内容区 - 占满剩余空间
      Column() {
        // 顶部搜索栏
        SearchBar()

        // 内容列表
        ContentList({
          onItemClick: (item: ContentItem) => {
            this.selectedContent = item;
          }
        })
      }
      .layoutWeight(1)
      .height('100%')
      .backgroundColor('#ffffff')

      // 右侧详情面板 - 固定宽度 360vp
      DetailPanel({
        content: this.selectedContent
      })
      .width(360)
      .height('100%')
      .backgroundColor('#fafafa')
    }
    .width('100%')
    .height('100%')
  }
}

这里的关键点:左侧和右侧用固定宽度,中间用 layoutWeight(1) 占满剩余空间。这样不管窗口怎么缩放,中间内容区都会自适应。

三栏布局示意图

基础组件拆分

不要把所有 UI 都写在一个 build 方法里。上面代码里的 NavSideBar、ContentList、DetailPanel 都是独立组件,各自管理自己的 UI 和逻辑。

以左侧导航栏为例,文件位置在 components/NavSideBar.ets

@Component
export struct NavSideBar {
  @Prop selectedId: number;
  onSelect: (id: number) => void = () => {};

  private navItems: string[] = ['推荐', '前端', '后端', '设计', '产品'];

  build() {
    Column({ space: 4 }) {
      Text('内容工作台')
        .fontSize(18)
        .fontWeight(FontWeight.Bold)
        .margin({ bottom: 20, top: 20 })
        .alignSelf(ItemAlign.Start)
        .padding({ left: 16, right: 16 })

      ForEach(this.navItems, (item: string, index: number) => {
        Row() {
          Text(item)
            .fontSize(15)
            .fontColor(this.selectedId === index ? '#007aff' : '#333333')
            .fontWeight(this.selectedId === index ? FontWeight.Medium : FontWeight.Normal)
        }
        .width('100%')
        .height(44)
        .padding({ left: 16, right: 16 })
        .backgroundColor(this.selectedId === index ? '#e8f0fe' : 'transparent')
        .borderRadius(8)
        .onClick(() => {
          this.onSelect(index);
        })
      }, (item: string) => item)
    }
    .width('100%')
    .height('100%')
    .padding({ left: 8, right: 8 })
  }
}

这个组件的设计思路:用 @Prop 接收当前选中项,用回调函数把点击事件传给父组件。数据和展示分离,组件本身不维护业务状态,只负责 UI 和事件转发。

项目目录结构示意图

工程配置注意事项

有几个配置点一开始很容易忽略:

1. 横竖屏锁定:工作台类应用一般只支持横屏,在 module.json5 里把 orientation 设成 landscape,不然竖屏下布局会乱。

2. 沉浸式状态栏:默认状态栏是白色背景,和应用内容区会有一条分割线。在 EntryAbility 里设置一下,让内容延伸到状态栏下面,视觉上更整体。

3. 字体大小适配:手机和折叠屏上字体大小差异很大,不要写死字号。用 vp 单位,或者用资源限定符做不同尺寸下的字体适配。

这些配置点看着小,但实际做项目的时候,一开始不处理,后面改起来很麻烦。

小结

这篇是整个系列的基础。工程结构搭好、三栏布局跑通、基础组件拆出来,后面加业务逻辑就顺畅了。下一篇会讲怎么用状态管理把这些组件串起来,实现交互联动。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐