一、技术前言

随着宠物经济在中国市场的持续爆发式增长,宠物服务类应用已从简单的信息展示工具进化为涵盖医疗、美容、寄养、社交、电商等多维度的综合服务平台。根据行业白皮书数据,2025 年中国宠物市场规模突破 8000 亿元,其中线上服务预约占比超过 35%。这一趋势对移动端应用提出了更高的要求:不仅需要在有限的屏幕空间内承载多种业务形态,还要在视觉上传递温暖、可信赖的品牌感受。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

鸿蒙 HarmonyOS 的 ArkUI 框架为这类需求提供了理想的声明式 UI 开发范式。与传统的命令式 UI 不同,ArkUI 采用 @Component + @Builder 的组件化拆分策略,通过状态驱动视图更新,开发者只需关注数据变化,框架自动完成 UI 的差异渲染。这一机制极大简化了多 Tab 页面、弹窗交互、动画效果等复杂场景的实现。

在本篇中,我们将深入剖析一个宠物服务平台的完整 ArkUI 实现方案。该应用覆盖六个核心业务场景——首页推荐、附近商家、订单管理、宠物档案、社区动态和个人中心——每个场景采用差异化的布局策略,避免视觉重复。技术上采用了以下关键决策:

  • @Observed + @State 双层状态管理:数据模型使用 @Observed 装饰器实现可观察性,组件内通过 @State 持有状态引用,数据变化自动触发局部刷新
  • 预计算常量数组替代运行时 filter:规避 ArkUI 中 filter 回调导致的不必要重渲染风险
  • 单定时器呼吸动画:全局仅一处 setInterval,通过布尔翻转驱动一个透明度动画循环,避免多重定时器的性能开销
  • Builder 函数组件化拆分:13 个 @Builder 函数将 1200+ 行的组件拆解为可维护的独立模块
  • 状态颜色映射工具函数:将业务状态(待支付/进行中/已完成等)与视觉色彩解耦,集中管理颜色策略

二、整体架构流程图

常量与工具层

数据模型层

Builder 渲染层

页面组件层

应用入口层

EntryAbility
UIAbility 生命周期

Index 组件
@Entry @Component

@State 状态变量组
14 个响应式状态

build 方法
Column 主容器

头部区域
渐变 Stack + Logo + 通知

6 Tab 内容区
差异化布局

图表卡片
柱状图统计

底部 Tab 栏
6 图标 + 选中态

弹窗系统
新增/编辑/删除 + 遮罩

PetService 服务模型

PetShop 商家模型

PetOrder 订单模型

PetProfile 档案模型

PetPost 动态模型

PetTask 任务模型

COLORS 色彩常量
18 个色值

TAB_LIST 元数据
6 Tab 定义

CHART_DATA 图表数据

工具函数
状态颜色映射

从架构图可以看出,整个应用采用四层架构:入口层负责生命周期管理,组件层负责状态持有和布局编排,Builder 层负责各区域的视觉渲染,数据层负责业务数据的封装与传递。常量和工具函数作为横切关注点,被各层按需引用。


三、色彩体系设计

3.1 ColorPalette 接口定义

应用首先定义了一个 ColorPalette 接口,作为整个色彩体系的类型契约:

interface ColorPalette {
  bg: string;           // 页面背景
  cardBg: string;       // 卡片背景
  textPrimary: string;  // 主文字
  textSecondary: string;// 次文字
  textTertiary: string; // 三级文字
  accent: string;       // 强调色
  accentLight: string;  // 浅强调色
  warm: string;         // 暖色
  warmLight: string;    // 浅暖色
  border: string;       // 边框色
  success: string;      // 成功状态
  warning: string;      // 警告状态
  danger: string;       // 危险状态
  info: string;         // 信息状态
  gradientStart: string;// 渐变起始
  gradientEnd: string;   // 渐变结束
  divider: string;      // 分割线
  overlay: string;       // 遮罩层
}

这一接口定义了 18 个色彩字段,覆盖了从背景到前景、从文字到状态、从渐变到遮罩的全场景需求。接口的作用不仅在于类型约束,更重要的是建立了色彩使用的语义化命名体系——开发者通过字段名即可理解色彩的用途,无需记忆具体的十六进制值。

3.2 COLORS 常量逐色分析

const COLORS: ColorPalette = {
  bg: '#FAF5F0',            // 暖米色背景
  cardBg: '#FFFFFF',        // 纯白卡片
  textPrimary: '#3D2E1F',   // 深棕主文字
  textSecondary: '#8B7355', // 暖灰次文字
  textTertiary: '#BFA88E',  // 浅棕三级文字
  accent: '#E67E22',        // 活力橙强调
  accentLight: '#FFE4B5',   // 浅橙背景
  warm: '#D4A574',          // 温暖棕
  warmLight: '#F5DEB3',     // 浅暖色
  border: '#E8DCC9',        // 暖灰边框
  success: '#4CAF50',        // 标准绿
  warning: '#FF9800',        // 标准橙
  danger: '#F44336',          // 标准红
  info: '#2196F3',           // 标准蓝
  gradientStart: '#E67E22',  // 渐变起点橙
  gradientEnd: '#D4A574',   // 渐变终点棕
  divider: '#F0E6D2',        // 浅暖分割线
  overlay: '#00000066'      // 半透明遮罩
};

对上述色彩进行分类解读:

背景层bg: #FAF5F0 是一个偏暖的米色调白色,比纯白 #FFFFFF 多了一份柔和感,营造宠物店的温馨氛围。cardBg 保持纯白以确保卡片内容在暖色背景上的清晰度,形成"暖底白卡"的层次对比。

文字层:三级文字色从 #3D2E1F(深棕)到 #8B7355(暖灰)再到 #BFA88E(浅棕),全部采用棕色系而非纯黑纯灰,与整体暖色调保持一致。这种设计避免了文字色与环境色的割裂感。

强调色accent: #E67E22 是品牌核心色——活力橙,用于按钮、图标、选中态等关键交互元素。accentLight: #FFE4B5 作为强调色的浅色变体,用于背景填充和标签底色。

渐变系gradientStartgradientEnd 从橙色过渡到棕色,用于头部区域的线性渐变,模拟日落色调,呼应"回家"的情感隐喻。

状态色:成功、警告、危险、信息四个状态色采用业界通用的绿/橙/红/蓝标准色,确保用户在跨应用使用时的认知一致性,不因品牌色而牺牲可辨识度。


四、Tab 元数据与辅助数据

4.1 Tab 元数据定义

const TAB_LIST = [
  { icon: $r('app.media.ic_public_0'), label: '首页' },
  { icon: $r('app.media.ic_public_1'), label: '附近' },
  { icon: $r('app.media.ic_public_2'), label: '订单' },
  { icon: $r('app.media.ic_public_3'), label: '档案' },
  { icon: $r('app.media.ic_public_4'), label: '社区' },
  { icon: $r('app.media.ic_public_5'), label: '我的' }
] as const;

