HarmonyOS ArkUI 物业安全巡检平台如何落地?从安全蓝灰与警示橙的视觉守护说起
一、技术前言

在智慧物业管理领域,安全巡检是保障建筑运行的第一道防线。从消防通道占用复查到配电间红外测温,从喷淋管网压力巡检到水泵启动试运行测试,每一项巡检任务都需要精确的流程管控、清晰的进度追踪和即时的异常告警能力。传统巡检应用往往面临三大痛点:取证影像模糊导致责任争议、告警铃声单一导致响应迟缓、巡检路径复杂导致界面交互割裂。

HarmonyOS ArkUI 框架以其声明式 UI 范式为这些问题提供了系统级的解决方案。ArkUI 基于 TypeScript 扩展的 ArkTS 语言,通过 @Component 装饰器封装可复用组件,通过 @State、@Observed 等状态管理装饰器实现数据驱动渲染,通过 @Builder 方法将复杂的 UI 结构拆分为可组合的构建块。这种架构天然适合巡检场景中"数据-视图-交互"紧耦合的需求。

本平台深度融合了 HarmonyOS 6.1.1 的三大前沿特性。Camera Kit 提供了 VideoSession 的 AUTO_FRAMING(影随人动)能力链——通过 isControlCenterSupported、getSupportedEffectTypes、enableControlCenter 三步实现巡检取景时人员始终居中;同时 PhotoSession 的手动对焦三接口 isFocusDistanceSupported、setFocusDistance、getFocusDistance 实现铭牌近拍到机房全景的精确对焦控制。Notification Kit 实现了沙箱自定义铃声链路——通过 buildWavBytes 生成正弦波 PCM 音频,写入 EL1 沙箱 filesDir,再以 'uri::' + fileUri.getUriFromPath(沙箱路径) 填入 NotificationRequest.sound,让不同等级告警拥有差异化铃声。Tabs 嵌套滚动 通过 nestedScroll(TabsNestedScrollMode) 让内层检查项列表滑到边缘后联动外层楼宇频道,实现巡检路径的自然流转。

二、整体架构流程图
整体架构以 Page1201 为根组件,采用 Stack 容器实现页面层叠:底层是 Column 纵向布局的头部 Banner + 内容区 + 底部 Tab 栏,顶层是全屏弹窗遮罩。内容区通过 currentTab 状态索引在 7 个 @Builder 方法间切换,每个 Tab 拥有完全独立的布局结构。四大特性(Camera Kit 影随人动、手动对焦、Tabs 嵌套滚动、Notification 沙箱铃声)分别挂载在相机、对焦、频道、告警四个 Tab 上,但它们的状态变量统一声明在组件顶层,实现跨 Tab 数据共享。

三、色彩体系设计
3.1 ColorPalette 接口定义
平台采用深色安全蓝灰主题,通过 ColorPalette 接口集中声明全部颜色字段:
interface ColorPalette {
bg: string; // 页面背景(安全蓝灰黑)
card: string; // 卡片底色(深蓝灰)
title: string; // 主标题(冷白)
sub: string; // 副标题(蓝灰)
text3: string; // 三级弱文本(暗蓝灰)
orange: string; // 警示橙(主色)
orangeD: string; // 警示橙深色(渐变起点)
blue: string; // 信息蓝(电气 / 内层日志)
green: string; // 合格绿(消防 / 已生效)
red: string; // 警示红(失败 / 删除)
line: string; // 分割线
tabOn: string; // Tab 选中色
mask: string; // 弹窗遮罩
onMain: string; // 橙底文字色(深暖黑)
}
3.2 COLORS 常量逐色分析
const COLORS: ColorPalette = {
bg: '#14171C', // 极深蓝灰黑,模拟夜间巡检环境
card: '#1E232B', // 卡片底色,比背景略亮一档
dark: '#283039', // 次级容器底色(统计格 / 进度条底)
title: '#EDF1F5', // 冷白色标题,高对比度保证暗光可读
sub: '#ADBAC7', // 蓝灰副标题,层次柔和过渡
text3: '#74818E', // 暗蓝灰弱文本,辅助信息不抢视觉
orange: '#FF8A2B', // 警示橙主色,巡检进度的视觉锚点
orangeD: '#E06A10', // 深橙渐变起点,头部Banner到背景的过渡
blue: '#4E9BE3', // 信息蓝,电气检查项与内层日志标识
green: '#34C98A', // 合格绿,已完成状态与读回校验通过
red: '#E85555', // 警示红,逾期任务与删除操作
line: '#2A313A', // 分割线,低对比度不干扰内容
tabOn: '#FF8A2B', // Tab 选中色与主色一致
mask: 'rgba(0,0,0,0.6)', // 半透黑遮罩
onMain: '#231507' // 橙底深字,保证按钮文字对比度
};
色彩设计遵循"安全警示"原则:橙绿蓝红四色分别对应"巡检中/已合格/信息参考/危险警告"四种语义状态,使用户在深色环境下凭颜色即可快速识别任务优先级。头部 Banner 的 linearGradient 从 orangeD 到 bg 实现警示橙到蓝灰黑的自然过渡,底部 7 Tab 栏选中态使用 orange 高亮,未选中态使用 text3 暗蓝灰弱化。

四、Tab 元数据与辅助数据
4.1 底部导航 Tab 定义
const TAB_LIST: TabMeta[] = [
{ icon: '📋', label: '任务' },
{ icon: '📷', label: '相机' },
{ icon: '🎯', label: '对焦' },
{ icon: '🌀', label: '频道' },
{ icon: '📜', label: '日志' },
{ icon: '🚨', label: '告警' },
{ icon: '👤', label: '我的' }
];
7 个 Tab 单排排列,从任务管理到个人中心覆盖巡检全流程。每个 Tab 的图标与其功能语义紧密对应:📋 代表任务清单,📷 代表取证拍照,🎯 代表对焦精准,🌀 代表频道流转,📜 代表日志记录,🚨 代表告警上报,👤 代表巡检员中心。

