任务案例实操文档

一、任务案例实操

在任务案例中,我们要实现任务的勾选、进度增加、以及滑动删除等效果

1、创建ArkTS

我们在编译器中创建一个ArkTS文件,文件命名为PropPage1,如下所示:

1.1页面初始化的准备

定义一个任务类,用来存储任务列表内容,设置静态变量id值为1,任务名字为string类型,使id不断增加,使用boolean类型判断任务状态是否完成


 

//任务类
class Task{
  static id: number = 1   //定义静态变量
  //任务名称
  name: string = `任务${Task.id++}`
  //任务状态:是否完成
  finished: boolean = false
}
1.1.2设置统一卡片样式,避免重复写复杂化,设置任务完成样式,设置字体颜色
//统一的卡片样式
@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')
}

1.2我们来定义常用变量

在页面中定义常用变量,定义一个totalTask,用来记录总任务量,定义一个finishTask,用来记录已完成的任务量,定义一个tasks,用来记录任务数组。
 

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

1.3进行页面初始化,删除多余代码
build() {
  Column({space:10}){
 
  }
  .width('100%')
  .height('100%')
  .backgroundColor('#F1F2F3')
}

2、进行页面的绘制

2.1定义统一卡片样式

根据页面要求,使用@Style封装card函数,统一样式,添加全局样式代码如下:

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

2.1.1绘制任务进度卡片,添加文本,设置进度条和宽度
//1.任务进度卡片
Row(){
  Text('任务进度:')
    .fontSize(30)
    .fontWeight(FontWeight.Bold)  //字体加粗

  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})  //设置外边距,上边距为20,下边距为10
.justifyContent(FlexAlign.SpaceEvenly)  //设置主轴,空间平均分配

2.1.2把进度条和文本放入层叠容器里面
  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)
    }

2.2设置新增任务案例按钮,添加点击事件,插入数据

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

2.2.1点击效果图片

2.3设置任务列表,利用foreach进行渲染

2.3.1写入foreach渲染,设置row布局和字体大小
ForEach(this.tasks,(item: Task, index) =>{
    Row(){
      Text(item.name)
        .fontSize(20)

}

}
2.3.2来添加一个复选框,设置固定为任务完成,在需要使得其勾选

Select 设置多选框是否完成,我们这里是否选中取决于我们的任务是否完成

Checkbox()
  .select(item.finished)

3.添加任务事件

3.1定义统计页面函数

handleTaskChange将更新任务总数量和更新已完成任务数量剪切到handleTaskChange中

handleTaskChange(){
  //2.更新任务总数量
  this.totalTask = this.tasks.length
  //2.更新已完成任务数量
  this.finishTask = this.tasks.filter(item => item.finished).length
}
3.2修改新增任务按钮中的更新任务总数量

调用handleTaskChange函数更新页面任务总数量和已完成任务数量统计,给button按钮添加如下代码:

//2.新增任务按钮
Button('新增任务')
  .width(200)
  .onClick(() =>{
    //1.新增任务数据
    this.tasks.push(new Task())
    //2.更新任务总数量
    this.handleTaskChange()
  })

3.3任务列表完成任务事件

给任务列表的Checkbox添加onChange事件,监听事件变化,根据选择状态更新当前任务状态,并调用handleTaskChange函数更新统计页面数据,在任务列表的Checkbox组件上添加如下代码:

.onChange(val => {
  //1.更新当前任务状态
  item.finished = val
  //2.更新已完成任务数量
  this.finishTask = this.tasks.filter(item => item.finished).length
})

3.4设置卡片的样式,让它放在两端美观
ForEach(
  this.tasks,
  (item: Task, index) =>{
    Row(){
      Text(item.name)
        .fontSize(20)
      Checkbox()
        .select(item.finished)
        .onChange(val => {
          //1.更新当前任务状态
          item.finished = val
          //2.更新已完成任务数量
          this.handleTaskChange()
        })
    }
    .card()
    .justifyContent(FlexAlign.SpaceBetween)
  }
)

3.5设置List

给任务列表添加list,间隙设置为10,foreach循环粘贴在list里面,因为在list里面不能直接写row,所以先用ListItem包裹

//3.任务列表
List({space:10}){
  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.handleTaskChange()
           })
       }
       .card()
       .justifyContent(FlexAlign.SpaceBetween)
     }
    }
  )
}
3.6给list设置布局居中、宽度、高度

4、列表左滑删除事件

4.1创建自定义构建函数DeleteButton,为列表左滑删除定义效果样式