TAB_LIST 使用 as const 断言确保类型为只读元组,防止运行时意外修改。每个 Tab 包含图标资源和标签文字两个字段。六个 Tab 覆盖了宠物服务的完整用户路径:从浏览推荐(首页)到线下消费(附近),从交易记录(订单)到信息管理(档案),从社交互动(社区)到个人中心(我的)。

4.2 图表数据

const CHART_DATA = [
  { label: '1月', value: 3200 },
  { label: '2月', value: 2800 },
  { label: '3月', value: 4500 },
  { label: '4月', value: 3800 },
  { label: '5月', value: 5200 },
  { label: '6月', value: 4800 }
] as const;

图表数据模拟了半年的消费统计,数值在 2800~5200 区间波动,符合宠物消费的季节性特征——5 月达到峰值(可能对应春季宠物美容高峰),2 月为低谷(春节后消费紧缩)。数据使用 as const 冻结为只读,确保渲染过程中不被篡改。

4.3 分类标签

const CATEGORY_LABELS = ['全部', '医疗', '美容', '寄养', '训练', '社交'] as const;

六个分类标签覆盖了宠物服务的主要业务类别,"全部"作为默认筛选态放在首位。这些标签在社区 Tab 中用于内容分类过滤。


五、工具函数

5.1 订单状态颜色映射

function orderStatusColor(status: string): string {
  const map: Record<string, string> = {
    '待支付': COLORS.warning,
    '进行中': COLORS.info,
    '已完成': COLORS.success,
    '已取消': COLORS.danger
  };
  return map[status] ?? COLORS.textTertiary;
  }

该函数实现了订单状态到视觉颜色的映射策略。使用 Record<string, string> 类型定义确保映射表的类型安全,四个状态分别对应警告(橙)、信息(蓝)、成功(绿)、危险(红)四色。?? COLORS.textTertiary 作为空值合并兜底,当传入未定义的状态时回退到三级文字色,避免颜色为 undefined 导致渲染异常。

5.2 任务颜色映射

function taskColor(priority: string): string {
  const map: Record<string, string> = {
    '高': COLORS.danger,
    '中': COLORS.warning,
    '低': COLORS.success
  };
  return map[priority] ?? COLORS.textSecondary;
}

任务优先级颜色映射采用同样的策略,高/中/低分别映射为红/橙/绿,符合用户对优先级的直觉认知。兜底色为次文字色 #8B7355,在视觉上弱于三种状态色但不会消失。

两个工具函数的核心设计思想是将颜色策略与业务逻辑解耦。如果未来需要调整颜色方案(例如暗色模式适配),只需修改 COLORS 常量或映射表,无需改动各 Builder 函数中的颜色引用。


六、数据模型层

应用定义了 6 个 @Observed 数据模型类,每个类对应一个核心业务实体。

6.1 PetService 服务模型

@Observed
class PetService {
  title: string = '';
  desc: string = '';
  price: number = 0;
  icon: string = '🐾';
  tag: string = '';
  rating: number = 0;
}

PetService 描述宠物服务项目(如体检、洗浴等),包含标题、描述、价格、图标 emoji、标签和评分六个字段。@Observed 装饰器使该类的实例在被 @State 引用时自动具备观察性——任何属性变化都会触发引用了该实例的 UI 组件重新渲染。icon 字段使用 emoji 字符串而非图片资源,是一种轻量化的图标方案,适合快速原型和内容频繁变化的场景。

6.2 PetShop 商家模型

@Observed
class PetShop {
  name: string = '';
  type: string = '';
  distance: string = '';
  rating: number = 0;
  tags: string[] = [];
}

PetShop 描述周边商家信息,tags 字段为字符串数组,用于存储商家的特色标签(如"24小时"、"免费停车"等)。数组类型在 ArkUI 中可以配合 ForEach 直接渲染列表项。

6.3 PetOrder 订单模型

@Observed
class PetOrder {
  service: string = '';
  shop: string = '';
  date: string = '';
  price: number = 0;
  status: string = '';
}

PetOrder 记录用户的消费订单,status 字段的值(待支付/进行中/已完成/已取消)通过前述 orderStatusColor 函数映射为视觉颜色,实现数据与展示的分离。

6.4 PetProfile 档案模型

@Observed
class PetProfile {
  name: string = '';
  breed: string = '';
  age: string = '';
  weight: string = '';
  vaccine: string = '';
  lastCheck: string = '';
}

PetProfile 存储宠物的基本档案信息,包括名字、品种、年龄、体重、疫苗记录和上次体检日期。六个字段覆盖了宠物健康管理的核心信息维度。

6.5 PetPost 社区动态模型

@Observed
class PetPost {
  author: string = '';
  avatar: string = '';
  content: string = '';
  likes: number = 0;
  comments: number = 0;
  category: string = '';
}

PetPost 描述社区互动内容,包含作者信息、内容正文、点赞数、评论数和分类标签。avatar 使用 emoji 字符串,与 PetService 的 icon 设计保持一致。

6.6 PetTask 任务模型

@Observed
class PetTask {
  title: string = '';
  time: string = '';
  priority: string = '';
  done: boolean = false;
}

PetTask 是个人中心的待办任务项,done 布尔字段控制任务完成状态,priority 字段通过 taskColor 函数映射为颜色。

所有 6 个模型类均使用 @Observed 装饰器,字段均提供默认值(= ''= 0),确保构造函数可以无参实例化。这一设计简化了 Mock 数据的创建——先 new PetService() 再逐属性赋值,避免构造函数参数列表过长。


七、组件主体结构

7.1 @State 状态变量组

@Entry
@Component
struct Index {
  @State currentTab: number = 0;
  @State services: PetService[] = [/* 6 条 Mock 数据 */];
  @State shops: PetShop[] = [/* 6 条 Mock 数据 */];
  @State orders: PetOrder[] = [/* 6 条 Mock 数据 */];
  @State profiles: PetProfile[] = [/* 3 条 Mock 数据 */];
  @State posts: PetPost[] = [/* 6 条 Mock 数据 */];
  @State tasks: PetTask[] = [/* 6 条 Mock 数据 */];
  @State showModal: boolean = false;
  @State modalType: string = 'add';
  @State editTarget: PetService | PetShop | PetOrder | PetProfile | PetPost | PetTask | null = null;
  @State breathFlag: boolean = false;
  @State scrollY: number = 0;
  @State selectedCategory: number = 0;
  @State activeTabLabel: string = '首页';
}

组件持有 14 个 @State 变量,可分为四类:

页面导航状态currentTab(当前 Tab 索引)、activeTabLabel(当前 Tab 名称)、selectedCategory(社区分类选中索引),控制页面导航和筛选状态。

业务数据集合:六个 @Observed 类的数组,分别对应六个 Tab 的数据源。数组引用变化时(如增删元素),ArkUI 自动触发对应区域的重新渲染。

弹窗交互状态showModal(弹窗显隐)、modalType(弹窗类型:add/edit/delete)、editTarget(编辑目标对象,使用联合类型支持所有模型),三个变量协作控制弹窗系统的行为。

