2023黑马程序员鸿蒙课程-状态管理-任务统计案例

话不多说,我们先展示一下运行效果嘿嘿

我们可以随便添加新的任务,可以勾选任务,也可以随时向左划去删除该任务: 

首先呢,我们得先创建一个任务类,初始值我们定义为布尔值boolean = false,我们通过改变布尔值的方式去修改任务的状态.

这是代码:

// 任务类
class Task{
  static id: number = 1
  // 任务名称
  name: string = `任务${Task.id++}`
  // 任务状态: 是否完成
  finished: boolean = false
}

接下来这个很好理解, 去设置一个@Style, 控制每一个任务的样式,这样可以让我们的代码更为简洁。  我们一会需要的时候,直接调用这个函数去修饰每个任务小板块的样式就可以了。

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

接下来我们直接初始化这些变量。   finishedTask 其实是左边这个数字, totalTask是右边的这个数字。 当我们增加删除任务的时候,进度条也会随之改变。

@Entry
@Component
struct PropPage{
  // 总任务数量
  @State totalTask: number = 0
  // 已完成任务数量
  @State finishedTask: number = 0
  // 任务数组
  @State tasks: Task[] = []             
}

每一次改变,我们都会触发这个函数。

// 任务修改时候触发的一个函数
handleTaskChange(){
  // 2. 更新任务总数量
  this.totalTask = this.tasks.length
  // 2. 更新已经完成函数的数量
  this.finishedTask = this.tasks.filter(item=>item.finished).length
}

首先我们先写任务进程部分, 因为要逐一解释一些关键字,所以我代码注释写的非常清楚,这里我就不过多解释了:

  // 现在我用这个容器 Stack(){.......} 将我的进度条以及这个百分比的进度值堆在一起, 这样进度条就和进度值重叠了。
  Stack(){
    // 进度条的写法是 Progress({.......})  在这里我选的是环形进度条
    Progress({
      value:this.finishedTask,
      total:this.totalTask,
      type: ProgressType.Ring
    })
      .width(100)      

    // 因为我用了 .justifyContent(FlexAlign.SpaceEvenly) 这个属性,它是用于平均分配的。 假设
    // 这里我不使用 Row() 再去把这个进度数字弄成一行,会导致百分比左右两边的数字隔的比较远。
    Row(){
      Text(this.finishedTask.toString())
        .fontSize(24)
        .fontColor('#36D')
      Text(' / '+this.totalTask.toString())
        .fontSize(24)
    }
  }


}
.card()
.margin({top:20,bottom:30})
.justifyContent(FlexAlign.SpaceEvenly)

接着我们新增按钮,当按一下按钮,我们就可以新增一个任务。当然这个按钮新增完数据后,紧跟着就得更改进度条,让其保持一致性。   更改进度条方法很简单,调用刚刚写好的那个 handleTaskChange 函数就行了。

// 2.新增任务按钮
Button('新增任务')
  .width(200)
  .onClick(()=>{
    // 1. 新增任务数据
    this.tasks.push(new Task())
    // 2. 更新任务总数量
    // this.totalTask = this.tasks.length
    // 上面这句省略了,增加函数的复用性, 专门写了个函数让我们来调用,功能和上面一样
    this.handleTaskChange()
  })

好了,我们重头戏就在这里。 我的注释这块写的非常非常详细,大家有问题可以仔细阅读。

  // 3. 任务列表, 遍历每一个新增的任务
    // 第一假如新增任务溢出屏幕,我们必须使用List容器
    // 第二 我们要滑动删除新增功能,也需要这个功能
    List({space:10}){
      ForEach(
        this.tasks,
        (item: Task, index) => {
          // List里面千万千万不可以直接写 Row(){} 必须用ListItem(){} 包裹着它,否则一定会报错。我遇到很多次了!!!
          ListItem(){
            Row(){
              Text(item.name)
                .fontSize(20)
              // 这里我用来一个复选框
              Checkbox()
                // 默认值是False,   finished: boolean = false  说明没有选中。 如果选中则修改默认值, 用布尔值去判断
                .select(item.finished,)
                  // onchange 是人为的去做勾选, 或者取消勾选
                .onChange(val => {
                  // 1.更新当前任务状态
                  item.finished = val
                  // 2. 更新已经完成函数的数量
                  // this.finishedTask = this.tasks.filter(item=>item.finished).length
                  // 上面这句省略了,增加函数的复用性, 专门写了个函数让我们来调用,功能和上面一样
                  this.handleTaskChange()
                })
            }
            // 给予每一个新增任务1属性
            .card()
            // 修改每一个新增的任务卡片布局,让其每行在手机正中间
            .justifyContent(FlexAlign.SpaceBetween)
          }
          // 在ListItem后面去跟一个函数,具体可以查官方文档
          // 建议用局部函数, 因为我们要操作外面定义的数组,更方便
          .swipeAction({end:this.DeleteButton(index)})

        }
      )
    }
    .width('100%')
    //任务列表必须设置高度,否则没办法往下拉。  但不能写成.height('100%') 这样没有效果。
    .layoutWeight(1)
    // 让任务列表居中
    .alignListItem(ListItemAlign.Center)




  }


  // 这是Column样式
  .width('100%')
  .height('100%')
  .backgroundColor('#F1F2F3')
}