@Builder DeleteButton(index: number){
  Button(){
    Image($r('app.media.shanchu1'))
      .fillColor(Color.White) //图标颜色
      .width(20)
  }
  .width(40)
  .height(40)
  .type(ButtonType.Circle)
  .backgroundColor(Color.Red)

用删除按钮,在ListItem组件上添加swipeAction属性,设置左滑效果,添加代码如下:

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

4.2使用margin设置删除按钮的边距
@Builder DeleteButton(index: number){
  Button(){
    Image($r('app.media.shanchu1'))
      .fillColor(Color.White) //图标颜色
      .width(20)
  }
  .width(40)
  .height(40)
  .type(ButtonType.Circle)
  .backgroundColor(Color.Red)
  .margin(5)

4.3设置点击事件,删除任务,更新任务总数量和已完成数量,调用handleTaskChange
.onClick(() =>{
  this.tasks.splice(index,1)
  this.handleTaskChange()
})

5、完整代码如下:


 

//任务类
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 PropPage1 {
  //总任务数量
  @State totalTask:  number = 0
  //已完成任务数量
  @State finishTask: number = 0
  //任务数组
  @State tasks: Task[] = []

  handleTaskChange(){
    //2.更新任务总数量
    this.totalTask = this.tasks.length
    //2.更新已完成任务数量
    this.finishTask = this.tasks.filter(item => item.finished).length
  }
  build() {
    Column({space:10}){
      //1.任务进度卡片
      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})  //设置外边距,上边距为20,下边距为10
      .justifyContent(FlexAlign.SpaceEvenly)  //设置主轴,空间平均分配
      //2.新增任务按钮
      Button('新增任务')
        .width(200)
        .onClick(() =>{
          //1.新增任务数据
          this.tasks.push(new Task())
          //2.更新任务总数量
          this.handleTaskChange()
        })
      //3.任务列表
      List({space:10}){
        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.handleTaskChange()
                 })
             }
             .card()
             .justifyContent(FlexAlign.SpaceBetween)
           }
            .swipeAction({end: this.DeleteButton(index) })

          }
        )
      }
      .width('100%')
      .layoutWeight(1)
      .alignListItem(ListItemAlign.Center)

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

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

6、优化代码

6.1任务卡片进度优化(@Prop)

首先创建定义TaskStatistics组件,将任务进度的卡片row里面的代码复制进来

@Component
struct  TaskStatistics{
   @Prop finishTask: number
   @Prop totalTask: number

  build() {
    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})  //设置外边距,上边距为20,下边距为10
    .justifyContent(FlexAlign.SpaceEvenly)  //设置主轴,空间平均分配

  }
}
6.1.1加入@State变为状态变量

定义组件,加入@State,定义finishTask变量和totalTask变量,初始化

@State finishTask: number
@State totalTask: number
6.1.2调用TaskStatistics组件

删除任务进度卡片,调用TaskStatistics,传入参数

//1.任务进度卡片
TaskStatistics({finishTask: this.finishTask, totalTask: this.totalTask})
上面的代码传递变量参数会显示报错,这时,我们需要再次回到子组件里面,因为父组件定义了@State ,所以我们在子组件里面组需要删除@state定义的变量
@Component
struct  TaskStatistics{

finishTask: number = 0
totalTask: number = 0

点击新增任务按钮,任务进度卡片任何显示,说明父子组件之间的同步失败了,进行以下操作

6.1.3@Prop的使用

单向同步,父组件的数据更改会会单向的在子组件里面更新,子组件的的数据更新, 不会同步到父组件里面,给子组件加上@Prop的装饰

@Prop finishTask: number = 0
@Prop totalTask: number = 0

刚加入的时候会报错,是因为@Prop装饰器不能在本地初始化,我们不能给它

赋值,不再初始化了

@Prop finishTask: number
@Prop totalTask: number 

运行效果如下所示

6.2任务列表优化(@Link,基本数据类型)

6.2.1将左滑动删除定义构造函数剪切到里面

由于build ()组件里面只能有一个根,所以这里的Button和List是不被允许的,则这里我们可以给其嵌套Column

//任务列表
@Component
struct TaskList {
  //总任务数量
  @State totalTask:  number = 0
  //已完成任务数量
  @State finishTask: number = 0
  //任务数组
  @State tasks: Task[] = []

  handleTaskChange(){
    //2.更新任务总数量
    this.totalTask = this.tasks.length
    //2.更新已完成任务数量
    this.finishTask = this.tasks.filter(item => item.finished).length
  }
  build() {
  Column(){
    //2.新增任务按钮
    Button('新增任务')
      .width(200)
      .onClick(() =>{
        //1.新增任务数据
        this.tasks.push(new Task())
        //2.更新任务总数量
        this.handleTaskChange()
      })

    //3.任务列表
    List({space:10}){
      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.handleTaskChange()
                })
            }
            .card()
            .justifyContent(FlexAlign.SpaceBetween)
          }
          .swipeAction({end: this.DeleteButton(index) })

        }
      )
    }
    .width('100%')
    .layoutWeight(1) //设置权重,让其剩余空间占满
    .alignListItem(ListItemAlign.Center)
  }

  }

//创建自主定义构造函数DeleteButton,为列表左滑删除定义效果样式
  @Builder DeleteButton(index: number){
    Button(){
      Image($r('app.media.shanchu1'))
        .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()
    })
  }
}
6.2.2对于@Link的使用(基本数据类型)