动画与滚动状态breathFlag(呼吸动画布尔翻转标志)、scrollY(滚动位置),前者驱动呼吸动画循环,后者记录内容区滚动偏移。

7.2 呼吸动画生命周期

aboutToAppear() {
  setInterval(() => {
    this.breathFlag = !this.breathFlag;
  }, 1800);
  }

组件生命周期 aboutToAppear 在组件创建后、渲染前触发。此处注册一个间隔 1800 毫秒的定时器,每次执行时翻转 breathFlag 布尔值。由于 breathFlag@State 变量,其值变化会自动触发引用了该变量的 UI 区域重新渲染——即头部 Logo 的透明度动画。1800 毫秒的间隔恰好匹配一次"呼吸"周期(吸气 + 呼气),营造出 Logo 轻微脉动的视觉效果。

这种设计的优势在于单定时器驱动单状态,整个应用只有一处 setInterval,避免了多个定时器叠加导致的性能开销和内存泄漏风险。

7.3 build() 主布局

build() {
  Column() {
    this.headerHome()
    Scroll() {
      Column() {
        if (this.currentTab === 0) {
          this.tabHome()
        } else if (this.currentTab === 1) {
          this.tabNearby()
        } else if (this.currentTab === 2) {
          this.tabOrders()
        } else if (this.currentTab === 3) {
          this.tabProfile()
        } else if (this.currentTab === 4) {
          this.tabCommunity()
        } else if (this.currentTab === 5) {
          this.tabMine()
        }
      }
    }
    .scrollBar(BarState.Off)
    .layoutWeight(1)

    this.tabBar()
    this.modalOverlay()
  }
  .backgroundColor(COLORS.bg)
  .height('100%')
}

build() 方法是组件的渲染入口,采用 Column 作为根容器,自上而下排列三个区域:headerHome()(固定头部)、Scroll + Column(可滚动内容区)、tabBar()(固定底部栏),外加 modalOverlay()(弹窗遮罩层,默认隐藏)。

内容区使用 if-else if 条件渲染根据 currentTab 值切换六个 Tab 的 Builder 函数。ArkUI 的条件渲染会创建/销毁组件实例,确保同一时刻只有当前 Tab 的组件存在于渲染树中,节省内存。

.scrollBar(BarState.Off) 隐藏滚动条,.layoutWeight(1) 让内容区占据头部和底部栏之间的全部剩余空间。根容器设置 .height('100%') 确保填满屏幕。


八、头部区域详解

@Builder
headerHome() {
  Stack({ alignContent: Alignment.Center }) {
    // 渐变背景层
    Column()
      .width('100%')
      .height(56)
      .linearGradient({
        direction: GradientDirection.Right,
        colors: [[COLORS.gradientStart, 0], [COLORS.gradientEnd, 1]]
      })
    
    // 内容层:Logo + 标题 + 通知按钮
    Row() {
      Text('🐾')
        .fontSize(24)
        .opacity(this.breathFlag ? 0.7 : 1.0)
        .animation({
          duration: 1500,
          curve: Curve.EaseInOut
        })
      
      Text('萌宠家')
        .fontSize(20)
        .fontWeight(FontWeight.Bold)
        .fontColor('#FFFFFF')
        .margin({ left: 8 })
      
      Blank()
      
      Text('🔔')
        .fontSize(20)
    }
    .width('90%')
    .height(56)
    .justifyContent(FlexAlign.Start)
  }
  .width('100%')
  .height(56)
}

头部区域使用 Stack 叠加布局,底层是 56vp 高的渐变色背景,上层是水平排列的内容行。

渐变背景:使用 linearGradient 从左到右(GradientDirection.Right)渲染,起点色 #E67E22(活力橙)到终点色 #D4A574(温暖棕),模拟日落的色彩过渡。0 到 1 的颜色锚点确保渐变从最左到最右完整覆盖。

Logo 呼吸动画:🐾 emoji 的 opacity 绑定到 breathFlag 状态——当 breathFlag 为 true 时透明度降至 0.7,为 false 时恢复 1.0。配合 .animation({ duration: 1500, curve: Curve.EaseInOut }),ArkUI 会在 1500 毫秒内以缓入缓出曲线完成透明度过渡。由于定时器每 1800 毫秒翻转一次,实际效果是 Logo 在 1.5 秒内缓慢变暗,然后在下一个 1.5 秒缓慢变亮,形成"呼吸"节奏。

Blank() 组件占据中间所有剩余空间,将通知按钮 🔔 推到右侧。Row 宽度设为 90%,在 Stack 居中后左右各留 5% 边距。


九、首页 Tab 分析(tabHome)

首页采用双列卡片网格布局,展示推荐服务。

@Builder
tabHome() {
  Column() {
    // 搜索框
    Row() {
      Text('🔍')
        .fontSize(16)
      Text('搜索宠物服务...')
        .fontSize(14)
        .fontColor(COLORS.textTertiary)
        .margin({ left: 8 })
      Blank()
      Text('📷')
        .fontSize(16)
    }
    .width('92%')
    .height(40)
    .backgroundColor(COLORS.cardBg)
    .borderRadius(20)
    .padding({ left: 16, right: 16 })
    .margin({ top: 12, bottom: 12 })
    
    // 双列卡片网格
    Row() {
      ForEach(this.services.slice(0, 4), (s: PetService) => {
        Column() {
          Text(s.icon).fontSize(32)
          Text(s.title)
            .fontSize(14)
            .fontWeight(FontWeight.Medium)
            .margin({ top: 8 })
          Text(s.desc)
            .fontSize(12)
            .fontColor(COLORS.textSecondary)
            .maxLines(1)
            .textOverflow({ overflow: TextOverflow.Ellipsis })
            .margin({ top: 4 })
          Text(`¥${s.price}`)
            .fontSize(16)
            .fontColor(COLORS.accent)
            .fontWeight(FontWeight.Bold)
            .margin({ top: 8 })
        }
        .layoutWeight(1)
        .backgroundColor(COLORS.cardBg)
        .borderRadius(12)
        .padding(12)
        .margin({ right: 4, bottom: 8 })
      }, (s: PetService) => s.title)
    }
    .width('92%')
    
    // 下方两个卡片
    Row() {
      ForEach(this.services.slice(4, 6), (s: PetService) => {
        // ... 同上卡片结构
      })
    }
    .width('92%')
    .margin({ bottom: 16 })
  }
  .width('100%')
}

首页的核心布局策略是 2×3 卡片网格,通过两次 Row + ForEach 实现,每次取 services 数组的 2 个元素。每个卡片使用 Column 垂直排列 emoji 图标、标题、描述和价格四层信息。

.slice(0, 4).slice(4, 6) 将 6 条数据分为两批渲染,而非使用一个 ForEach 渲染 6 个卡片。这种"分批 ForEach"策略确保了两行卡片的布局精确性——每行严格 2 个卡片,各占 layoutWeight(1) 等分宽度。

