HarmonyOS 动态任务列表应用开发实战
API Version 24 | ArkUI 声明式 UI | ArkTS
一、前言
随着 HarmonyOS Next 的推进,越来越多的开发者关注纯血鸿蒙应用开发。ArkTS 融合了 TypeScript 的类型安全与声明式 UI 的开发效率,为移动端开发带来全新体验。
本文以一个完整的 "动态任务列表" 应用为案例,从项目结构、页面布局、状态管理到交互逻辑,带您掌握 HarmonyOS 应用开发的核心技术。
二、应用功能概览
本应用是一个功能完整的待办事项管理工具,包含以下特性:
- 新建任务 --- 输入标题、可选副标题,选择优先级(高/中/低)
- 任务列表展示 --- 标题、副标题、优先级彩色标签
- 完成状态切换 --- 点击任务项切换,已完成显示删除线和绿色对勾
- 左滑删除 --- 向左滑动露出红色删除按钮
- 清除已完成 --- 一键清除所有已完成任务
- 重置示例 --- 恢复三个预设示例任务
- 实时统计 --- 顶部显示任务总数
三、项目结构
使用 DevEco Studio 创建 Empty Ability 模板项目(API 24),目录结构如下:
app6124/
├── AppScope/app.json5 # 应用级配置
├── entry/src/main/
│ ├── ets/
│ │ ├── entryability/ # Ability 生命周期
│ │ └── pages/Index.ets # 主页面
│ ├── module.json5 # 模块配置
│ └── resources/base/media/ # 图标资源(SVG)
├── build-profile.json5 # 项目级构建
└── hvigor/ # Hvigor 构建配置
四、核心技术实现
4.1 数据模型层
通过枚举和自定义类定义任务的数据结构:
typescript
enum Priority {
HIGH = 'HIGH', MEDIUM = 'MEDIUM', LOW = 'LOW'
}
class TaskItem {
title: string;
subtitle: string;
priority: Priority;
completed: boolean;
constructor(title: string, subtitle: string, priority: Priority, completed: boolean) {
this.title = title;
this.subtitle = subtitle;
this.priority = priority;
this.completed = completed;
}
}
使用 enum + class 的组合,配合 @State 装饰器实现响应式追踪。当任务对象的属性变化时,ArkUI 会自动侦测并刷新 UI。
4.2 主页面结构
页面使用 @Entry 和 @Component 装饰器标记,这是 ArkTS 声明式 UI 的核心模式:
typescript
@Entry
@Component
struct Index {
@State tasks: TaskItem[] = [];
@State newTaskTitle: string = '';
@State newTaskSubtitle: string = '';
@State newTaskPriority: Priority = Priority.HIGH;
@State priorityPickerShow: boolean = false;
aboutToAppear(): void {
this.resetSampleTasks();
}
// ... 方法
}
| 装饰器 | 作用 |
|---|---|
@Entry |
标记为页面入口,可作为路由目标 |
@Component |
声明该结构体是 UI 组件 |
@State |
标记为响应式状态,修改后自动触发 UI 刷新 |
4.3 顶层布局
采用 Column 纵向布局,背景色为浅灰色(#F2F2F7),模拟分组列表效果:
typescript
build() {
Column() {
// 1. 标题栏
// 2. 新建任务输入区
// 3. 操作按钮
// 4. 任务列表
// 5. 底部提示
}
.width('100%').height('100%').backgroundColor('#F2F2F7')
}
4.4 标题栏
使用 Row 水平布局,左侧应用名称,右侧任务总数,中间用 Blank() 撑满:
typescript
Row() {
Text('动态任务列表').fontSize(22).fontWeight(FontWeight.Bold).fontColor('#1C1C1E')
Blank()
Text(`共 ${this.tasks.length} 项`).fontSize(14).fontColor('#8E8E93')
}
.width('100%').padding({ left: 16, right: 16, top: 12, bottom: 8 })
Blank 组件类似于 Flexbox 的 flex-grow: 1,自动占据剩余空间实现两端对齐。
4.5 新建任务输入区
输入区包含三部分:标题输入框、副标题输入框、优先级选择器 + 添加按钮行。
TextInput 组件 通过 @State 双向绑定数据:
typescript
TextInput({ placeholder: '输入新任务...', text: this.newTaskTitle })
.placeholderColor('#C7C7CC').fontSize(16).height(44).padding({ left: 12 })
.onChange((value: string) => { this.newTaskTitle = value; })
优先级选择器 设计为弹出式下拉菜单,使用 Radio 单选组件:
typescript
if (this.priorityPickerShow) {
Column() {
ForEach([Priority.HIGH, Priority.MEDIUM, Priority.LOW], (p: Priority) => {
Row() {
Radio({ value: this.getPriorityText(p), group: 'priority' })
.checked(this.newTaskPriority === p)
.onChange(() => { this.setPriority(p); })
Text(this.getPriorityText(p)).fontSize(15)
}.onClick(() => { this.setPriority(p); })
})
}
// ...样式
}
这里使用 条件渲染 (if 直接写在 UI 描述中)控制下拉菜单显示隐藏,比 visible 属性更直观高效。
4.6 添加任务逻辑
typescript
addTask(): void {
const title = this.newTaskTitle.trim();
if (!title) return;
this.tasks.push(
new TaskItem(title, this.newTaskSubtitle.trim(), this.newTaskPriority, false)
);
this.newTaskTitle = '';
this.newTaskSubtitle = '';
this.newTaskPriority = Priority.HIGH;
}
由于 tasks 被 @State 装饰,push 操作自动触发列表重新渲染。ArkTS 对数组的响应式追踪支持 push、splice、filter 等常见操作。
4.7 操作按钮区
两个按钮并排,分别使用红色和绿色背景,内部包含图标 + 文字:
typescript
Button() {
Row() {
Image($r('app.media.ic_checkmark')).width(16).height(16).fillColor('#FFFFFF')
Text('清除已完成').fontSize(14).fontColor('#FFFFFF').margin({ left: 4 })
}
}
.height(36).backgroundColor('#FF3B30').borderRadius(18)
.padding({ left: 14, right: 14 })
.onClick(() => { this.clearCompleted(); })
使用 带子组件的 Button ------ Button() 不传 label,内部用 Row + Image + Text 自定义内容。这与 Button('文字') 的单标签模式是两种不同用法。
4.8 任务列表
使用 List + ListItem + ForEach 组合:
typescript
List({ space: 0 }) {
ForEach(this.tasks, (task: TaskItem, index: number) => {
ListItem() {
// 任务卡片 UI
}
.onClick(() => { this.toggleTaskCompletion(index); })
.swipeAction({ end: (): void => this.deleteSwipeAction(index) })
})
}
.width('100%').layoutWeight(1)
.divider({ strokeWidth: '0.5px', color: '#E5E5EA', startMargin: 52, endMargin: 0 })
ForEach--- 遍历数组,增量更新 DOMswipeAction--- 左滑操作,使用@Builder构建面板divider--- 列表分隔线,支持缩进控制.layoutWeight(1)--- 占满剩余空间
4.9 左滑删除
swipeAction 的 end 属性接收构建函数。ArkTS 不支持 Function.bind,使用 lambda 闭包 传参:
typescript
@Builder deleteSwipeAction(index: number) {
Button() {
Image($r('app.media.ic_delete')).width(24).height(24).fillColor('#FFFFFF')
}
.width(68).height('100%').backgroundColor('#FF3B30').borderRadius(0)
.onClick(() => { this.deleteTask(index); })
}
@Builder 装饰器可以将一段 UI 描述封装为可复用构建函数,内可直接编写声明式 UI 代码。
4.10 条件样式渲染
根据完成状态动态切换样式:
typescript
Image(task.completed ? $r('app.media.ic_checkmark_circle') : $r('app.media.ic_circle'))
.fillColor(task.completed ? '#34C759' : '#C7C7CC')
Text(task.title)
.fontColor(task.completed ? '#8E8E93' : '#1C1C1E')
.decoration({
type: task.completed ? TextDecorationType.LineThrough : TextDecorationType.None
})
使用 三元运算符 动态控制样式属性,无需分支代码,非常简洁。
4.11 核心方法
typescript
toggleTaskCompletion(index: number): void {
if (index >= 0 && index < this.tasks.length) {
this.tasks[index].completed = !this.tasks[index].completed;
}
}
deleteTask(index: number): void {
if (index >= 0 && index < this.tasks.length) {
this.tasks.splice(index, 1);
}
}
clearCompleted(): void {
this.tasks = this.tasks.filter(task => !task.completed);
}
resetSampleTasks(): void {
this.tasks = [
new TaskItem('学习 @State 装饰器', '掌握声明式响应式数据绑定', Priority.HIGH, true),
new TaskItem('探索 Grid 布局', '了解网格布局的参数配置', Priority.LOW, false),
new TaskItem('完成第一篇笔记', '将学到的知识整理成文档', Priority.MEDIUM, false),
];
}
修改 @State 数组后,UI 自动响应式更新。
五、资源文件管理
在 resources/base/media/ 下存放 6 个 SVG 图标:
| 文件 | 用途 |
|---|---|
ic_arrow_down.svg |
优先级下拉箭头 |
ic_checkmark.svg |
对勾图标 |
ic_checkmark_circle.svg |
实心圆圈对勾(已完成) |
ic_circle.svg |
空心圆圈(未完成) |
ic_refresh.svg |
刷新图标 |
ic_delete.svg |
删除图标 |
SVG 图标的优势是可通过 .fillColor() 动态修改颜色,无需为每种颜色准备独立图片。
六、构建与部署
6.1 构建
终端执行:
bash
hvigorw assembleHap
流程:PreBuild → CreateModuleInfo → MergeProfile → ProcessResource → CompileArkTS → PackageHap → SignHap → 完成。
6.2 常见问题
问题 1:Button 标签冲突
Error: The Button component with a label parameter can not have any child.
要么用 Button('文字') 不带花括号,要么用 Button() { Text('文字') },二者选一。
问题 2:Function.bind 不支持
Error: 'Function.bind' is not supported (arkts-no-func-bind)
ArkTS 禁用了 bind,改用 lambda:() => this.myMethod(arg)。
问题 3:属性命名冲突
Warning: ... cannot have name as 'activeCount'.
某些名称与系统属性冲突,换名即可(如 incompleteCount)。
问题 4:运行时闪退
检查 module.json5 中 pages 是否指向正确的 $profile:main_pages,以及 main_pages.json 的页面路径是否匹配。
七、ArkTS 开发最佳实践
7.1 声明式 UI 思维
ArkTS 采用声明式范式,与传统命令式 UI 本质不同:
| 传统命令式 | ArkTS 声明式 |
|---|---|
| 手动 findViewById | 直接编写 UI 描述 |
| 手动 setText/setColor | UI 自动随状态变化 |
| 复杂的 Diff 刷新 | 细粒度响应式追踪 |
写作 UI 时思考"当前状态下 UI 应该什么样",而非"怎么修改控件"。
7.2 响应式状态体系
| 装饰器 | 场景 |
|---|---|
@State |
组件内自有状态 |
@Prop |
父→子单向传递 |
@Link |
父子双向同步 |
@Provide/@Consume |
跨层级通信 |
@Observed/@ObjectLink |
深层对象观测 |
7.3 组件化拆分建议
应用功能增长时拆分为子组件:
Index → TaskHeader + TaskInput(含 PriorityPicker)
→ TaskActions + TaskList(含 TaskCard)+ TaskFooter
子组件通过 @Prop/@Link 通信,保持代码清晰可维护。
7.4 生命周期
| 钩子 | 时机 |
|---|---|
aboutToAppear |
组件创建前,初始化数据 |
onPageShow |
页面显示 |
onPageHide |
页面隐藏 |
aboutToDisappear |
组件销毁前,清理资源 |
八、扩展方向
当前应用功能完整,还可扩展:
- 数据持久化 --- 使用
@ohos.data.preferences或@ohos.data.relationalStore保存任务 - 任务编辑 --- 长按弹出对话框修改标题/副标题/优先级
- 搜索筛选 --- 按关键词搜索、按优先级筛选
- 拖拽排序 --- 使用
ListItem的dragEnabled属性 - 时间提醒 --- 结合
@ohos.backgroundTaskManager发送本地通知 - 深色模式 --- 监听
configurationChange切换主题
九、总结
本文从零构建了一个完整的 HarmonyOS 动态任务列表应用,涵盖了:
- ArkTS 基础 --- 枚举、类、装饰器、泛型
- ArkUI 核心组件 --- Column、Row、Text、TextInput、Button、List、Image、Radio、Stack
- 声明式 UI 语法 --- 条件渲染、循环渲染、@Builder
- 响应式状态 --- @State 与数组响应式追踪
- 事件处理 --- onClick、onChange、swipeAction
- 资源管理 --- SVG 图标引用与动态着色
- 构建部署 --- Hvigor 流程与问题排查
通过这个项目,您已掌握 HarmonyOS 应用开发的核心流程。声明式 UI 需要一些时间适应,但上手后会发现代码更简洁、维护成本更低。
项目完整代码位于
entry/src/main/ets/pages/Index.ets,SVG 图标位于resources/base/media/。使用 DevEco Studio 打开即可运行体验。本文发布于 HarmonyOS Next API 24,所有代码均已通过
hvigorw assembleHap构建验证,零错误零警告。