Advertisement

【鸿蒙】任务列表案例

阅读量:

任务列表案例

  • 页面初始化数据准备。

1.1定义任务实体类

定义Task类,用于存放序列号、名称、选中状态。

class Task {
static id: number = 1
name: string = 任务${Task.id++}
finished: boolean = false
}

1.2定义常用变量

定义以下三个变量,totalTask为任务总量、finishTask为已完成任务量、Task为任务数组。

//任务总量
@State totalTask: number = 0
//以完成的任务数量
@State finishTask: number = 0
//任务数组
@State tasks: Task[] = []

1.3初始化界面

初始界面,删除无用代码,保留以下代码:

build() {
Column({ space: 8 }) {

}
.width("100%")
.height("100%")
.backgroundColor("#F1F2F3")
}

  • 绘制页面

2.1定义卡片样式

使用@Styles封装card组件,用于统一卡片样式

//统一的卡片样式
@Styles
function card() {
.width("95%")
.padding(20)
.backgroundColor(Color.White)
.borderRadius(15)
.shadow({ radius: 6, color: "#1F000000", offsetX: 2, offsetY: 4 })//阴影
}

2.2定义新增任务选中样式

使用@Extend封装finishedTask组件,用于统一任务选中样式

//任务完成样式
@Extend(Text)
function finishedTask() {
.decoration({ type: TextDecorationType.LineThrough })
.fontColor("#B1B2B1")
}

2.3绘制任务进度显示卡片

在Column容器中绘制任务进度显示卡片

Row() {
Text("任务进度:")
.fontSize(30)
.fontWeight(FontWeight.Bold)
Stack() {
Progress({
value: this.finishTask,
total: this.totalTask,
type: ProgressType.Ring
})
.width(100)
Row() {
Text(this.finishTask.toString())
.fontSize(24)
.fontColor("#36D")
Text(" / " + this.totalTask.toString())
.fontSize(24)
}
}
}.card().margin({ top: 20, bottom: 10 })
.justifyContent(FlexAlign.SpaceEvenly)

2.4绘制新增任务按钮

依次绘制新增任务按钮,用于实现点击新增任务效果。

//2.新增任务按钮
Button("新增任务")
.width(200)

.onClick(() => {
//1.新增任务数据
this.tasks.push(new Task())
//2.更新任务总数量
this.totalTask = this.tasks.length
})

2.5绘制任务列表

依次绘制任务列表,使用Tasks列表数据渲染出List页面。

//3.任务列表
List({space:8}) {
ForEach(
this.tasks,
(item:Task,index)=>{
ListItem(){
Row(){
Text(item.name)
.fontSize(20)
Checkbox()
.select(item.finished)
.onChange(Val => {
//1.更新当前任务的状态
item.finished=Val
//2.更新已完成任务数量
this.finishTask=this.tasks.filter(item=>item.finished).length
})
}.card()
.justifyContent(FlexAlign.SpaceBetween)
}
}
)
}.width("100%").alignListItem(ListItemAlign.Center).layoutWeight(1)

2.6列表左划删除

2.6.1绘制删除按钮

构建自定义函数DeleteButton,用于封装删除按钮样式。

//定义自定义构建函数,用于封装删除按钮组件。
@Builder DeleteButton(index:number){
Button(){
Image($r("app.media.shanchu"))
.width(24)
}.width(40)
.height(40)
.type(ButtonType.Circle)
.backgroundColor(Color.Red).margin(5)
.onClick(()=>{
this.tasks.splice(index,1)
this.totalTask = this.tasks.length
this.finishTask=this.tasks.filter(item=>item.finished).length
})
}

2.6.2设置左划效果

在ListItem组件上引用删除按钮,添加swipeAction属性,设置左滑效果。

ListItem(){}.swipeAction({end:this.DeleteButton(index)})

三、对组件进行封装优化处理

入口组件为父组件,引用父组件的就叫子组件。

父子组件需要数据传递时,就需要@Prop和@Link装饰器

首先,我们要新增一个自定义组件,将任务统计组件放到里面。

@Component

struct TaskStatistics {

build() {

Row(){

Text('任务进度:')

.fontSize(30)

.fontWeight(FontWeight.Bold)

Stack(){

Progress({

value: this.stat.finishTask,

total: this.stat.totalTask,

type: ProgressType.Ring

})

.width(100)

Row(){

Text(this.finishTask.toString())

.fontSize(24)

.fontColor('#36D')

Text(' / ' + this.totalTask.toString())

.fontSize(24)

}

}

}

.card()

.margin({top: 5, bottom: 10})

.justifyContent(FlexAlign.SpaceEvenly)

}

}

然后为了能够让组件内容跟随状态的变化,引起自动刷新。

3.1将任务进度卡片封装成自定义组件:@Prop

所以我们将finishTask和totalTask定义成@state状态

@State totalTask: number = 0

@State finishTask: number = 0

然后,我们就可以直接在任务进度这些代码原位置上直接调用这个自定义好的组件。