文本溢出处理是首页的重要细节:.maxLines(1) 限制描述文字为单行,.textOverflow({ overflow: TextOverflow.Ellipsis }) 在溢出时显示省略号。这一组合确保了卡片高度在内容长短不一时保持一致,避免因某个描述过长导致整行卡片高度不齐。


十、附近 Tab 分析(tabNearby)

附近 Tab 采用列表卡片布局,每张卡片包含商家名称、类型、距离、评分和标签。

@Builder
tabNearby() {
  Column() {
    ForEach(this.shops, (shop: PetShop) => {
      Row() {
        // 左侧:商家图标占位
        Column() {
          Text('🏪').fontSize(28)
        }
        .width(56)
        .height(56)
        .backgroundColor(COLORS.warmLight)
        .borderRadius(12)
        .justifyContent(FlexAlign.Center)
        
        // 右侧:信息区
        Column() {
          Row() {
            Text(shop.name)
              .fontSize(15)
              .fontWeight(FontWeight.Medium)
            Blank()
            Text(shop.distance)
              .fontSize(12)
              .fontColor(COLORS.accent)
          }
          .width('100%')
          
          Row() {
            Text(shop.type)
              .fontSize(12)
              .fontColor(COLORS.textSecondary)
            Text('⭐'.repeat(Math.ceil(shop.rating)))
              .fontSize(10)
              .margin({ left: 8 })
            Blank()
            Text(`${shop.rating}`)
              .fontSize(12)
              .fontColor(COLORS.warning)
          }
          .width('100%')
          .margin({ top: 4 })
          
          // 标签行
          Row() {
            ForEach(shop.tags, (tag: string) => {
              Text(tag)
                .fontSize(10)
                .fontColor(COLORS.warm)
                .backgroundColor(COLORS.warmLight)
                .borderRadius(4)
                .padding({ left: 6, right: 6, top: 2, bottom: 2 })
                .margin({ right: 6 })
            }, (tag: string) => tag)
          }
          .margin({ top: 6 })
        }
        .layoutWeight(1)
        .margin({ left: 12 })
        .alignItems(VerticalAlign.Top)
      }
      .width('92%')
      .backgroundColor(COLORS.cardBg)
      .borderRadius(12)
      .padding(12)
      .margin({ bottom: 8 })
    }, (shop: PetShop) => shop.name)
  }
  .width('100%')
  .padding({ top: 12 })
}

与首页的双列网格不同,附近 Tab 采用水平分栏卡片布局:左侧 56vp 的圆角图标区(暖色背景 + emoji),右侧 layoutWeight(1) 的信息区。信息区内部分为三行——名称行(含距离)、类型评分行(含星级和评分数字)、标签行。

星级渲染使用 '⭐'.repeat(Math.ceil(shop.rating)),将评分数字转换为对应数量的 emoji 星星。Math.ceil 确保评分为 4.5 时显示 5 颗星,是一种向上取整的评分展示策略。

标签行使用嵌套的 ForEach 遍历 shop.tags 数组,每个标签用浅暖色背景 + 暖色文字渲染,形成"药丸"形态的标签。

.alignItems(VerticalAlign.Top) 确保右侧信息区顶部对齐左侧图标区,避免因信息行数不同导致内容垂直居中时的错位。


十一、订单 Tab 分析(tabOrders)

订单 Tab 采用时间轴样式布局,通过左侧的状态色条和连线模拟时间轴效果。

@Builder
tabOrders() {
  Column() {
    ForEach(this.orders, (order: PetOrder, idx: number) => {
      Row() {
        // 左侧色条 + 时间轴效果
        Column() {
          // 顶部连线(第一个订单不显示)
          if (idx > 0) {
            Line()
              .width(2)
              .height(20)
              .color(COLORS.divider)
          }
          // 状态圆点
          Circle({ width: 10, height: 10 })
            .fill(orderStatusColor(order.status))
          // 底部连线(最后一个订单不显示)
          if (idx < this.orders.length - 1) {
            Line()
              .width(2)
              .height(40)
              .color(COLORS.divider)
          }
        }
        .width(24)
        .alignItems(HorizontalAlign.Center)
        
        // 右侧订单卡片
        Column() {
          Row() {
            Text(order.service)
              .fontSize(14)
              .fontWeight(FontWeight.Medium)
            Blank()
            Text(`¥${order.price}`)
              .fontSize(14)
              .fontColor(COLORS.accent)
              .fontWeight(FontWeight.Bold)
          }
          
          Row() {
            Text(order.shop)
              .fontSize(12)
              .fontColor(COLORS.textSecondary)
            Blank()
            Text(order.date)
              .fontSize(11)
              .fontColor(COLORS.textTertiary)
          }
          .margin({ top: 4 })
          
          Row() {
            Text(order.status)
              .fontSize(11)
              .fontColor(orderStatusColor(order.status))
              .backgroundColor(orderStatusColor(order.status).replace('#', '#').slice(0, 7) + '15')
              .borderRadius(4)
              .padding({ left: 8, right: 8, top: 2, bottom: 2 })
          }
          .margin({ top: 6 })
        }
        .layoutWeight(1)
        .backgroundColor(COLORS.cardBg)
        .borderRadius(12)
        .padding(12)
        .margin({ left: 8 })
      }
      .width('92%')
      .margin({ bottom: 4 })
    }, (order: PetOrder) => `${order.service}_${idx}`)
  }
  .width('100%')
  .padding({ top: 12 })
}

订单 Tab 的核心视觉创新是左侧时间轴。每个订单项的左侧 Column 包含三部分:顶部连线(Line 组件,2vp 宽,20vp 高,浅色)、状态圆点(Circle 组件,10vp 直径,填充状态颜色)、底部连线(40vp 高的 Line)。首项不显示顶部连线,末项不显示底部连线,形成连贯的时间轴视觉效果。

状态圆点颜色由 orderStatusColor 函数动态计算——待支付为橙色、进行中为蓝色、已完成为绿色、已取消为红色,用户一眼即可区分订单状态分布。

状态标签的背景色使用了巧妙的颜色操作:orderStatusColor(order.status) 返回的十六进制色值拼接 '15' 后缀,形成低透明度的同色背景。例如 #FF9800 + 15 = #FF980015,即 8% 透明度的橙色背景。这种设计确保标签背景与文字颜色同源,视觉上自然协调。

ForEach 的 key 使用了 `${order.service}_${idx}` 模板字符串,将服务名与索引组合作为唯一键。这是因为可能存在同名服务(如两次洗浴),纯名称作 key 会冲突,加索引保证唯一性。


十二、档案 Tab 分析(tabProfile)

档案 Tab 展示宠物基本信息,采用大卡片布局。