双向同步,父组件的数据更改会单向的在子组件里面更新,子组件的的数据更新,也会同步到父组件里面同样地,@Link修饰器也不能本地初始化

@Component
struct TaskList {
  //总任务数量
  @Link totalTask:  number
  //已完成任务数量
  @Link finishTask: number

初始化时必须带上$


 

//2.任务列表
TaskList({finishTask: $finishTask, totalTask: $totalTask})

6.3对于@Link的使用(对象类型)

6.3.1定义一个StatInfo,用来统计任务统计信息
//任务统计信息
class StatInfo {
  //总任务量
  totalTask:  number = 0
  已完成任务量
  finishTask: number = 0
}
6.3.2主页面中修改变量类型

定义StaInfo对象来替代原来totalTask和finishTask两个变量。

struct PropPage1 {
  //统计信息
  @State stat: StatInfo =  new StatInfo()

因为数据类型的改变,引用该值的地方会进行报错,以引用TaskStatistics自定义组件为参考,进行修改。

//1.任务进度卡片
TaskStatistics({finishTask: this.stat.finishTask, totalTask: this.stat.totalTask})
6.3.3修改子组件里面的变量
@Component
struct TaskList {
  //总任务数量
  @Link stat: StatInfo
  //任务数组
  @State tasks: Task[] = []
  handleTaskChange(){
    //2.更新任务总数量
    this.stat.totalTask = this.tasks.length
    //2.更新已完成任务数量
    this.stat.finishTask = this.tasks.filter(item => item.finished).length
  }
6.3.4对调用的内容进行修改
//2.任务列表
TaskList({stat: $stat})

6.4利用(@Provide,@Consume)优化代码

6.4.1分别将TaskStatistics和TaskList子组件中的stat对象,使用@Consume装饰。

按照任务将TaskStatistics自定义组件的参数修改成StaInfo对象类型,并修改相关引用报错。

6.4.2将主页面中stat对象从@ State装饰器修改成@Provide装饰器,代码如下:
//@State stat: StatInfo =  new StatInfo()

@Provide stat: StatInfo =  new StatInfo()
6.4.3分别将TaskStatistics和TaskList子组件中的stat对象,使用@Consume装饰,修改代码如下:
@Consume stat: StatInfo

//@Link stst: StatInfo

6.4.4修改子组件引用方式,修改代码如下:
//1.任务进度卡片
TaskStatistics()
 //2.任务列表
 TaskList()

此时使用@Provide和@Consume装饰后,达到了数据双向同步效果,此时不需要传递效果,依然能实现数据同步效果。

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

6.5.1在Task类上添加@Observed装饰器,代码如下:
//任务类
@Observed
class Task{
  static id: number = 1   //定义静态变量
  //任务名称
  name: string = `任务${Task.id++}`
  //任务状态:是否完成
  finished: boolean = false
}
6.5.2自定义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)
  }
}

6.5.3修改TaskList组件中的列表渲染代码

引用TaskItem自定义组件,传入参数及onTaskChange实现函数,代码如下:

ListItem(){
   TaskItem({item: item, onTaskChange:this.handleTaskChange.bind(this)})
 }
.swipeAction({end: this.DeleteButton(index) })

6.5.4此时点击列表完成,相应的完成效果就会展示。

@Observed@ObjectLink可实现嵌套类数据修改的双向同步,案例较为简单,大家可以修改Task类,进行类的嵌套,进一步查看效果。

7、整体完整代码


 

//任务类
@Observed
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')
}

//任务统计信息
class StatInfo {
  //总任务量
  totalTask:  number = 0
  已完成任务量
  finishTask: number = 0
}

@Entry
@Component
struct PropPage1 {
  //统计信息
  //@State stat: StatInfo =  new StatInfo()
  @Provide stat: StatInfo =  new StatInfo()
 
  build() {
    Column({space:10}){
      //1.任务进度卡片
     TaskStatistics()
      //2.任务列表
      TaskList()
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#F1F2F3')
  }
}


@Component
struct  TaskStatistics{
  @Consume stat: StatInfo

  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.stat.finishTask.toString())
            .fontSize(24)
            .fontColor('#36D')
          Text(' / ' + this.stat.totalTask.toString())
            .fontSize(24)
        }
      }
    }
    .card()
    .margin({top: 20, bottom:10})  //设置外边距,上边距为20,下边距为10
    .justifyContent(FlexAlign.SpaceEvenly)  //设置主轴,空间平均分配

  }
}

@Component
struct TaskList {
  //总任务数量
  @Consume stat: StatInfo
  //@Link stst: StatInfo
  //任务数组
  @State tasks: Task[] = []

  handleTaskChange(){
    //2.更新任务总数量
    this.stat.totalTask = this.tasks.length
    //2.更新已完成任务数量
    this.stat.finishTask = this.tasks.filter(item => item.finished).length
  }
  build() {
  Column(){
    //2.新增任务按钮
    Button('新增任务')
      .width(200)
      .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)
  }

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


@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)
  }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值