build() {

Column({space: 10}){

Header()

// 1.任务进度卡片

TaskStatistics()

那么,PropPage就变成了父组件,TaskStatistics()就变成了子组件。

然后,应该让父组件的参数传给子组件让他渲染,所以,我们在子组件TaskStatistics()中传递一下。

TaskStatistics({finishTask: this.finishTask, totalTask: this.totalTask})

然后报错

由于,父组件中有@state,子组件也有,所以我们把子组件中的@State删掉。

所以我们就用@Prop和@Link装饰器

先让父组件给子组件单向传递,所以我们用@Prop

@Prop totalTask: number = 0

@Prop finishTask: number = 0

4.2把新增按钮和任务列表封装成组件:@Link

然后我们新增一个组件

//任务列表

@Component

struct TaskList {

}

然后,将任务按钮和任务列表粘贴进来

build() {

Column(){

// 2.新增任务按钮

Button('新增任务')

.width(200)

.margin({bottom: 10})

.onClick(() => {

// 1.新增任务数据

this.tasks.push(new Task())

// 2.更新任务总数量

this.handleTaskChange()

})

// 3.任务列表

List({space: 10}){

ForEach(

this.tasks,

(item: Task, index) => {

ListItem(){

TaskItem({item: item, onTaskChange: this.handleTaskChange.bind(this)})

}

.swipeAction({end: this.DeleteButton(index)})

}

)

}

.width('100%')

.layoutWeight(1)

.alignListItem(ListItemAlign.Center)

}

.layoutWeight(1)

}

然后把与父组件相联的函数也一并粘贴进来。

// 任务数组

@State tasks: Task[] = []

handleTaskChange(){

// 1.更新任务总数量

this.stat.totalTask = this.tasks.length

// 2.更新已完成任务数量

this.stat.finishTask = this.tasks.filter(item => item.finished).length

}

然后!还有删除按钮的一个自定义构建函数,也一并粘贴进来

@Builder DeleteButton(index: number){

Button(){

Image($r('app.media.ic_public_delete_filled'))

.fillColor(Color.White)

.width(20)

}

.width(40)

.height(40)

.type(ButtonType.Circle)

.backgroundColor(Color.Red)

.margin(5)

.onClick(() => {

this.tasks.splice(index, 1)

this.handleTaskChange()

})

}

然后,我们还要注意,自定义组件的根节点的问题 ,所以我们用column容器封装进去

Column(){

// 2.新增任务按钮

Button('新增任务')

.width(200)

.margin({bottom: 10})

.onClick(() => {

// 1.新增任务数据

this.tasks.push(new Task())

// 2.更新任务总数量

this.handleTaskChange()

})

// 3.任务列表

List({space: 10}){

ForEach(

this.tasks,

(item: Task, index) => {

ListItem(){

TaskItem({item: item, onTaskChange: this.handleTaskChange.bind(this)})

}

.swipeAction({end: this.DeleteButton(index)})

}

)

}

.width('100%')

.layoutWeight(1)

.alignListItem(ListItemAlign.Center)

}

.layoutWeight(1)

然后加一个权重

然后 父亲的数据传给儿子,然后儿子的数据同时需要传给父亲,父子同时需要做数据修改的时候,就需要双向传递

@Link totalTask: number

@Link finishTask: number

然后初始化 得用$

TaskStatistics({finishTask:finishTask, totalTask: totalTask})

这样就做到了双向操作

然后我们将父组件中的基础数据类型定义成一个其他的类

// 任务统计信息

class StatInfo {

totalTask: number = 0

finishTask: number = 0

}

然后将父组件中的类型直接修改成StatInfo类型。

@State stat: StatInfo = new StatInfo()

再将任务进度卡片的属性取名对象修改一下

TaskStatistics({finishTask:this.stat.finishTask, totalTask: this.stat.totalTask})

Link是可以传对象的,所以把任务列表的属性和更新事件也做一下修改

//2.任务列表

TaskList({stat: $stat})

handleTaskChange(){

//更新任务总数量

this.stat.totalTask = this.tasks.length

//更新已完成任务数量

this.stat.finishTask = this.tasks.filter(item => item.finished).length

}

3.2 实现数据同步@privide 、@consume

Provide和Consume可以跨组件提供类似于@State和@Link的双向同步

先对父组件进行修改

@Entry
@Component
struct PropPage {
// 统计信息
@Provide stat: StatInfo = new StatInfo()

然后将子组件的@Link改成@Consume

@Component
struct TaskStatistics {
@Consume stat: StatInfo

修改子组件引用方式,修改代码如下:

//1.任务进度卡片

TaskStatistics()

//2.任务列表

TaskList()

3.3点击完成效果实现(@Observed@ObjectLink)

使用@extend装饰器来做样式的扩展。

@Extend(Text) function finishedTask(){
.decoration({type:TextDecorationType.LineThrough})
.fontColor('#B1B2B1')
}

在Task类上添加@Observed装饰器,代码如下:

//任务类

@Observed

class Task{

static id:number=1

//任务名称

name:string=任务${Task.id++}

//任务状态:是否完成

finished:boolean=false

}

自定义TaskItem类,使用@ObjectLink装饰变量item,定义onTaskChange事件,该事件实现方式由引用处传递,添加代码如下:

@Component

struct TaskItem {

@ObjectLink item:Task

onTaskChange:()=>void

build() {

Row(){

if(this.item.finished){

Text(this.item.name)

.finishedTask()

}else {

Text(this.item.name)

}

Checkbox()

.select(this.item.finished)

.onChange(val=>{

//1.更新当前任务状态

this.item.finished=val

//2.更新已完成任务数量

this.onTaskChange()

})

}

.card()

.justifyContent(FlexAlign.SpaceBetween)

}

}

4.5.3 修改TaskList组件中的列表渲染代码,引用TaskItem自定义组件,传入参数及onTaskChange实现函数,代码如下:

ListItem(){

TaskItem({ item:item ,onTaskChange:this.handleTaskChange.bind(this)})

} .swipeAction({end:this.DeleteButton(index)})

全部评论 (0)

还没有任何评论哟~