@Builder
tabProfile() {
  Column() {
    ForEach(this.profiles, (p: PetProfile) => {
      Column() {
        // 头部:头像 + 名字 + 品种
        Row() {
          Column() {
            Text('🐕').fontSize(40)
          }
          .width(64)
          .height(64)
          .backgroundColor(COLORS.warmLight)
          .borderRadius(32)
          .justifyContent(FlexAlign.Center)
          
          Column() {
            Text(p.name)
              .fontSize(16)
              .fontWeight(FontWeight.Bold)
            Text(p.breed)
              .fontSize(12)
              .fontColor(COLORS.textSecondary)
              .margin({ top: 2 })
          }
          .margin({ left: 12 })
          .alignItems(VerticalAlign.Top)
          
          Blank()
          
          Text('编辑')
            .fontSize(12)
            .fontColor(COLORS.accent)
            .onClick(() => {
              this.modalType = 'edit';
              this.editTarget = p;
              this.showModal = true;
            })
        }
        
        // 分割线
        Divider()
          .color(COLORS.divider)
          .margin({ top: 12, bottom: 12 })
        
        // 信息网格:年龄 / 体重 / 疫苗 / 上次体检
        Row() {
          Column() {
            Text('年龄')
              .fontSize(11)
              .fontColor(COLORS.textTertiary)
            Text(p.age)
              .fontSize(14)
              .fontWeight(FontWeight.Medium)
              .margin({ top: 4 })
          }
          .layoutWeight(1)
          .alignItems(HorizontalAlign.Center)
          
          Column() {
            Text('体重')
              .fontSize(11)
              .fontColor(COLORS.textTertiary)
            Text(p.weight)
              .fontSize(14)
              .fontWeight(FontWeight.Medium)
              .margin({ top: 4 })
          }
          .layoutWeight(1)
          .alignItems(HorizontalAlign.Center)
          
          Column() {
            Text('疫苗')
              .fontSize(11)
              .fontColor(COLORS.textTertiary)
            Text(p.vaccine)
              .fontSize(14)
              .fontWeight(FontWeight.Medium)
              .margin({ top: 4 })
          }
          .layoutWeight(1)
          .alignItems(HorizontalAlign.Center)
          
          Column() {
            Text('上次体检')
              .fontSize(11)
              .fontColor(COLORS.textTertiary)
            Text(p.lastCheck)
              .fontSize(14)
              .fontWeight(FontWeight.Medium)
              .margin({ top: 4 })
          }
          .layoutWeight(1)
          .alignItems(HorizontalAlign.Center)
        }
      }
      .width('92%')
      .backgroundColor(COLORS.cardBg)
      .borderRadius(16)
      .padding(16)
      .margin({ bottom: 12 })
    }, (p: PetProfile) => p.name)
  }
  .width('100%')
  .padding({ top: 12 })
}

档案 Tab 的布局特色在于四宫格信息区。在头像和名字的头部行之后,通过 Divider 组件绘制水平分割线,然后使用 Row + 4 个等宽 Column(各 layoutWeight(1))构建四宫格信息区:年龄、体重、疫苗、上次体检。每个格子上下排列标签(11vp 浅棕色)和数值(14vp 中等粗细),居中对齐。

"编辑"按钮的点击事件是一个典型的状态驱动弹窗交互:设置 modalType'edit'editTarget 为当前 PetProfile 实例,showModaltrue。三个 @State 变量的变化会同步触发弹窗组件的显示和内容填充,无需命令式的 DOM 操作。

头像区域使用 64vp × 64vp 的圆形背景(borderRadius(32) 实现圆形),填充暖色,内置 emoji 宠物图标。justifyContent(FlexAlign.Center) 确保 emoji 在圆形容器内居中。


十三、社区 Tab 分析(tabCommunity)

社区 Tab 包含分类筛选条和信息流列表。

@Builder
tabCommunity() {
  Column() {
    // 分类筛选条
    Scroll() {
      Row() {
        ForEach(CATEGORY_LABELS, (label: string, idx: number) => {
          Text(label)
            .fontSize(13)
            .fontColor(this.selectedCategory === idx ? '#FFFFFF' : COLORS.textSecondary)
            .backgroundColor(this.selectedCategory === idx ? COLORS.accent : COLORS.accentLight)
            .borderRadius(16)
            .padding({ left: 12, right: 12, top: 6, bottom: 6 })
            .margin({ right: 8 })
            .onClick(() => {
              this.selectedCategory = idx;
            })
        }, (label: string) => label)
      }
      .padding({ left: 12, right: 12 })
    }
    .scrollable(ScrollDirection.Horizontal)
    .scrollBar(BarState.Off)
    .width('100%')
    .height(44)
    
    // 动态列表
    ForEach(this.posts, (post: PetPost) => {
      Column() {
        // 作者行
        Row() {
          Text(post.avatar).fontSize(28)
          Column() {
            Text(post.author)
              .fontSize(13)
              .fontWeight(FontWeight.Medium)
            Text(post.category)
              .fontSize(11)
              .fontColor(COLORS.textTertiary)
              .margin({ top: 2 })
          }
          .margin({ left: 8 })
          .alignItems(VerticalAlign.Top)
          Blank()
          Text('关注')
            .fontSize(11)
            .fontColor(COLORS.accent)
        }
        .width('100%')
        
        // 内容
        Text(post.content)
          .fontSize(14)
          .fontColor(COLORS.textPrimary)
          .margin({ top: 8 })
          .maxLines(2)
          .textOverflow({ overflow: TextOverflow.Ellipsis })
        
        // 互动行
        Row() {
          Text(`❤️ ${post.likes}`).fontSize(12).fontColor(COLORS.textSecondary)
          Text(`💬 ${post.comments}`).fontSize(12).fontColor(COLORS.textSecondary).margin({ left: 16 })
          Blank()
          Text('🗑️').fontSize(12)
            .onClick(() => {
              this.modalType = 'delete';
              this.editTarget = post;
              this.showModal = true;
            })
        }
        .width('100%')
        .margin({ top: 8 })
      }
      .width('92%')
      .backgroundColor(COLORS.cardBg)
      .borderRadius(12)
      .padding(12)
      .margin({ bottom: 8 })
    }, (post: PetPost) => post.content)
  }
  .width('100%')
}

社区 Tab 的核心交互是水平滚动的分类筛选条。分类标签横向排列在 Scroll 容器中(scrollable(ScrollDirection.Horizontal)),当前选中项使用橙底白字,未选中项使用浅橙底暖灰字,形成鲜明的选中态对比。点击分类时仅更新 selectedCategory 索引值,ArkUI 自动重新渲染筛选条样式。

信息流卡片包含三层信息:作者行(emoji 头像 + 名字 + 分类 + 关注按钮)、内容正文(限制 2 行 + 省略号)、互动行(点赞数 + 评论数 + 删除按钮)。删除按钮通过 .onClick 设置弹窗交互,与档案 Tab 的编辑按钮使用相同的状态驱动模式。

分类筛选条的宽度设为 44vp 固定高度,内部 Row 的 padding 确保标签不贴边。.scrollBar(BarState.Off) 隐藏水平滚动条,保持视觉简洁。


十四、我的 Tab 分析(tabMine)

我的 Tab 是结构最复杂的页面,分为三大区块:用户卡片、消费统计图表和任务清单。