最后一部分我们写最后一个功能,那就是删除按钮,也很重要的哈,向左划每个任务条,就能看到删除按钮,点击删除就好了, so easy!

@Builder DeleteButton(index:number){
  Button(){
    Image($r('app.media.return'))
      .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()
  })
}

以上是每段代码的解析,我知道有些繁琐, 我接下来会把整个代码给大家,直接复制粘贴到编译器就可以实现了。

// 任务类
class Task{
  static id: number = 1
  // 任务名称
  name: string = `任务${Task.id++}`
  // 任务状态: 是否完成
  finished: boolean = false
}

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

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

@Entry
@Component
struct PropPage{
  // 总任务数量
  @State totalTask: number = 0
  // 已完成任务数量
  @State finishedTask: number = 0
  // 任务数组
  @State tasks: Task[] = []

  // 任务修改时候触发的一个函数
  handleTaskChange(){
    // 2. 更新任务总数量
    this.totalTask = this.tasks.length
    // 2. 更新已经完成函数的数量
    this.finishedTask = this.tasks.filter(item=>item.finished).length
  }

  build() {

    Column({space:10}){
      Row(){
        Text('任务进程')
          .fontSize(30)
          .fontWeight(FontWeight.Bold)


        // 现在我用这个容器 Stack(){.......} 将我的进度条以及这个百分比的进度值堆在一起, 这样进度条就和进度值重叠了。
        Stack(){
          // 进度条的写法是 Progress({.......})  在这里我选的是环形进度条
          Progress({
            value:this.finishedTask,
            total:this.totalTask,
            type: ProgressType.Ring
          })
            .width(100)

          // 因为我用了 .justifyContent(FlexAlign.SpaceEvenly) 这个属性,它是用于平均分配的。 假设
          // 这里我不使用 Row() 再去把这个进度数字弄成一行,会导致百分比左右两边的数字隔的比较远。
          Row(){
            Text(this.finishedTask.toString())
              .fontSize(24)
              .fontColor('#36D')
            Text(' / '+this.totalTask.toString())
              .fontSize(24)
          }
        }


      }
      .card()
      .margin({top:20,bottom:30})
      .justifyContent(FlexAlign.SpaceEvenly)


      // 2.新增任务按钮
      Button('新增任务')
        .width(200)
        .onClick(()=>{
          // 1. 新增任务数据
          this.tasks.push(new Task())
          // 2. 更新任务总数量
          // this.totalTask = this.tasks.length
          // 上面这句省略了,增加函数的复用性, 专门写了个函数让我们来调用,功能和上面一样
          this.handleTaskChange()
        })

      // 3. 任务列表, 遍历每一个新增的任务
      // 第一假如新增任务溢出屏幕,我们必须使用List容器
      // 第二 我们要滑动删除新增功能,也需要这个功能
      List({space:10}){
        ForEach(
          this.tasks,
          (item: Task, index) => {
            // List里面千万千万不可以直接写 Row(){} 必须用ListItem(){} 包裹着它,否则一定会报错。我遇到很多次了!!!
            ListItem(){
              Row(){
                Text(item.name)
                  .fontSize(20)
                // 这里我用来一个复选框
                Checkbox()
                  // 默认值是False,   finished: boolean = false  说明没有选中。 如果选中则修改默认值, 用布尔值去判断
                  .select(item.finished,)
                    // onchange 是人为的去做勾选, 或者取消勾选
                  .onChange(val => {
                    // 1.更新当前任务状态
                    item.finished = val
                    // 2. 更新已经完成函数的数量
                    // this.finishedTask = this.tasks.filter(item=>item.finished).length
                    // 上面这句省略了,增加函数的复用性, 专门写了个函数让我们来调用,功能和上面一样
                    this.handleTaskChange()
                  })
              }
              // 给予每一个新增任务1属性
              .card()
              // 修改每一个新增的任务卡片布局,让其每行在手机正中间
              .justifyContent(FlexAlign.SpaceBetween)
            }
            // 在ListItem后面去跟一个函数,具体可以查官方文档
            // 建议用局部函数, 因为我们要操作外面定义的数组,更方便
            .swipeAction({end:this.DeleteButton(index)})

          }
        )
      }
      .width('100%')
      //任务列表必须设置高度,否则没办法往下拉。  但不能写成.height('100%') 这样没有效果。
      .layoutWeight(1)
      // 让任务列表居中
      .alignListItem(ListItemAlign.Center)




    }


    // 这是Column样式
    .width('100%')
    .height('100%')
    .backgroundColor('#F1F2F3')
  }


  @Builder DeleteButton(index:number){
    Button(){
      Image($r('app.media.return'))
        .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()    
    })
  }

}

代码的注释我写的非常非常详细,不懂的童鞋要仔细看哈!!!

哦对了,这是我删除键引用的一个小图片,如果你懒的找话可以拿去用,命名为:return.png ,放在 app.media.retur 文件夹下面。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值