和 note1 一样,还是对着Demo自己敲一遍代码;不过考虑到TodoList太简单(相比于第一个Demo),决定结合后面章节的
router
相关知识点,做一点个性化的扩展。
1 准备
有了 note1 对ArkTs的细致剖析,实际上这个 todolist
的Demo难度远小于第一个Demo。
下载项目源码,完成若干文件的拷贝:
-
src/main/ets/viewmodel
——数据源,没有改动的空间 -
src/main/ets/common
——一些常量定义,没有改动的空间 -
src/main/resources/base
——资源文件,重要,有改动空间但没必要改动……(比如想给自己的待办项换个图标)
2 TodoItem
照着Demo实现待办项即可:
import CommonConstants from '../common/constant/CommonConstant'
@Component
export default struct TodoItem {
private content?: string
@State done: boolean = false;
@Builder labelIcon(icon: Resource) {
Image(icon)
.objectFit(ImageFit.Contain)
.width($r('app.float.checkbox_width'))
.height($r('app.float.checkbox_width'))
.margin($r('app.float.checkbox_margin'))
}
build() {
Row() {
if (this.done) {
this.labelIcon($r('app.media.ic_ok'))
} else {
this.labelIcon($r('app.media.ic_default'))
}
Text(this.content)
// ===== snip =====
}
// ===== snip =====
.onClick(() => {
this.done = !this.done
})
}
}
关键部分只是一个条件渲染,用于在不同的完成状态选择不同的图标,这个在 note 1 已经接触过了。
3 完成所有任务时使用Router
根据官网教程顺序,TodoList后面就是应用程序框架 UIAbility ,有个页面跳转的 Demo ,令我萌生想把TodoList跟这个Demo结合起来的想法。
容易想到,对于待办列表这个页面来说,当所有任务完成时,跳转到一个祝贺页面,是常见、简单且合理的场景。
于是做如下实现。
3.1 未完成项数统计
要在所有任务完成时进行页面跳转,首先应能够统计未完成的项数。
这里就适合使用 @Link
装饰器,将父子元素双向绑定,父元素(即总页面)提供总的待办任务数,每个子元素( TodoItem
)在被点为完成时将此未完成项数减一。
Talk is Cheap:
@Component
export default struct TodoItem {
private content?: string
@State done: boolean = false;
@Link unfinishedTasks: number;
build() {
Row() {
// ===== snip =====
.onClick(() => {
this.done = !this.done
if (this.done) {
this.unfinishedTasks -= 1;
} else {
this.unfinishedTasks += 1;
}
})
}
}
Index页面:
@Entry
@Component
struct Index {
private totalTasks: Array<string> = [];
@State @Watch('onTaskCountChange') unfinishedTasks: number = 0;
@State msg: string = '';
onTaskCountChange(propName: string): void {
this.msg = "toggle " + this.unfinishedTasks;
if (this.unfinishedTasks === 0) {
router.pushUrl({
url: 'pages/Congrats',
params: { count: this.totalTasks.length }
}).catch((error: Error) => {
this.msg = 'error: ' + JSON.stringify(error);
});
}
}
aboutToAppear() {
this.totalTasks = DataModel.getData();
this.unfinishedTasks = this.totalTasks.length;
}
build() {
Column({ space: CommonConstants.COLUMN_SPACE }) {
Text($r('app.string.page_title'))
// ===== snip =====
Text(this.msg)
.textAlign(TextAlign.Center)
ForEach(this.totalTasks, (item: string) => {
TodoItem({ content: item, unfinishedTasks: $unfinishedTasks });
}, (item: string) => JSON.stringify(item))
}
// ===== snip =====
}
}
这里关注 unfinishedTasks
这个变量,它和 @Link
的双向绑定,也是前一节中遇到过的。
3.2 未完成项数监控
能够实时更新未完成项数之后,还有一个重要的任务就是 当这个项数减为 0
时,要进行页面跳转 ,这里,通过 @Watch('[callback]')
装饰器,实现对这个变量的动态监控。
@State @Watch('onTaskCountChange') unfinishedTasks: number = 0;
onTaskCountChange(propName: string): void {
this.msg = "toggle " + this.unfinishedTasks;
if (this.unfinishedTasks === 0) {
router.pushUrl({
url: 'pages/Congrats',
params: { count: this.totalTasks.length }
}).catch((error: Error) => {
this.msg = 'error: ' + JSON.stringify(error);
});
}
}
每次这个变量发生变化时(在这里就是由子元素触发的变化),系统会调用一次回调函数,在函数中,我们判断这个变量的值是否减至 0
,若是,执行页面跳转。
3.3 页面跳转
页面跳转的部分,就完全是 Demo 的复刻。我们提供一个 Congrats.ets
页面,跳转时向它传递完成的任务总数,在页面中获取参数并打印出来。
@Entry
@Component
struct Congrats {
private count?: string = (router.getParams() as Record<string, string>)['count'];
build() {
Row() {
Column() {
Text('Great! U have finished ' + this.count + " tasks!")
.fontSize('38fp')
.textAlign(TextAlign.Center)
.padding({ left: 0.5 })
Blank()
Button('ok')
.fontSize('16fp')
.width('260vp')
.height('40vp')
.backgroundColor('#007DFF')
.onClick(() => {
router.back();
})
}
.width('100%')
.height('100%')
}
}
}
4 结果
第二个页面没有样式设计,凑合看~