@Builder
tabMine() {
  Column() {
    // 区块一:用户信息卡片
    Row() {
      Column() {
        Text('👤').fontSize(36)
      }
      .width(72)
      .height(72)
      .backgroundColor(COLORS.accentLight)
      .borderRadius(36)
      .justifyContent(FlexAlign.Center)
      
      Column() {
        Text('石天雄')
          .fontSize(18)
          .fontWeight(FontWeight.Bold)
        Text('已陪伴 3 只萌宠 892 天')
          .fontSize(12)
          .fontColor(COLORS.textSecondary)
          .margin({ top: 4 })
      }
      .margin({ left: 12 })
      .alignItems(VerticalAlign.Top)
      Blank()
      Text('⚙️').fontSize(20)
    }
    .width('92%')
    .backgroundColor(COLORS.cardBg)
    .borderRadius(16)
    .padding(16)
    .margin({ bottom: 12 })
    
    // 区块二:消费统计图表
    this.chartCard()
    
    // 区块三:任务清单
    Column() {
      Text('今日任务')
        .fontSize(15)
        .fontWeight(FontWeight.Bold)
        .margin({ bottom: 8 })
      
      ForEach(this.tasks, (task: PetTask) => {
        Row() {
          // 左侧色条(优先级颜色)
          Column()
            .width(4)
            .height(40)
            .backgroundColor(taskColor(task.priority))
            .borderRadius(2)
          
          // 任务信息
          Column() {
            Text(task.title)
              .fontSize(13)
              .fontWeight(FontWeight.Medium)
              .decoration({
                type: task.done ? TextDecorationType.LineThrough : TextDecorationType.None,
                color: COLORS.textTertiary
              })
            Text(task.time)
              .fontSize(11)
              .fontColor(COLORS.textTertiary)
              .margin({ top: 2 })
          }
          .margin({ left: 8 })
          
          Blank()
          
          // 完成状态切换
          Text(task.done ? '✅' : '⬜')
            .fontSize(16)
            .onClick(() => {
              task.done = !task.done;
            })
        }
        .width('100%')
        .backgroundColor(COLORS.cardBg)
        .borderRadius(8)
        .padding(8)
        .margin({ bottom: 6 })
      }, (task: PetTask, idx: number) => `${task.title}_${idx}`)
    }
    .width('92%')
    .backgroundColor(COLORS.cardBg)
    .borderRadius(16)
    .padding(16)
    .margin({ bottom: 16 })
  }
  .width('100%')
  .padding({ top: 12 })
}

用户信息卡片使用 72vp 圆形头像(borderRadius(36))+ 右侧两行文字(名字 + 陪伴标语)+ 右侧设置按钮的三段式布局,与档案 Tab 的宠物信息卡片风格统一但尺寸更大,突出"我的"页面的个人中心属性。

任务清单的设计亮点在于左侧 4vp 宽色条,颜色由 taskColor 函数根据优先级动态计算——高优先级红色、中优先级橙色、低优先级绿色。色条高度 40vp 与任务卡片高度匹配,borderRadius(2) 赋予轻微圆角。

完成任务的核心交互是 onClick 中直接翻转 task.done 布尔值。由于 PetTask@Observed 类,@State tasks 数组中的元素属性变化会触发引用该元素的 UI 重新渲染——TextDecorationType.LineThrough(删除线)会自动出现/消失,emoji 从 ⬜ 变为 ✅,无需手动更新 UI。


十五、图表卡片分析(chartCard)

@Builder
chartCard() {
  Column() {
    Row() {
      Text('消费统计')
        .fontSize(15)
        .fontWeight(FontWeight.Bold)
      Blank()
      Text('近6个月')
        .fontSize(11)
        .fontColor(COLORS.textTertiary)
    }
    .width('100%')
    .margin({ bottom: 12 })
    
    // 柱状图
    Row() {
      ForEach(CHART_DATA, (item, idx: number) => {
        Column() {
          // 柱子
          Column()
            .width(20)
            .height(item.value / 100)
            .backgroundColor(idx === CHART_DATA.length - 1 ? COLORS.accent : COLORS.warm)
            .borderRadius(4)
          
          // 月份标签
          Text(item.label)
            .fontSize(10)
            .fontColor(COLORS.textTertiary)
            .margin({ top: 4 })
        }
        .layoutWeight(1)
        .alignItems(HorizontalAlign.Center)
      }, (item, idx: number) => `${item.label}_${idx}`)
    }
    .width('100%')
    .height(80)
    .alignItems(VerticalAlign.Bottom)
  }
  .width('92%')
  .backgroundColor(COLORS.cardBg)
  .borderRadius(16)
  .padding(16)
  .margin({ bottom: 12 })
}

图表卡片实现了一个纯 UI 柱状图,无需引入图表库。核心原理是:每个柱子使用 Column 组件,高度设为 item.value / 100(如 5200 / 100 = 52vp),通过数值到高度的线性映射实现可视化。柱子宽度固定 20vp,圆角 4vp。

颜色策略上,最新月份(idx === CHART_DATA.length - 1)使用强调色 COLORS.accent(橙色),其余月份使用暖色 COLORS.warm(棕色),引导用户关注最新数据。

外层 Row 设置 .alignItems(VerticalAlign.Bottom) 确保所有柱子底部对齐,月份标签在柱子下方。每个 Column 使用 layoutWeight(1) 等分宽度,6 根柱子均匀分布。

这种柱状图方案的优势在于零依赖、高可控,缺点是不支持交互(如点击查看详情)和动画。在数据量小(6 个数据点)的场景下完全够用。


十六、底部 Tab 栏分析(tabBar)

@Builder
tabBar() {
  Row() {
    ForEach(TAB_LIST, (tab, idx: number) => {
      Column() {
        Text(tab.icon)
          .fontSize(22)
          .opacity(this.currentTab === idx ? 1.0 : 0.5)
        Text(tab.label)
          .fontSize(10)
          .fontColor(this.currentTab === idx ? COLORS.accent : COLORS.textTertiary)
          .margin({ top: 2 })
      }
      .layoutWeight(1)
      .alignItems(HorizontalAlign.Center)
      .onClick(() => {
        this.currentTab = idx;
        this.activeTabLabel = tab.label;
      })
    }, (tab, idx: number) => `${tab.label}_${idx}`)
  }
  .width('100%')
  .height(56)
  .backgroundColor(COLORS.cardBg)
  .border({ width: { top: 1 }, color: COLORS.divider })
}

底部 Tab 栏使用 Row + 6 个等宽 Column(各 layoutWeight(1)),每项包含 emoji 图标和标签文字。选中态通过两个维度的视觉变化体现:图标 opacity 从 0.5 提升至 1.0,标签文字颜色从三级文字色变为强调色。这种双重反馈确保用户即使在户外强光下也能清晰辨识当前 Tab。

顶部边框使用 .border({ width: { top: 1 }, color: COLORS.divider }) 仅渲染上边框,在 Tab 栏与内容区之间形成细腻的分界线。

