【HarmonyOS/ArkTS实战】 从零搭建高颜值待办清单(TodoList)应用 | 状态管理与列表渲染详解
本文详细解析了如何从零构建一个功能完整的待办清单(TodoList)应用。通过幽默风趣的方式,循序渐进地讲解了鸿蒙应用开发的核心知识点:@State 状态管理的魔力、List/ForEach 动态列表渲染的实现、组件封装与通信,以及用户交互(如删除、选中划线)的实现细节。无论您是鸿蒙初学者还是前端开发者,都能通过本实战 Demo 快速掌握 ArkTS 的声明式 UI 编程范式,实现数据驱动的界面更
文章目录
鸿蒙开发初体验:手撸一个高颜值的 TodoList(附源码)
哈喽大家好!我是正在死磕鸿蒙开发的大师兄。
最近在学 ArkTS,官方文档看得我眼花缭乱,不如直接上手敲个 Demo 来得实在。俗话说得好:“好记性不如烂笔头,烂笔头不如敲键盘”。今天就带大家看看我刚出炉的TodoList(待办清单)。

别看它功能简单,里面可是藏着鸿蒙开发的“核心内功”。来,搬好小板凳,咱们一段代码一段代码地“盘”它!
1. 万物皆对象:先造个“砖”

在写界面之前,我们得先定义一下,“待办事项”这玩意儿到底长啥样。
这里我定义了一个简单的 MyItem 类。它只有两个属性:一个独一无二的身份证号 id,还有一个具体的任务内容 text。
这就像是盖房子前的烧砖环节,砖头造好了,后面才能砌墙。
class MyItem{
id:number;
text:string;
constructor(id:number,text:string) {
this.id=id;
this.text=text;
}
}
2. 这里的指挥官:主组件与状态管理
接下来是主界面。在鸿蒙里,@State 是个魔法修饰符。一旦你给变量加上了这个标记,UI 界面就会死死地盯着它。只要数据一变,界面立马自动刷新,完全不需要我们手动去操作 DOM 或者 View。

这里我初始化了两个状态:
text:用来接收输入框里的字。list:存放我们所有的待办任务(为了不让界面太冷清,我先塞了两个测试任务进去)。
@Entry
@Component
struct TodoList {
@State text: string = '';
@State list: MyItem[]=[
new MyItem(Date.now(),"测试任务1"),
new MyItem(Date.now(),"测试任务2"),
]
// build() 函数下面会讲...
3. 颜值即正义:标题与输入框

进入 build() 函数,这就是我们搭积木的地方。
首先是标题,大大的“待办清单”四个字,得加粗,得显眼!
然后是输入框区域。这里有个小技巧:TextInput 的 onChange 事件。你每敲一个字,我就把它同步到 this.text 里。当你点击“添加”按钮时,我不仅要把任务塞进列表,还得顺手把输入框清空,不然还得手动删字,那多累啊。
build() {
Row() {
Column() {
// 标题区域
Text('待办清单')
.fontSize(32)
.fontWeight(FontWeight.Bold)
.fontColor('#2c3e50')
.margin({ top: 40, bottom: 30 })
// 输入区域
Row() {
TextInput({ placeholder: '添加新的待办事项...', text: this.text })
.width('75%')
.height(48)
.fontSize(16)
.backgroundColor('#f8f9fa')
.borderRadius(8)
.padding({ left: 16, right: 16 })
.onChange((value) => {
this.text = value
})
Button('添加')
.width('20%')
.height(48)
.fontSize(12)
.fontColor(Color.White)
.backgroundColor('#3498db')
.borderRadius(8)
.margin({ left: 12 })
.onClick(() => {
if(this.text.trim()) {
this.list.push(new MyItem(Date.now(), this.text.trim()))
this.text = ""
}
})
}
.width('100%')
.margin({ bottom: 10 })
// 下面是列表区域...
4. 列表的魔法:有数据显列表,没数据显图片

这部分逻辑我特意加了个判断:如果 list 里有货,就用 ForEach 循环渲染出来;如果 list 被删光了,那就显示一张“空空如也”的图片,顺便卖个萌。
List 组件里包裹着 ForEach,这是鸿蒙处理列表的标准姿势。注意看 TodoItem,这是我自己封装的子组件(下面会说),这样代码看起来就不会乱成一锅粥。
(PS: 那个 app.media.ic_empty 是我在资源文件夹里放的一张图,大家自己练手时随便找张图替换就行)
// 列表区域
if(this.list.length) {
Text(`共 ${this.list.length} 个待办事项`)
.fontSize(14)
.fontColor('#7f8c8d')
.margin({ bottom: 2 })
List() {
ForEach(this.list, (item: MyItem, index: number) => {
ListItem() {
TodoItem({ item, index, list: this.list })
}
.padding({ top: 8, bottom: 8 })
})
}
.layoutWeight(1)
.divider({
strokeWidth: 1,
color: '#ecf0f1',
startMargin: 20,
endMargin: 20
})
} else {
Column() {
Image($r('app.media.ic_empty'))
.width(120)
.height(120)
.margin({ bottom: 20 })
Text('待办事项空空如也~')
.fontSize(18)
.fontColor('#95a5a6')
}
.width('100%')
.height('60%')
.justifyContent(FlexAlign.Center)
}
}
.width('100%')
.height('100%')
.padding({ left: 24, right: 24 })
.backgroundColor(Color.White)
}
.height('100%')
.backgroundColor('#f5f7fa')
}
}
5. 子组件:每一行都是戏