4.2 Camera Kit 效果枚举
const EFFECT_INFOS: EffectInfo[] = [
{ type: 0, name: 'BEAUTY', desc: '美颜 · since 20' },
{ type: 1, name: 'PORTRAIT', desc: '人像 · since 20' },
{ type: 2, name: 'AUTO_FRAMING', desc: '影随人动 · 6.1.1 新增' }
];
三效果枚举展示了 ControlCenterEffectType 的完整谱系。BEAUTY 和 PORTRAIT 自 API 20 起就存在,AUTO_FRAMING 是 6.1.1 新增能力,本平台正是利用这一新特性实现巡检跟拍时人员始终居中。
4.3 对焦预设三档
const FOCUS_PRESETS: FocusPreset[] = [
{ label: '铭牌近拍', distance: 0.1, scene: '0.1 · 铭牌/序列号' },
{ label: '设备中距', distance: 0.5, scene: '0.5 · 配电柜整柜' },
{ label: '环境远距', distance: 0.9, scene: '0.9 · 机房全景' }
];
对焦距离值范围 0.0(最近)到 1.0(最远),三档预设覆盖巡检取证的三个典型景别:0.1 用于拍摄设备铭牌和出厂序列号,0.5 用于拍摄配电柜整体和管线走向,0.9 用于拍摄机房全景和疏散通道。
4.4 嵌套滚动频道数据
const OUTER_CHANNELS: ChannelItem[] = [
{ name: '1 号楼', icon: '🏢' },
{ name: '2 号楼', icon: '🏬' },
{ name: '地下车库', icon: '🅿️' },
{ name: '配电房', icon: '⚡' },
{ name: '消防泵房', icon: '🚒' }
];
const INNER_TABS: string[] = ['消防', '电气', '管道', '通道', '监控'];
外层 5 个楼宇频道代表物业巡检的责任分区,内层 5 个检查项类别覆盖消防、电气、管道、通道、监控五大专业领域。两层 Tabs 嵌套形成 25 个检查矩阵,每个矩阵下有 8 条检查卡片,共 200 个检查点位。
4.5 月度隐患柱状图数据
const MONTH_NAME: string[] = ['03', '04', '05', '06', '07', '08'];
const MONTH_HAZARD: number[] = [12, 9, 15, 7, 11, 6];
const MONTH_MAX: number = 16;
近 6 个月隐患发现数据呈下降趋势(15→7→6),反映巡检整改效果。满刻度 16 处用于柱高归一化换算,柱状图随呼吸动画在 ±6% 区间交替波动。
五、工具函数
5.1 时间戳生成
function nowTime(): string {
const d = new Date();
return `${String(d.getHours()).padStart(2, '0')}:${String(d.getMinutes()).padStart(2, '0')}:${String(d.getSeconds()).padStart(2, '0')}`;
}
nowTime 函数为 SwipeLog、FocusRecord、NoticeLog 三类时间线提供统一的时间戳格式化,使用 padStart(2, '0') 保证时分秒始终两位数,格式为 HH:mm:ss。
5.2 嵌套模式文案映射
function modeLabel(mode: TabsNestedScrollMode): string {
return mode === TabsNestedScrollMode.SELF_FIRST
? 'SELF_FIRST·先内后外' : 'SELF_ONLY·仅内层';
}
function modeShort(mode: TabsNestedScrollMode): string {
return mode === TabsNestedScrollMode.SELF_FIRST ? '先内后外' : '仅内层';
}
两个函数将 TabsNestedScrollMode 枚举翻译为中文文案。modeLabel 返回完整技术文案用于日志记录,modeShort 返回短文案用于头部状态胶囊和模式切换 chips。SELF_FIRST 模式下内层滑到边缘会接力触发外层切换,SELF_ONLY 模式下内层滑动不联动外层。
5.3 影随人动状态配色
function framingStateColor(s: string): string {
if (s === '影随人动已启用') return COLORS.green;
if (s === '控制中心不支持' || s === 'AUTO_FRAMING 未声明') return COLORS.blue;
if (s.indexOf('失败') >= 0 || s.indexOf('被拒') >= 0 || s.indexOf('未就绪') >= 0) return COLORS.red;
return COLORS.text3;
}
该函数将影随人动能力链的各阶段结果映射为颜色:已启用为合格绿(表示巡检跟拍已就位),能力不支持为信息蓝(设备限制非故障),失败/被拒为警示红(需要处理),未查询为暗蓝灰弱化。
5.4 任务状态配色
function taskStatusColor(s: string): string {
if (s === '已完成') return COLORS.green;
if (s === '进行中') return COLORS.orange;
if (s === '待复查') return COLORS.blue;
if (s === '已逾期') return COLORS.red;
return COLORS.text3;
}
四种巡检任务状态对应四种颜色语义:已完成→合格绿、进行中→警示橙(正在执行)、待复查→信息蓝(需要复核)、已逾期→警示红(超时未完成)。
5.5 WAV 音频生成
function buildWavBytes(freq: number, durationMs: number): ArrayBuffer {
const sampleRate = 44100;
const numSamples = Math.floor(sampleRate * durationMs / 1000);
const dataSize = numSamples * 2;
const buf = new ArrayBuffer(44 + dataSize);
const view = new DataView(buf);
// ... 44 字节 WAV 头写入 ...
for (let i = 0; i < numSamples; i++) {
const t = i / sampleRate;
const env = Math.min(1, i / (sampleRate * 0.02)); // 起音包络
const decay = Math.max(0, 1 - t / (durationMs / 1000)); // 自然衰减
view.setInt16(44 + i * 2, Math.round(Math.sin(2 * Math.PI * freq * t) * 0.5 * env * decay * 32767), true);
}
return buf;
}
buildWavBytes 是 Notification Kit 沙箱铃声链路的起点。它使用 DataView 手工构造 44 字节 WAV 文件头(RIFF/WAVE/fmt /data 四段),再按频率参数生成 16bit 单声道 PCM 采样数据。起音包络 env 在前 20ms 内从 0 线性升到 1 避免爆音,自然衰减 decay 从 1 线性降到 0 模拟铃声渐弱。
5.6 检查要点池
function checkPoint(tabName: string, i: number): string {
const pool: string[] = tabName === '消防'
? ['灭火器压力表指针', '消火栓水带卡扣', '疏散指示标识灯', ...]
: tabName === '电气'
? ['配电柜母排温度', '断路器接线端子', ...]
: // ... 管道/通道/监控各有 8 项 ...
return pool[Math.min(pool.length - 1, Math.max(0, i - 1))];
}
checkPoint 函数为嵌套频道的 5 类检查项各提供 8 个行业语义化检查要点,如消防类的"灭火器压力表指针"、电气类的"配电柜母排温度"等。通过 tabName 分发到对应池,再按 i 索引取出,保证检查卡片内容真实可信。
六、数据模型层
6.1 TaskItem 巡检任务模型
@Observed export class TaskItem {
building: string; // 楼宇 / 分区名
item: string; // 巡检项目名
progress: number; // 进度(0~100)
status: string; // 状态(已完成/进行中/待复查/已逾期)
constructor(building: string, item: string, progress: number, status: string) {
this.building = building; this.item = item;
this.progress = progress; this.status = status;
}
}
TaskItem 是任务 Tab 的核心实体,使用 @Observed 装饰器使其字段变化能被 UI 感知。7 条 Mock 数据覆盖了从 1 号楼消防通道复查到 5 号楼应急照明断电测试的典型巡检场景,状态涵盖已完成、进行中、待复查、已逾期四种,进度值从 0% 到 100% 不等。
6.2 FocusRecord 对焦记录模型
@Observed export class FocusRecord {
time: string; // 操作时间戳
distance: number; // 设置的对焦距离(0.0~1.0)
readback: number; // 读回值(-1 表示调用失败)
ok: string; // 校验结论(已生效/读回偏差/失败)
constructor(distance: number, readback: number, ok: string) {
this.time = nowTime(); this.distance = distance;
this.readback = readback; this.ok = ok;
}
}
FocusRecord 记录每次手动对焦操作的完整链路:设置值 → 读回值 → 校验结论。readback 为 -1 时表示 getFocusDistance 调用失败,ok 字段为"已生效"时表示差值 < 0.01,为"读回偏差"时表示差值超出阈值。初始注入了 3 条种子记录(0.9/0.5/0.1 三档各一条)。
6.3 InnerCard 检查项卡片模型
@Observed export class InnerCard {
id: string; // ForEach 键
tag: string; // 检查项类别名
title: string; // 检查点标题
desc: string; // 检查内容描述
}
InnerCard 是嵌套频道内层的列表条目,由 innerMockData 生成器批量创建,每个频道×检查项组合产生 8 条,内容包含楼宇名、检查类别、检查点标题和描述。
6.4 SwipeLog 翻页日志模型
@Observed export class SwipeLog {
layer: string; // '外层楼宇' / '内层检查项'
tabName: string; // 切换到的页签名
fromIdx: number; // 起始索引
toIdx: number; // 目标索引
mode: string; // 事发时的嵌套模式
time: string; // 时间戳
}
SwipeLog 记录两层 Tabs 翻页事件,layer 区分外层楼宇和内层检查项,mode 记录事发时的嵌套模式(SELF_FIRST 或 SELF_ONLY),用于验证"内层滑到边缘是否接力外层"。
6.5 RingItem 与 NoticeLog 模型
@Observed export class RingItem {
name: string; // 铃声名
file: string; // 沙箱文件名
freq: number; // 生成频率 Hz
duration: number; // 时长 ms
size: string; // 文件大小展示
inSandbox: boolean; // 是否已写入沙箱
}
@Observed export class NoticeLog {
title: string; // 通知标题
text: string; // 通知正文(含发布结果)
time: string; // 发布时间戳
}
RingItem 管理 4 条告警铃声(疏散警报/消防长鸣/门禁提示/周界蜂鸣),初始均未导入沙箱。NoticeLog 记录通知发布历史,成功和失败均记一条,初始注入 2 条种子数据。
七、组件主体结构
7.1 @State 状态变量总览
组件 Page1201 声明了大量状态变量,按功能分为五组:
基础 UI 状态:currentTab 控制当前 Tab 索引,breath 驱动呼吸动画,timer 管理定时器句柄。
弹窗状态:addModal、editModal、delModal 三态开关,editIdx、delIdx 操作索引,绑定 TaskItem 实体。
Camera Kit 成员:previewController(XComponent 控制器)、cameraInput、previewOutput、videoSession(影随人动宿主)、photoSession(手动对焦宿主)、surfaceReady、sessionMode(idle/video/photo)、framingState、focusSupported、focusDistance、focusRecords、permState。
Notification Kit 成员:granted、notifyId、ringList、currentRingIdx、noticeLogs、alarmTitle、alarmLevel、alarmDesc。
Tabs 嵌套滚动成员:nestedMode、outerIndex、innerIndex、swipeLogs。
7.2 生命周期
aboutToAppear() {
notificationManager.isNotificationEnabled().then((enabled: boolean) => {
this.granted = enabled;
}).catch(() => {});
this.focusRecords.unshift(new FocusRecord(0.9, 0.9, '已生效'));
this.focusRecords.unshift(new FocusRecord(0.5, 0.51, '已生效'));
this.focusRecords.unshift(new FocusRecord(0.1, 0.12, '读回偏差'));
this.timer = setInterval(() => {
this.breath = !this.breath;
}, 1000);
}
aboutToDisappear() {
clearInterval(this.timer);
this.releaseSession();
}
aboutToAppear 做三件事:查询通知授权状态、注入 3 条种子对焦记录、启动 1000ms 呼吸动画定时器。aboutToDisappear 清理定时器并释放相机资源,防止后台占用摄像头。
7.3 build() 根构建
build() {
Stack({ alignContent: Alignment.Center }) {
Column() {
this.headerBanner()
Divider().strokeWidth(1).color(COLORS.line)
Column() {
if (this.currentTab === 0) { this.tabTask() }
else if (this.currentTab === 1) { this.tabCamera() }
// ... 其余 Tab 分支 ...
}.layoutWeight(1).width('100%')
this.tabBar()
}.width('100%').height('100%')
if (this.addModal || this.editModal || this.delModal) {
this.modalOverlay(() => { this.closeAllModals(); })
}
}.width('100%').height('100%').backgroundColor(COLORS.bg)
}
根构建使用 Stack 容器实现层叠:底层 Column 纵向排列头部 Banner、分割线、内容区(layoutWeight(1) 占满中间)、底部 Tab 栏;顶层是条件渲染的弹窗遮罩。内容区通过 if-else if 链在 7 个 Tab Builder 间切换。
八、头部区域详解
头部 headerBanner Builder 是全页视觉锚点,采用 linearGradient 从 orangeD 到 bg 的 160° 渐变:
@Builder headerBanner() {
Column({ space: 10 }) {
Row() {
Column({ space: 4 }) {
Text('巡检卫士 · 物业安全巡检').fontSize(20).fontWeight(FontWeight.Bold)
.fontColor(COLORS.onMain)
Text(this.currentTab === 0 ? `任务 · 今日 ${this.taskList.length} 项巡检`
: this.currentTab === 1 ? '相机 · 影随人动取证预览'
// ... Tab 联动副标题 ...
: '巡检员中心').fontSize(11).fontColor(COLORS.onMain).opacity(0.85)
}.alignItems(HorizontalAlign.Start).layoutWeight(1)
Circle({ width: 10, height: 10 }).fill(COLORS.onMain)
.opacity(this.breath ? 0.9 : 0.45)
}.width('100%')
// 状态胶囊行 ...
}.padding({ left: 16, right: 16, top: 12, bottom: 12 })
.width('100%')
.linearGradient({ angle: 160, colors: [[COLORS.orangeD, 0], [COLORS.bg, 1]] })
}
头部包含两行内容。第一行是应用标题"巡检卫士 · 物业安全巡检"和 Tab 联动副标题,副标题通过 currentTab 索引切换 7 种文案,右侧是呼吸圆点(breath 翻转驱动透明度 0.9/0.45 交替)。
第二行是 4 个状态胶囊:任务胶囊显示巡检项数、相机会话胶囊显示 sessionMode(idle/video/photo,配以灰/绿/橙三色圆点)、通知授权胶囊显示 granted 状态(绿/红圆点)、嵌套滚动胶囊显示 modeShort 结果。胶囊均使用 mask 半透黑背景,与橙渐变头部形成层次。
九、Tab0 任务 — 完成率统计与进度条清单
任务 Tab 是巡检工作的首页,纵向 Scroll 布局包含四段内容。
第一段:完成率统计大卡。46px 的橙色 monospace 大数字显示完成率百分比,搭配渐变进度条(linearGradient 从 orange 到 green)和三格统计(进行中橙、待复查蓝、已逾期红)。完成率通过 taskDone() 方法遍历 taskList 统计 progress >= 100 的条目数计算得出。
第二段:任务清单头。显示"📋 今日巡检任务 · N 项"标题和"+ 新增"入口按钮,点击触发 openAdd() 打开新建弹窗。
第三段:进度条清单。使用 ForEach 遍历 taskList,每条渲染为 taskCard Builder。卡片包含楼宇徽标、状态徽章、巡检项目名、Progress 线性进度条(color 绑定 taskStatusColor)和编辑/删除操作按钮。
第四段:月度隐患柱状图。chartCard Builder 使用 Column + ForEach 实现传统柱状图,6 根渐变柱(orange→orangeD)的柱高由 barHeight(i) 方法计算,breath 状态控制奇偶柱 ±6% 交替波动。
十、Tab1 相机 — 影随人动取证预览
相机 Tab 是 Camera Kit 影随人动特性的主舞台,采用独占高度布局。
授权卡:显示 CAMERA 权限状态(permState)和 Surface 就绪状态。点击"申请相机权限"调用 requestCameraPermission(),该方法通过 abilityAccessCtrl.createAtManager().requestPermissionsFromUser() 动态申请 ohos.permission.CAMERA 权限,检查 authResults[0] === 0 判断授权结果。
XComponent 预览本体:使用 Stack 叠加 XComponent(SURFACE 类型)和会话模式标签。onLoad 回调置 surfaceReady = true,后续会话创建依赖此标志。XComponentController 的 getXComponentSurfaceId() 为 PreviewOutput 提供 Surface ID。
影随人动能力链:startVideoMode() 方法完整实现了 AUTO_FRAMING 三步链路。首先通过 getCameraManager(ctx) 获取相机管理器,遍历 getSupportedCameras() 找到后摄,调用 getSupportedOutputCapability 获取预览 Profile。然后创建 CameraInput → open → 创建 PreviewOutput(传入 SurfaceId)→ 创建 VideoSession → beginConfig → addInput + addOutput → commitConfig。最后调用 queryFraming(this.videoSession) 执行能力链三步:
queryFraming(session: camera.VideoSession) {
if (!session.isControlCenterSupported()) {
this.framingState = '控制中心不支持';
this.framingSupported = false;
return;
}
const effects = session.getSupportedEffectTypes();
this.framingSupported = effects.includes(camera.ControlCenterEffectType.AUTO_FRAMING);
if (!this.framingSupported) { this.framingState = 'AUTO_FRAMING 未声明'; return; }
session.enableControlCenter(true);
this.framingState = '影随人动已启用';
}
第一步 isControlCenterSupported() 检查本机是否有控制中心,第二步 getSupportedEffectTypes() 获取支持的特效列表并检查是否包含 AUTO_FRAMING,第三步 enableControlCenter(true) 请求系统接管构图。三步任一失败均有降级文案,不会崩溃。
十一、Tab2 对焦 — 手动对焦三接口
对焦 Tab 展示 Camera Kit 手动对焦的三个接口,使用 PhotoSession(与 VideoSession 互斥)。
能力查询卡:queryFocusSupport() 调用 this.photoSession.isFocusDistanceSupported() 同步方法判断本机是否支持设置对焦距离。未启动拍照会话时显示"未启动拍照会话",已启动但不支持时显示"不支持设置对焦距离"。
三档预设:ForEach(FOCUS_PRESETS) 渲染三个等宽卡片,点击设置 focusDistance 为对应预设值,选中态使用橙色背景高亮。
焦距滑杆:Slider 组件范围 0~1 步进 0.01,onChange 实时更新 focusDistance,下方显示 distanceLabel 景别文案。
设置并读回校验:applyFocus() 方法是手动对焦三接口的完整演示:
applyFocus() {
this.photoSession.setFocusDistance(this.focusDistance); // 设置
const readBack = this.photoSession.getFocusDistance(); // 读回
const ok = Math.abs(readBack - this.focusDistance) < 0.01 ? '已生效' : '读回偏差';
this.focusRecords.unshift(new FocusRecord(this.focusDistance, readBack, ok));
}
setFocusDistance 设置对焦距离,getFocusDistance 立即读回,差值 < 0.01 判为已生效。结果以 FocusRecord 实体 unshift 置顶时间线。
对焦记录时间线:ForEach(this.focusRecords) 渲染时间线行,每行显示时间戳、设置值、箭头、读回值和校验结论徽章。focusOkColor 函数将"已生效"映射为绿色、"失败"映射为红色、其他映射为橙色。
十二、Tab3 频道 — 楼宇×检查项双层 Tabs 嵌套滚动
频道 Tab 是 Tabs 嵌套滚动特性的核心演示区。
模式切换 chips:ForEach([TabsNestedScrollMode.SELF_ONLY, TabsNestedScrollMode.SELF_FIRST]) 渲染两个可点击 chips,点击切换 nestedMode。选中态橙色背景白字,未选中态卡片底色暗字。
双层位置说明行:橙色圆点 + “外层 X 号楼” + 蓝色圆点 + “内层 Y(第 N/5 页)”,实时显示两层 Tabs 的当前位置。
外层宿主 Tabs:Tabs({ barPosition: BarPosition.Start }) 包含 5 个 TabContent,每个 tabBar 为楼宇图标+名称。barMode(BarMode.Scrollable) 支持横滑页签。onChange 回调记录外层翻页日志(layer=‘外层楼宇’),更新 outerIndex。
内层 Tabs:innerTabs(channel) Builder 为每个外层频道创建内层 Tabs。5 个检查项子页签,每个 TabContent 内是 List + ForEach(innerMockData(channel, name)),8 条检查卡片保证内容超一屏。关键代码:
.nestedScroll(this.nestedMode) // 嵌套滚动挂载点
nestedScroll 挂载在被嵌套的内层 Tabs 上。在 SELF_FIRST 模式下,内层滑到边缘后继续滑动会接力触发外层 onChange;在 SELF_ONLY 模式下则不会。内层 onChange 同样记录翻页日志(layer=‘内层检查项’)。
十三、Tab4 日志 — nestedScroll 翻页时间轴
日志 Tab 以固定行高 72px 的时间轴展示两层翻页事件。
顶部计数 + 清空按钮:显示"已记录 N 次翻页",Button('清空日志') 的 enabled 绑定 swipeLogs.length > 0。
图例说明行:橙色圆点"外层楼宇翻页" + 蓝色圆点"内层检查项翻页" + 当前嵌套模式文案。
时间轴列表:ForEach(this.swipeLogs) 渲染每条日志。每行分三列:时间列(固定宽 52,时间 + OUT/IN 标识)、竖线列(固定宽 3,外层橙色内层蓝色)、内容卡(层级徽章 + 页签名 + fromIdx→toIdx + 模式+时间)。空态显示引导文案"去「频道」Tab 滑动内外层页签"。
十四、Tab5 告警 — 沙箱自定义铃声全链路
告警 Tab 是 Notification Kit 特性的压缩单 Tab 展示,包含五个区块。
区块① 异常上报表单:标题输入框 + 等级 chips(一般/严重/紧急,配以蓝/橙/红三色) + 描述文本域 + 发布按钮。点击"🔔 发布告警通知"调用 submitAlarm(),组合等级与描述后走 publishNotice()。
区块② 通知授权状态卡:显示 granted 状态,"重新查询"调用 isNotificationEnabled(),"申请授权"调用 requestAuth()。requestAuth() 方法先尝试 requestEnableNotification(hostCtx),若返回 1600004(曾被拒绝)则降级调用 openNotificationSettings(hostCtx) 拉起系统通知设置页。
区块③ 告警铃声行:ForEach(this.ringList) 渲染 4 条铃声,每行显示名称、频率/时长/大小/沙箱状态。点击"生成"调用 importRing(idx),该方法调用 saveRingToSandbox 将 WAV 写入 EL1 filesDir。点击"设默认"调用 setCurrentRing(idx) 切换 currentRingIdx。
sound 实时值预览:getSoundValue() 方法实时拼接 'uri::' + fileUri.getUriFromPath(沙箱路径),让用户在发布前预览通知 sound 字段的实际值。
区块⑤ 发布历史:ForEach(this.noticeLogs) 渲染 NoticeLog 时间线,成功记录标题为标题色,失败记录标题为红色。
通知发布核心方法
publishNotice(title: string, text: string) {
const ring = this.ringList[this.currentRingIdx];
if (!ring.inSandbox) { this.importRing(this.currentRingIdx); }
const appCtx = hostCtx.getApplicationContext();
appCtx.area = contextConstant.AreaMode.EL1;
const sandboxPath = appCtx.filesDir + '/' + ring.file;
const uri = fileUri.getUriFromPath(sandboxPath);
const soundVal = 'uri::' + uri;
const request: notificationManager.NotificationRequest = {
id: this.notifyId++,
notificationSlotType: notificationManager.SlotType.SOCIAL_COMMUNICATION,
content: { /* ... */ },
sound: soundVal // 沙箱 uri 填入 sound
};
notificationManager.publish(request);
}
关键链路:沙箱铃声必须在 EL1 区域 → 取 filesDir 路径 → fileUri.getUriFromPath 转为 uri → 'uri::' 前缀拼接 → 填入 NotificationRequest.sound。这是 6.1.1 新特性,之前 sound 只能传 rawfile 文件名。
十五、Tab6 我的 — 巡检员渐变大卡与绩效清单
我的 Tab 展示巡检员个人信息和月度绩效。
渐变大卡:linearGradient 从 orangeD 到 orange 的 140° 渐变,包含巡检员姓名、工号、班次、三列统计(本月点位 312、整改闭环 54、巡检里程 46.8km)和责任区说明。
绩效清单:ForEach(PERF_ROWS) 渲染 6 行绩效数据,每行包含序号徽章、绩效名称、说明文案和数值。前三行数值使用橙色(重点指标),后三行使用副标题色。
十六、底部 Tab 栏
@Builder tabBar() {
Row() {
ForEach(TAB_LIST, (tab: TabMeta, index: number) => {
Column({ space: 3 }) {
Text(tab.icon).fontSize(17)
Text(tab.label).fontSize(9)
.fontColor(this.currentTab === index ? COLORS.tabOn : COLORS.text3)
}.justifyContent(FlexAlign.Center)
.layoutWeight(1)
.padding({ top: 7, bottom: 7 })
.onClick(() => { this.switchTab(index); })
}, (tab: TabMeta) => tab.label)
}.width('100%').backgroundColor(COLORS.card)
.border({ width: { top: 1 }, color: COLORS.line })
}
底部 7 Tab 单排,选中态 tabOn(警示橙),未选中态 text3(暗蓝灰)。点击调用 switchTab(idx),该方法在离开相机/对焦页时释放会话(cameraInput 同一时间只能绑一个 session),防止摄像头后台占用。
十七、弹窗系统
17.1 全屏遮罩 modalOverlay
@Builder modalOverlay(onClose: () => void) {
Column() {
Column().width('100%').layoutWeight(1)
.onClick(() => { onClose(); })
if (this.addModal) { this.panelAdd(onClose) }
else if (this.editModal) { this.panelEdit(onClose) }
else if (this.delModal) { this.panelDel(onClose) }
}.width('100%').height('100%').backgroundColor(COLORS.mask)
.justifyContent(FlexAlign.End)
}
遮罩层使用 Column 布局,空白区 layoutWeight(1) 占满上方,面板在底部弹出。backgroundColor(COLORS.mask) 为半透黑遮罩,点击空白区调用 onClose 关闭弹窗。面板根据三态标志(addModal/editModal/delModal)三选一渲染。
17.2 新建巡检任务 panelAdd
新建面板包含楼宇输入框、巡检项目输入框、初始进度 Slider(0~100 步进 5)和取消/创建按钮。点击"创建"调用 confirmAdd(),该方法将表单数据封装为 TaskItem 并 unshift 置顶任务清单,初始进度 >= 100 时状态置为"已完成",否则置为"进行中"。
17.3 编辑巡检进度 panelEdit
编辑面板显示当前任务的楼宇/项目/状态信息,提供进度 Slider。点击"保存"调用 confirmEdit(),更新 taskList[editIdx] 的 progress 和 status(100% 自动置为"已完成")。
17.4 删除确认 panelDel
删除面板显示待删除任务的楼宇和项目名,点击"删除"调用 confirmDel(),使用 splice(delIdx, 1) 从列表中移除。
十八、功能模块对比表
| 维度 | 任务 Tab | 相机 Tab | 对焦 Tab | 频道 Tab | 日志 Tab | 告警 Tab | 我的 Tab |
|---|---|---|---|---|---|---|---|
| 布局方式 | Scroll 纵向四段 | 独占高度+Stack | Scroll 纵向五段 | 双层 Tabs 嵌套 | 固定行高时间轴 | Scroll 五区块 | Scroll 纵向 |
| 数据模型 | TaskItem | — | FocusRecord | InnerCard | SwipeLog | RingItem/NoticeLog | PerfRow |
| 字段数 | 4 | — | 4 | 4 | 5 | 6/3 | 3 |
| 核心操作 | 新增/编辑/删除任务 | 影随人动开关/停止 | 三档预设/Slider/校验 | 模式切换/双层翻页 | 清空日志 | 上报/授权/铃声/发布 | — |
| 动画效果 | 呼吸柱状图波动 | 呼吸圆点 | — | 嵌套接力滚动 | — | 呼吸圆点 | 渐变大卡 |
| 状态颜色 | 已完成绿/进行中橙/待复查蓝/已逾期红 | idle灰/video绿/photo橙 | 已生效绿/偏差橙/失败红 | 外层橙/内层蓝 | 外层橙/内层蓝 | 一般蓝/严重橙/紧急红 | 前三行橙/后三行副色 |
| 数据量 | 7 条 | 3 效果枚举 | 3 种子记录 | 5×5×8=200 检查卡 | 动态增长(封顶40) | 4 铃声+2 种子日志 | 6 绩效行 |
| 特殊组件 | Progress 进度条 | XComponent+Stack | Slider 滑杆 | Tabs.nestedScroll | List 固定行高 | TextInput/TextArea | linearGradient 渐变 |
深化解析:从代码结构到业务闭环
布局方式与数据流
物业巡检页面需要把任务、取证、日志、告警和人员绩效串成闭环。任务卡不只是展示进度,它还是后续相机取证与告警记录的业务入口;日志用于说明操作何时发生;状态颜色帮助巡检员优先处理逾期和严重隐患。分析这些模块时,重点要观察同一个任务实体如何被列表、弹窗、统计卡与进度图共同消费,以及修改后哪些区域会随状态更新。
页面根结构通常由头部、内容区和底部 Tab 栏组成。头部负责展示当前业务状态,内容区根据索引选择不同的 @Builder,底部导航负责修改索引。这样的结构把“当前显示什么”收敛为一个明确状态:用户点击 Tab 后先更新索引,ArkUI 再重新计算相关分支。各个 Builder 虽然共享主题色和页面级数据,却可以采用完全不同的布局方式;高密度列表适合纵向 Scroll,概览数据适合横向统计卡或双列 Flex,实时预览类组件需要独占有界高度,历史事件则适合时间轴或固定行高 List。
数据模型层承担界面与业务之间的契约。使用 @Observed 的实体保存可编辑字段,页面级 @State 数组负责驱动 ForEach。新增时创建新实体并插入数组,编辑时修改目标实体,删除时移除对应项。为了让列表差分稳定,key 应来自不会改变的唯一标识,不宜使用标题等可编辑字段。统计数字、完成比例和分类数量属于派生信息,可以从数组即时计算,避免同时维护两份状态后出现卡片已经更新、图表仍显示旧值的情况。
弹窗表单使用独立缓存是必要的。打开新增弹窗时清空缓存,打开编辑弹窗时复制目标字段,用户确认后才写回正式模型。这样点击取消不会污染列表数据。若直接把 TextInput 双向绑定到列表实体,用户尚未保存时卡片就可能跟着变化,破坏“确认提交”的交互语义。删除弹窗还需要保存目标索引或唯一标识,并在确认时再次校验目标存在,避免列表变化后误删其他项。
核心代码与状态驱动机制
@State 的价值不是简单替代普通变量,而是建立状态与界面之间的依赖关系。当前 Tab、筛选条件、动画开关、弹窗显隐、下载进度或能力状态发生变化时,只有读取这些变量的组件需要刷新。代码段中连续的修饰器调用分别控制尺寸、间距、背景、字体和事件,它们共同构成声明式描述;阅读时应从容器方向、子项分布、状态绑定和交互回调四个层面理解,而不是逐个孤立翻译属性名称。
ForEach 负责把数组映射为重复 UI。回调中的 item 提供业务字段,index 适合显示顺序,但不适合作为长期身份。列表发生新增或删除时,稳定 key 可以让框架复用未变化节点,减少重建。若直接修改对象属性后界面没有按预期刷新,可在保持实体身份的前提下替换数组引用;但不应为了刷新把所有元素都重新构造,否则会增加无意义渲染并丢失局部状态。
条件渲染体现了页面状态机。空闲时展示引导,准备中展示进度,成功时展示结果,失败时展示原因和重试入口。相比一个布尔值,四态文案更能覆盖异步能力。系统接口调用前先检查权限、设备支持和会话状态,调用后再读取结果校验。异常处理除了记录错误码,还要把可理解的反馈写入响应式状态,让用户知道失败发生在哪一步。
动画效果与颜色使用策略
呼吸动画通常由定时器周期翻转 breath,再把该状态映射为透明度、柱高或圆点半径的小幅变化。它适合表达“正在运行”或让统计图保持生命感,但幅度应克制,不能改变核心数据含义。柱状图的基础高度仍由真实数值计算,动画只能在很小范围内偏移;进度环的角度仍由完成比例决定,不能为了视觉效果显示超过真实进度的结果。页面离开时必须清理定时器,避免后台继续刷新。
颜色常量应按语义使用。主色承担选中态和主要操作,辅助色突出数据或次级动作,绿色表达完成与可用,橙色表达进行中或需要注意,红色只用于失败、逾期和删除等高风险场景。弱文本与分割线降低视觉权重,遮罩色用于聚焦弹窗。颜色不能成为唯一的状态信息,还要配合文字、图标或进度值,保证色觉差异用户也能理解。
渐变更适合头部大卡、核心指标或柱状图,不宜在每个小元素上重复使用。深色主题要检查正文与卡片背景的对比度,浅色主题则要避免辅助文字过淡。选中和未选中 Tab 除颜色差异外,还可以通过字重、图标透明度或底部指示器区分。这样既保持主题统一,又能建立清晰的信息层级。
各 Tab 之间的交互联动
各 Tab 不应只共享一个导航索引,还应围绕业务对象建立必要联动。列表页新增或编辑数据后,头部计数、图表和个人统计要同步更新;网页或地图产生的结果应写入记录模型,供下载、日志或我的页面继续展示;通知、字幕、相机等系统能力的状态应在头部胶囊或对应 Tab 中保持一致。跨 Tab 跳转时先更新必要参数,再修改当前索引,可以避免目标页面读取到旧条件。
切换离开重型组件时需要处理资源边界。相机输入、地图监听、字幕控制器、Web 下载代理和定时器都不能只创建不释放。可以在统一的 switchTab 方法中判断来源与目标,离开能力页时解除监听或停止会话;页面销毁时再执行兜底释放。释放方法应允许重复调用,并对每个资源独立判空,确保一次异常不会阻止后续清理。
交互反馈要覆盖成功与失败。按钮点击后先进入处理中状态并防止重复提交;成功后更新模型、关闭弹窗并显示结果;失败后保留用户输入,展示错误原因和重试入口。权限拒绝、能力不支持、网络失败、文件不存在和输入非法都属于正常业务分支。通过状态卡或行内提示展示这些分支,比只在控制台打印更符合完整产品体验。
边界场景与验证思路
空列表时应显示占位说明和新增入口,不能只留下空白。长标题需要限制行数并使用省略号,数字字段需要限定上下界,文本提交前要去除首尾空格。筛选后无结果应保留清除条件的入口。删除最后一项后,当前选择索引要回退到有效范围。异步搜索连续触发时,应防止较早请求晚返回后覆盖新结果。
验证数据链路时,可以依次检查新增、编辑、删除和筛选:新增后列表条数、统计数字和图表是否同时变化;编辑取消后正式数据是否保持不变;删除后 ForEach key 是否稳定;切换 Tab 再返回时必要数据是否仍在。验证系统能力时分别模拟支持、拒绝和异常,确认界面都有明确状态。验证动画时检查页面离开后是否停止,低性能设备上是否仍保持流畅。
视觉验收需要检查不同屏幕宽度、系统字体放大、深浅背景对比和长文本换行。表格中的布局方式、模型、字段数、核心操作、动画、状态颜色、数据量和特殊组件应与正文一致。Mermaid 图则需要对应真实的数据流和能力链路,节点文字加引号以避免中文或特殊字符导致解析失败。
组件化设计的进一步理解
参数化 Builder 适合抽取重复的统计格、状态行、标签和按钮组。参数只传入渲染所需数据和事件,不让子构建器直接依赖过多页面变量,可以降低耦合。业务复杂后,可把模型与系统能力封装为独立控制器,页面只负责组合 UI 和响应状态。这样既保留声明式代码的直观性,也能让权限、错误码翻译和资源释放得到集中管理。
当前单页面集中展示完整源码,便于博文逐段讲解。若演进为正式项目,可以按领域拆分组件:导航和页面框架位于容器层,列表、图表和弹窗位于展示层,数据读写和 Kit 接入位于服务层。组件之间通过参数、回调、@Link 或 @ObjectLink 传递状态,不使用全局变量代替清晰的数据流。
性能优化首先来自减少不必要刷新。派生数据不要重复存储,动画状态不要进入列表 key,长列表使用稳定标识,Canvas 只在数据或尺寸变化时重绘。其次是控制资源生命周期,页面不可见时停止高成本任务。最后才是微调阴影、渐变和绘制细节。这样的优先级能保证页面在功能增加后仍然可维护。
通过以上补充,可以看到 ArkUI 的声明式模式并非只让布局语法更简洁,它更重要的价值是把数据变化、界面刷新和交互反馈连接为可追踪链路。理解每个代码段读取什么状态、写入什么状态、影响哪些组件,才能真正掌握文章中多个 Tab、图表、弹窗和系统能力协同工作的原理。
巡检任务状态闭环补充分析
任务 Tab 中的进度、状态与弹窗操作构成最核心的数据闭环。新增任务时,楼宇和巡检项目决定任务身份,初始进度决定首次状态;编辑时 Slider 修改的是表单缓存,确认后才写回 TaskItem。当进度达到百分之百,状态自动切换为已完成;低于百分之百时仍保持进行中。这里不能只修改进度条宽度而遗漏状态文字,否则同一卡片会同时出现“已完成”和未满进度的矛盾信息。删除任务后,统计卡的任务总数、完成数量和柱状图数据也应从最新数组重新推导。
相机与对焦两个 Tab 共享硬件输入,却依赖不同会话类型。进入相机页时创建 VideoSession 承载影随人动,进入对焦页时创建 PhotoSession 承载焦距设置;切换之前先释放旧会话,可以避免同一 CameraInput 被重复绑定。能力查询、效果枚举和启用控制中心属于前后依赖的三步链路,任一步失败都应终止后续调用,并把状态卡更新为具体失败阶段。手动对焦也要在设置后读回数值,通过差值判断镜头是否真正到位,而不能只把调用成功当作最终结果。
频道与日志 Tab 用来观察嵌套滚动行为。SELF_FIRST 表达内层到边缘后由外层接力,SELF_ONLY 表达手势只作用于内层;每次切换都写入 SwipeLog,使抽象的滚动策略变成可检查时间线。告警 Tab 则把铃声沙箱路径、授权状态和通知历史连接起来。发布前确认授权和文件存在,发布成功后追加记录,才能让界面形成从隐患发现到通知留痕的完整巡检链路。
从颜色策略看,任务状态不能直接散落在卡片代码的多个三元表达式中,最好统一经过状态颜色函数映射。已完成使用绿色、进行中使用橙色、待复查使用蓝色、已逾期使用红色,进度条、徽章和提示文字读取同一结果,才能避免语义不一致。我的 Tab 虽然不提供编辑操作,却复用了任务统计结果,因此它也是验证数据链路的重要位置:任务变化后,个人绩效概览需要同步反映最新数量。底部导航的选中态则用警示橙与暗蓝灰区分,既呼应物业巡检主题,也让用户明确当前所处模块。
十九、总结与展望
本平台以"安全蓝灰 + 警示橙"的深色视觉体系为基底,将 HarmonyOS 6.1.1 的四大前沿特性——Camera Kit 影随人动、Camera Kit 手动对焦三接口、Notification Kit 沙箱自定义铃声、Tabs 嵌套滚动——有机融合进物业安全巡检的 7 个业务场景中。
在技术架构上,平台采用"状态集中声明 + Builder 分散渲染"的模式。所有 @State 变量统一声明在组件顶层,确保跨 Tab 数据共享(如相机 Surface 在相机和对焦两个 Tab 间共用);而每个 Tab 的 UI 结构封装在独立的 @Builder 方法中,保证布局互不相同。弹窗系统采用"三态标志 + 全屏遮罩 + 底部面板"的统一模式,通过 if-else if 链三选一渲染,点空白关闭、按钮确认,交互逻辑简洁一致。
在特性集成深度上,影随人动能力链的三步查询(isControlCenterSupported → getSupportedEffectTypes → enableControlCenter)保证了从设备能力到运行时状态的完整验证;手动对焦三接口(isFocusDistanceSupported → setFocusDistance → getFocusDistance)实现了"设置-读回-校验"闭环;沙箱铃声链路(buildWavBytes → saveRingToSandbox EL1 → fileUri.getUriFromPath → 'uri::' 前缀 → NotificationRequest.sound)打通了从音频生成到通知发布的完整管道;Tabs 嵌套滚动(nestedScroll(TabsNestedScrollMode))通过 SELF_FIRST/SELF_ONLY 双模式切换,让内外层滑动接力行为可观测、可记录。
展望未来,本平台可在以下方向继续深化:引入 GIS 地图组件实现巡检路径可视化追踪;接入蓝牙信标实现室内定位自动打卡;利用 AI 识别引擎对取证照片进行隐患自动分类;通过分布式能力实现多巡检员协同任务分发。HarmonyOS 的分布式架构和 AI 能力为这些扩展提供了坚实的技术底座。
附录: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 将自动执行以下操作:
- 生成项目骨架(Stage 模型目录结构)
- 执行
ohpm install安装依赖 - 运行 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 版本编写,不同版本界面可能存在细微差异。
更多推荐

所有评论(0)