点击事件仅需两行代码——设置 currentTab 索引和 activeTabLabel 名称,ArkUI 自动完成内容区切换、选中态样式更新、滚动位置重置等全部 UI 操作,充分体现了声明式 UI 的简洁性。


十七、弹窗系统分析

弹窗系统由遮罩层和三个弹窗 Builder 组成,通过 showModalmodalTypeeditTarget 三个状态变量协作控制。

17.1 遮罩层

@Builder
modalOverlay() {
  if (this.showModal) {
    Stack({ alignContent: Alignment.Center }) {
      // 半透明遮罩
      Column()
        .width('100%')
        .height('100%')
        .backgroundColor(COLORS.overlay)
        .onClick(() => {
          this.showModal = false;
        })
      
      // 弹窗内容
      if (this.modalType === 'add') {
        this.modalAdd()
      } else if (this.modalType === 'edit') {
        this.modalEdit()
      } else if (this.modalType === 'delete') {
        this.modalDelete()
      }
    }
    .width('100%')
    .height('100%')
  }
}

遮罩层使用 Stack 叠加布局,底层是 #00000066(40% 透明黑色)的全屏遮罩,上层根据 modalType 值条件渲染三种弹窗。点击遮罩区域关闭弹窗——这是移动端标准的交互模式。

if (this.showModal) 条件确保弹窗关闭时组件从渲染树中移除,而非仅隐藏,节省内存。

17.2 新增弹窗(modalAdd)

@Builder
modalAdd() {
  Column() {
    Text('新增服务')
      .fontSize(16)
      .fontWeight(FontWeight.Bold)
      .margin({ bottom: 16 })
    
    TextInput({ placeholder: '服务名称' })
      .width('100%')
      .height(40)
      .borderRadius(8)
      .border({ width: 1, color: COLORS.border })
    
    // 更多输入框...
    
    Row() {
      Text('取消')
        .layoutWeight(1)
        .height(40)
        .textAlign(TextAlign.Center)
        .backgroundColor(COLORS.warmLight)
        .borderRadius(8)
        .onClick(() => {
          this.showModal = false;
        })
      
      Text('确定')
        .layoutWeight(1)
        .height(40)
        .textAlign(TextAlign.Center)
        .backgroundColor(COLORS.accent)
        .fontColor('#FFFFFF')
        .borderRadius(8)
        .margin({ left: 8 })
        .onClick(() => {
          this.showModal = false;
        })
    }
    .width('100%')
    .margin({ top: 16 })
  }
  .width('80%')
  .backgroundColor(COLORS.cardBg)
  .borderRadius(16)
  .padding(20)
}

新增弹窗包含标题、多个 TextInput 输入框和取消/确定双按钮。按钮使用 layoutWeight(1) 等分宽度,确定按钮使用强调色背景 + 白色文字,取消按钮使用浅暖色背景,形成主次按钮的视觉层级。

17.3 编辑弹窗(modalEdit)与删除弹窗(modalDelete)

编辑弹窗与新增弹窗结构类似,但会从 editTarget 读取已有数据填充表单。删除弹窗更简洁,仅展示确认文案和取消/确认按钮,确认按钮使用 COLORS.danger(红色)背景以警示操作风险。


十八、功能模块对比表

模块 布局方式 数据模型 字段数 核心操作 动画效果 状态颜色 数据量 特殊组件
首页 双列卡片网格 PetService 6 点击卡片 6 slice分批ForEach
附近 水平分栏列表 PetShop 5 查看详情 评分→星星emoji 6 嵌套ForEach标签
订单 时间轴卡片 PetOrder 5 编辑/删除 状态→颜色映射 6 Line时间轴连线+Circle圆点
档案 大卡片+四宫格 PetProfile 6 编辑 3 Divider分割线+四宫格
社区 横滚筛选+信息流 PetPost 6 关注/删除 选中态切换 6 水平Scroll分类条
我的 三段式区块 PetTask + CHART_DATA 4+2 完成任务 优先级→色条 6 纯UI柱状图+色条
头部 Stack叠加 通知 呼吸动画 线性渐变背景
图表 柱状图 CHART_DATA 2 最新→强调色 6 Column高度映射
Tab栏 等分图标行 TAB_LIST 2 切换Tab 选中→强调色 6 opacity双态
弹窗 Stack遮罩 联合类型 新增/编辑/删除 删除→危险色 条件渲染三弹窗

十九、总结与展望

19.1 技术总结

本文剖析的萌宠家宠物服务平台,完整展示了基于 HarmonyOS ArkUI 框架构建多场景应用的核心技术方案。从架构层面看,应用采用了四层设计——常量与工具层提供色彩和数据基础设施,数据模型层通过 @Observed 实现可观察性,组件层通过 @State 管理响应式状态,Builder 层通过 13 个 @Builder 函数拆分渲染逻辑。

六个 Tab 的布局策略各不相同——双列网格、分栏列表、时间轴、大卡片、信息流、三段式——确保了视觉差异化和用户体验的丰富性。每个布局都不是随意选择,而是根据内容特征量身设计:服务推荐需要网格化浏览,商家信息需要左右分栏,订单流程需要时间轴暗示进度,宠物档案需要大卡片突出重要性,社区动态需要信息流式浏览,个人中心需要多区块聚合。

19.2 关键技术决策回顾

决策 选择 替代方案 选择理由
状态管理 @Observed + @State AppStorage 全局状态 六 Tab 数据各自独立,无需跨组件共享
动画方案 单 setInterval + 布尔翻转 多 animateTo 调用 全局仅一个定时器,性能开销最小
图标方案 emoji 字符 Image 组件 + 图片资源 零依赖、即改即显、包体积零增加
柱状图 Column 高度映射 第三方图表库 数据量小,纯 UI 方案够用且可控
Tab 切换 if-else 条件渲染 Swiper 组件 条件渲染只保留当前 Tab 组件,内存更省
文字溢出 maxLines + Ellipsis 截断或自动缩小 省略号保留信息提示,不破坏布局
状态颜色 工具函数 + Record 映射 硬编码颜色值 颜色策略集中管理,易维护易扩展

19.3 展望

当前实现已在视觉层、交互层、状态管理层达到了生产级 Demo 的标准,但仍有以下可优化空间:

  • 网络请求层:当前使用 Mock 数据,实际应用需引入 @ohos.net.httpRetrofit 风格的网络框架,增加请求拦截、错误重试、缓存策略
  • 持久化存储:宠物档案、任务清单等用户数据需通过 @ohos.data.preferences 或 SQLite 持久化,确保应用重启后数据不丢失
  • 组件抽取:13 个 Builder 函数可进一步抽取为独立组件文件,通过 @Component export struct 实现跨页面复用
  • 暗色模式:COLORS 常量需改造为根据 ConfigurationConstant.ColorMode 动态切换的双色板
  • 无障碍支持:emoji 图标对屏幕阅读器不友好,需补充 accessibilityText 属性
  • 动画丰富度:可在 Tab 切换时增加 transition 转场动画,在卡片点击时增加 scale 缩放反馈