最后是 TodoItem 组件,也就是列表里的每一行。
这里我有两个小心思:
- 划掉它:点击复选框或文字,字变灰并加上删除线。这种“干掉一个任务”的快感,懂的都懂。
- 删掉它:后面跟着一个红色的删除按钮。这里我用
list.splice(index, 1)直接操作父组件传过来的数组。虽然在大型项目里咱们推荐用事件回调,但在这种小 Demo 里,简单粗暴才是王道!
我还加了个 .onHover,鼠标放上去会有个变色效果,细节拉满有没有!
@Component
struct TodoItem {
private item: MyItem = new MyItem(Date.now(), "")
private index: number = 0
private list: MyItem[] = []
@State isChecked: boolean = false
@State isHover: boolean = false
build() {
Row() {
// 复选框区域
Row() {
Checkbox()
.width(24)
.height(24)
.selectedColor('#3498db')
.onChange((value) => {
this.isChecked = value
})
Text(this.item.text)
.fontSize(18)
.fontColor(this.isChecked ? '#95a5a6' : '#2c3e50')
.margin({ left: 12 })
.decoration({
type: this.isChecked ? TextDecorationType.LineThrough : TextDecorationType.None
})
.maxLines(1)
.textOverflow({ overflow: TextOverflow.Ellipsis })
}
.layoutWeight(1)
.onClick(() => {
this.isChecked = !this.isChecked
})
// 删除按钮
Button('删除')
.width(60)
.height(32)
.fontSize(12)
.fontColor(Color.White)
.backgroundColor(this.isHover ? '#e74c3c' : '#e67e22')
.borderRadius(6)
.opacity(this.isHover ? 1 : 0.7)
.onClick(() => {
this.list.splice(this.index, 1)
})
.onHover((isHover) => {
this.isHover = isHover
})
}
.width('100%')
.height(56)
.padding({ left: 16, right: 16 })
.backgroundColor(this.isHover ? '#f8f9fa' : Color.White)
.borderRadius(8)
.shadow({ radius: 2, color: '#00000008', offsetX: 0, offsetY: 1 })
.onHover((isHover) => {
this.isHover = isHover
})
}
}
📝 总结
搞定!不到 200 行代码,一个功能齐全、长得还不赖的 TodoList 就诞生了。

通过这个 Demo,我们学到了:
- 如何用 ArkTS 搭建界面(Row, Column)。
- 如何处理用户输入。
- 如何渲染列表。
- 如何让数据驱动 UI 变化。
感觉鸿蒙开发也没那么难嘛(除了偶尔要跟括号做斗争之外 😂)。
希望这个笔记对想入门的小伙伴有帮助,咱们下个 Demo 见!
更多推荐


所有评论(0)