鸿蒙 ArkUI 的声明式范式让"多场景差异化布局 + 统一状态管理"成为自然而非勉强的选择。随着 OpenHarmony 生态的持续扩展,基于 ArkUI 的行业应用将拥有更广阔的实践空间。

附录:DevEco Studio 创建新项目与查看 SDK 版本

本章节演示如何使用 DevEco Studio 创建一个 HarmonyOS 新项目,并查看当前 IDE 已安装的 SDK 版本,适合作为其他技术博文的补充操作指南。


一、创建新项目

1.1 进入欢迎界面

启动 DevEco Studio 后,首先看到的是欢迎界面。左侧导航栏默认选中 “项目”,右侧提供三个主要入口:

  • 新建项目:从头创建新项目
  • 打开项目:打开本地已有项目
  • 克隆仓库:从 Git 等版本控制拉取代码

点击 “新建项目” 按钮,进入项目创建向导。

在这里插入图片描述

1.2 选择项目模板

在弹出的"新建项目"对话框中,左侧分类标签提供了两种项目类型:

类型 说明
应用(Application) 开发标准的 HarmonyOS 应用,具备完整的 Ability 生命周期
元服务(Atomic Service) 开发轻量级的原子化服务,无需安装即可使用

选择 “应用” 标签后,右侧展示多种模板。对于大多数场景,推荐选择 “Empty Ability” —— 这是一个最基础的入门模板,仅包含 Hello World 功能,适合从零开始构建应用。

在这里插入图片描述

1.3 配置项目信息

点击 “下一步” 后,进入项目配置界面,需要填写以下核心参数:

配置项 示例值 说明
项目名称(Project name) rollboat 应用的项目名称,建议使用英文命名
包名(Bundle name) com.rollboat.myapplication 应用唯一标识,采用反向域名格式
保存路径(Save location) D:\CodeFactory\rollboat 项目本地存储路径,避免使用中文和空格
兼容 SDK(Compatible SDK) 6.1.1(24) 目标 HarmonyOS API 版本,点击"查看参考"可了解各版本差异
模块名称(Module name) entry 主模块名称,默认 entry 为应用入口模块
设备类型(Device types) ☑ Phone 勾选目标设备:Phone / Tablet / 2in1 / Car / Wearable / TV

右侧预览区会实时展示当前模板的默认效果 —— 一个居中显示的 “Hello World” 文本。

在这里插入图片描述

1.4 完成创建

确认配置无误后,点击右下角 “完成” 按钮,IDE 将自动执行以下操作:

  1. 生成项目骨架(Stage 模型目录结构)
  2. 执行 ohpm install 安装依赖
  3. 运行 Hvigor 构建初始化(Build Init

构建日志中显示 “退出代码为 0” 表示项目初始化成功。

在这里插入图片描述

1.5 项目结构概览

创建完成后,左侧项目面板展示的是标准的 Stage 模型 目录结构:

rollboat/
├── .hvigor/                   # Hvigor 构建工具缓存
├── .idea/                     # IDE 配置文件
├── AppScope/                  # 应用级全局配置
│   └── app.json5
├── entry/                     # 主模块(入口模块)
│   ├── src/main/ets/
│   │   ├── entryability/      # Ability 生命周期管理
│   │   │   └── EntryAbility.ets
│   │   └── pages/             # UI 页面
│   │       └── Index.ets      # 首页(默认 Hello World)
│   ├── src/main/resources/    # 资源文件
│   ├── module.json5           # 模块配置
│   └── build-profile.json5    # 构建配置
├── oh_modules/                # OHPM 依赖包
├── build-profile.json5        # 工程构建配置
├── hvigorfile.ts              # Hvigor 构建脚本
└── oh-package.json5           # 包管理配置

核心文件 Index.ets 的默认代码如下,采用 ArkTS 声明式 UI 语法:

@Entry
@Component
struct Index {
  @State message: string = 'Hello World';

  build() {
    RelativeContainer() {
      Text(this.message)
        .id('HelloWorld')
        .fontSize($r('app.float.page_text_font_size'))
        .fontWeight(FontWeight.Bold)
        .alignRules({
          center: { anchor: '__container__', align: VerticalAlign.Center },
          middle: { anchor: '__container__', align: HorizontalAlign.Center }
        })
        .onClick(() => {
          this.message = 'Welcome';
        })
    }
    .height('100%')
    .width('100%')
  }
}
关键语法 作用
@Entry 标记为页面入口,可用于路由跳转
@Component 声明为自定义组件
@State 状态变量,数据变更时自动触发 UI 刷新
RelativeContainer 相对布局容器,替代传统线性布局
.onClick() 点击事件,此处点击后文本变为 “Welcome”

打开右侧 Previewer(预览器),选择 Phone 设备,即可实时预览 Hello World 效果,无需连接真机或启动模拟器。

在这里插入图片描述


二、查看 SDK 版本

2.1 查看 HarmonyOS SDK

DevEco Studio 安装时已内置 HarmonyOS SDK,无需单独下载。通过以下路径查看:

文件 → 设置 → HarmonyOS SDK(或快捷键 Ctrl + Alt + S 搜索 “HarmonyOS SDK”)

在设置面板中,可以看到当前已安装的 SDK 版本信息:

名称 阶段 状态
HarmonyOS 6.1.1 Release ✅ 已安装

界面顶部提示:“HarmonyOS SDK 已经包含在 IDE,无需单独安装”,省去了手动配置 SDK 的繁琐步骤。

在这里插入图片描述

2.2 查看 ArkUI-X SDK(跨平台扩展)

如果项目需要将 ArkUI 框架扩展到多个 OS 平台(Android / iOS / OpenHarmony),还需要配置 ArkUI-X SDK。路径如下:

文件 → 设置 → 语言和框架 → ArkUI-X

在这里可以查看已安装和可选的 ArkUI-X SDK 版本:

版本 SDK 版本号 阶段 状态
API Version 24 6.1.1.100 Release ✅ 已安装
API Version 23 6.1.0.28 Beta1 未安装
API Version 22 6.0.2.112 Release 未安装

安装路径示例:D:\DevTools\ArkUI-X\sdk

说明:ArkUI-X 允许开发者使用一套 ArkTS 主代码,同时构建多平台应用。如果仅开发 HarmonyOS 原生应用,无需额外安装 ArkUI-X SDK。

在这里插入图片描述


三、小结

步骤 操作 关键点
创建项目 欢迎页 → 新建项目 → 选择 Empty Ability 模板 → 配置项目信息 → 完成 使用 Stage 模型 + ArkTS 语言
查看 SDK 设置 → HarmonyOS SDK SDK 已内置,无需手动安装
跨平台扩展 设置 → ArkUI-X 根据需要安装对应 API 版本

至此,DevEco Studio 的项目创建与 SDK 环境确认全部完成,可以开始 HarmonyOS 应用的功能开发。


本文基于 DevEco Studio 6.1.1 Release 版本编写,不同版本界面可能存在细微差异。

Logo

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

更多推荐