深入鸿蒙开发:掌握ArkTS中的关键装饰器

深入鸿蒙开发:掌握ArkTS中的关键装饰器

@Styles装饰器

介绍

@styles装饰器主要用于将样式绑定到组件上,通过使用这个装饰器,你可以定义一组样式,并将这些样式应用到特定的组件上,这种方式使得样式管理更加模块化和可重用,只负责在当前页面的复用。

组件内公共样式有两种方案:

  1. 组件内的定义。
  2. 组件外的定义。

组件内部定义的语法:

// 定义
@Styles boxStyles(){
   .width(100)
   .height(100)
   .backgroundColor(Color.Red)
   .borderRadius(20)
 }
 // 使用
 Column(){}.boxStyles()

注意:
不是所有的样式都可以提取到@Styles 装饰器中,一般默认是容器的布局样式布局属性是可以提取到@Styles 装饰其中,但是字体或者文本样式不支持提取。
使用案例:

@Entry
@Component
struct Page09_AddTask{
// 定义
  @Styles boxStyles(){
    .width(100)
    .height(100)
    .backgroundColor(Color.Red)
    .borderRadius(20)
  }
  build(){
    Column(){
  // 标题模块
      Text("新建任务")
        .fontSize(26)
        .fontWeight(FontWeight.Bold)
        .fontColor($r("app.color.text_color"))
        .margin({bottom:20})
      // 使用
        Column(){}.boxStyles()
    }
    .width("100%")
    .height("100%")
    .padding(10)
    .backgroundColor("#f1f3f5")
  }
}

效果:

 深入理解ArkTS:从装饰器到条件渲染

组件外部定义的语法:

// 定义
@Styles function boxStyles(){
  .width(100)
    .height(100)
    .backgroundColor(Color.Red)
    .borderRadius(20)
}

// 使用
 Column(){}.boxStyles()

组件外部定义需要多加一个function语法。

总结:

  1. 布局容器的公共样式可以用Styles来进行定义。
  2. 公共样式提取放在组件内部,也可以放在组件外部,但是必须同一个文件中。
  3. 虽然你可以理解为@Styles定义公共样式实际上就是抽取函数出来,但是它不支持参数。

@Extend装饰器

介绍

@Extend装饰器主要用于对鸿蒙原生组建的样式或者事件进行扩展,你可以使用这个装饰器对原生的组件进行重定义,然后在页面中复用,实际上类似于@Styles装饰器的效果。必须指定你对哪个原生组件进行扩展。

定义代码格式如下:

// 定义Text组件的扩展样式
@Extend(Text) function TextStyles(){
  .fontSize(16)
  .fontColor($r("app.color.add_title_color"))
}
@Extend(TextInput) function InputStyles(){
  .borderRadius(0)
  .fontWeight(FontWeight.Bold)
  .backgroundColor("#f1f3f5")
  .placeholderColor($r("app.color.text_color"))
}
@Extend(Line) function LineStyles(value:string = "100%"){
  .width(value)
  .height(1)
  .backgroundColor($r("app.color.add_title_color"))
}

使用格式如下:

Text("标题").TextStyles()
TextInput({placeholder:"请输入任务名字"}).InputStyles()
Line().LineStyles()
@Entry
@Component
...
...

案例代码:

@Styles
function boxStyles() {
  .width(100)
  .height(100)
  .backgroundColor(Color.Red)
  .borderRadius(20)
}

// 定义Text组件的扩展样式
@Extend(Text)
function TextStyles() {
  .fontSize(16)
  .fontColor($r("app.color.add_title_color"))
}

@Extend(TextInput)
function InputStyles() {
  .borderRadius(0)
  .fontWeight(FontWeight.Bold)
  .backgroundColor("#f1f3f5")
  .placeholderColor($r("app.color.text_color"))
}

@Extend(Line)
function LineStyles(value: string = "100%") {
  .width(value)
  .height(1)
  .backgroundColor($r("app.color.add_title_color"))
}

@Entry
@Component
struct Page09_AddTask {
  build() {
    Column() {
      // 标题模块
      Text("新建任务")
        .fontSize(26)
        .fontWeight(FontWeight.Bold)
        .fontColor($r("app.color.text_color"))
        .margin({ bottom: 20 })
      // 主体内容
      Column() {
        // 存放标题的模块
        Column() {
          Text("标题")
            .TextStyles()
          TextInput({ placeholder: "请输入任务名字" })
            .InputStyles()
          Line().LineStyles()
        }
      }
    }
    .width("100%")
    .height("100%")
    .padding(10)
    .backgroundColor("#f1f3f5")
  }
}

案例效果:
 深入理解ArkTS:从装饰器到条件渲染

总结:

  1. @Extend来进行样式扩展,针对指定的组件进行样式的封装、重构、让页面样式作用更加精准。
  2. @Extend来扩展样式,解决@Styles无法传递参数的问题。

@State装饰器

介绍

ArkTs中,@State装饰器用于定义状态变量。状态变量是组件内部的状态,他们的变化会触发组件的从新渲染,反应最新的状态值,这种机制可以使得UI可以根据应用状态自动更新,简化界面与数据同步的复杂度。

定义代码格式如下:

@Entry
@Component
struct Page10_State {
  @State message:string = "hello world"
  username:string = "WuYouke"
  build() {
    Column(){

    }
    .width("100%")
    .height("100%")
  }
}

用@State装饰器定义的变量,代表组件的内部状态,只要这个变量发生变化,页面UI会进行重新渲染。

特点

  1. @State来修饰变量,必须要求变量要进行初始化,不能为空值。
  2. @State来修饰变量,可以支持Objectclassstringnumberboolean等等。
  3. 如果出现嵌套类型的数据结构,嵌套对象的属性发生变化,页面无法检测更新。
  4. 标记为@State的变量默认都是私有变量,只能在组件中访问,不能在外面访问。

关于复杂类型数据定义

import { UserModel } from "../viewmodel/UserModel"
@Entry
@Component
struct Page10_State {
  @State message:string = "hello world"
  @State user:UserModel = new UserModel(1,"WuYouKe")
  @State users:UserModel[] = [new UserModel(2,"WuQi"),new UserModel(2,"WuMan")]
  @State Users2:Array<UserModel> = [new UserModel(2,"WuLun"),new UserModel(2,"ZhangYongfang")]
  build() {
    Column(){
    }
    .width("100%")
    .height("100%")
  }
}

其中UserModel代码为:

export class UserModel{
  id:number
  name:string
  constructor(id: number, name: string) {
    this.id = id
    this.name = name
  }
}

总结

页面上要用到数据,并不是所有数据都需要大家采用@State来修饰,有些变量无需采用@State来修饰的,只需要在页面加载一次,直接定义就可以了。

@Prop装饰器

前言

鸿蒙开发中,涉及到父子组件通信,我们需要用到@Prop装饰器,子组件可以使用这个数据进行渲染,但是修改子组件的传递至,父组件无法同步。【前端框架中,一般都是单向数据流】

方案一:格式如下

子组件中定义了一个变量,这个变量默认初始化,也可以接受父组件传递过来的值。

struct Children{
	private title:string = "title"
	build{
		Text(`${this.title}`)
	}
}

负组件中调用子组件传递参数

struct Parent{
	build(){
		Children({title:"Harmony OS")
	}
}

总结:

  1. 只要父组件中传递子组件传递的数据过去,字段和子组件内容同名,默认会覆盖组件的数据。
  2. 子组件基本变量,能够接收数据,但是无法实现修改页面更新。
  3. 适用于页面中标题、尺寸、这些内容不变,也无需在页面引起数据的动态渲染。

方案二:格式如下

采用单向数据流来接受数据变化,子组件代码如下,@Prop修饰不需要初始化。

struct Children{
	@Prop title:string = "title"
	build{
		Text(`${this.title}`)
	}
}

负组件中调用子组件传递参数

struct Parent{
	build(){
		Children({title:"Harmony OS")
	}
}

 深入理解ArkTS:从装饰器到条件渲染

总结:

  1. @Prop只能在@Component组件中使用,不能@Entry组件中使用。
  2. @Prop来定义变量,可以默认不初始化,本来就是要接收外部数据。
  3. @Prop来定义数据,可以进行修改,但是只能影响本组件,无法同步给父组件。

@prop为单项数据流:
 深入理解ArkTS:从装饰器到条件渲染

@Link装饰器

前言:

双向数据通信,父组件传递值给子组件,子组件可以修改传递过去这个值,同步将内容修改完成传递到父组件。

使用格式如下:

父组件代码:

import { UserModel } from "../viewmodel/UserModel"
import { Children } from "../pages/Children"
@Entry
@Component
struct Page10_State {
  @State user:UserModel = new UserModel(1,"WuYouke")
  build() {
    Column(){
      Text(`父组件 - 编号:${this.user.id}、姓名:${this.user.name}`)
      Button("父组件按钮").onClick((event: ClickEvent) => {
        this.user.name = "WuYouke------"
      })
      Children({user:this.user})

    }
    .width("100%")
    .height("100%")
  }
}

子组件代码:

import { UserModel } from "../viewmodel/UserModel"

@Component
export struct Children {
  @Link user:UserModel;
  build() {
    Column(){
      Text(`子组件 - 编号:${this.user.id}、姓名:${this.user.name}`)
      Button("子组件按钮").onClick((event: ClickEvent) => {
        this.user.name = "吴又可"
      })
    }
    .width("100%")
    .height("100%")
  }
}

 深入理解ArkTS:从装饰器到条件渲染

总结:

  1. @Link只能在@Component组件中使用,不能在@Entry组件中使用。
  2. @Link来定义变量,可以默认不初始化,本来代表接收外部数据。
  3. @Link来定义数据,可以进行修改,不管父组件还是子组件数据都是同步变化。

@ObjectLink与@Observed装饰器:

前言:

在鸿蒙开发中,会使用@State来装饰我们变量,一旦变量发生变化页面更新,遇到了复杂的数据结构,数据更新会有差别,比如嵌套对象,数组对象。

  • 嵌套对象:如果修改时内层对象的属性,默认页面无法检测更新。 - 数组对象:修改数组页面都能检测到变化,修改数组里面的对象属性,页面无法检测更新。

嵌套对象修改无法监听更新案例:

比如如下效果:只能修改第一层的属性值,不能修改更深层的值,如果修改更深层则需要重新new一个对象进行替换。
边实战边学习一、:掌握ArkTS中的关键装饰器
代码如下:

class Pet {
  id: string = ""
  name: string = ""
  type: string = ""

  constructor(id: string, name: string, type: string) {
    this.id = id
    this.name = name
    this.type = type
  }
}

class Student {
  id: string = ""
  name: string = ""
  pet: Pet

  constructor(id: string, name: string, pet: Pet) {
    this.id = id
    this.name = name
    this.pet = pet
  }
}

@Entry
@Component
struct Page14_ObjectLink {
  @State stu: Student = new Student("1", "王何凤", new Pet("001", "快快", "猫"))

  aboutToAppear(): void {
    console.log(JSON.stringify(this.stu))
  }

  build() {
    Column() {
      Text(`学生信息为:${JSON.stringify(this.stu)}`).fontSize(25)
      Button("修改学生名字").onClick((event: ClickEvent) => {
        this.stu.name = "吴用"
      })
      Button("修改学生宠物名字").onClick((event: ClickEvent) => {
        this.stu.pet.name = "乐乐"
      })
      Button("通过new新宠物替换修改学生宠物名字").onClick((event: ClickEvent) => {
        const temp = new Pet("001", "乐乐", "猫")
        this.stu.pet = temp
      })
    }
    .height('100%')
    .width('100%')
  }
}

对象数组无法监听到更新案例:

对象数组进行数据循环遍历的时候,如果直接操作数组中的元素,数组变化能引起页面更新,比如新增何删除能引起页面更新,但是你操作数组里的对象属性无法检测更新。
边实战边学习一、:掌握ArkTS中的关键装饰器
代码如下:

class Pet {
  id: string = ""
  name: string = ""
  type: string = ""

  constructor(id: string, name: string, type: string) {
    this.id = id
    this.name = name
    this.type = type
  }
}

class Student {
  id: string = ""
  name: string = ""
  pet: Pet

  constructor(id: string, name: string, pet: Pet) {
    this.id = id
    this.name = name
    this.pet = pet
  }
}

@Entry
@Component
struct Page14_ObjectLink {
  @State stu: Student = new Student("1", "王何凤", new Pet("001", "快快", "猫"))
  @State school: Array<Student> = [
    new Student("001", "吴用", new Pet("001", "快快", "猫")),
    new Student("001", "王何凤", new Pet("002", "乐乐", "猫")),
    new Student("001", "吴又可", new Pet("003", "饼干", "鼠")),
  ]

  aboutToAppear(): void {
    console.log(JSON.stringify(this.stu))
  }

  build() {
    Column() {
      Button("+添加学生信息").onClick(() => {
        const tempDate = new Student("004", "王何凤", new Pet("004", "小灰灰", "兔子"))
        this.school.push(tempDate)
      }).height(20).fontSize(14).margin({ bottom: 10 })
      ForEach(this.school, (item: Student, index: number) => {
        Text(`姓名 :${item.name},   宠物名称 :${item.pet.name},   宠物类型 :${item.pet.type}`)
          .fontColor(Color.Brown)
        Row() {
          Button("修改").onClick(() => {
            item.name = "默认名称"
          }).width(70).height(20).fontSize(14)
          Button("删除")
            .onClick(() => {
              this.school.splice(index, 1)
            })
            .width(70)
            .height(20)
            .fontSize(14)
            .backgroundColor(Color.Red)
        }
      }, (item: Student) => item.id)
    }
    .height('100%')
    .width('100%')
    .alignItems(HorizontalAlign.Start)
    .justifyContent(FlexAlign.Start)
  }
}

数组对象深度更新解决方案:

studentModel.ets

export class Pet {
  id: string = ""
  name: string = ""
  type: string = ""

  constructor(id: string, name: string, type: string) {
    this.id = id
    this.name = name
    this.type = type
  }
}

@Observed
export class Student {
  id: string = ""
  name: string = ""
  pet: Pet

  constructor(id: string, name: string, pet: Pet) {
    this.id = id
    this.name = name
    this.pet = pet
  }
}

在父组件中:

import { Pet, Student } from "../viewmodel/StudentModel"
import { ObjectLinkList } from "../view/ObjectLinkList"

@Entry
@Component
struct Page14_ObjectLink {
  @State school: Array<Student> = [
    new Student("001", "吴用", new Pet("001", "快快", "猫")),
    new Student("001", "王何凤", new Pet("002", "乐乐", "猫")),
    new Student("001", "吴又可", new Pet("003", "饼干", "鼠")),
  ]
  build() {
    Column() {
      Button("+添加学生信息").onClick(() => {
        const tempDate = new Student("004", "王何凤", new Pet("004", "小灰灰", "兔子"))
        this.school.push(tempDate)
      }).height(20).fontSize(14).margin({ bottom: 10, top: 15 })
      ForEach(this.school, (item: Student, index: number) => {
        ObjectLinkList({item:item})
      }, (item: Student) => item.id)
    }
    .height('100%')
    .width('100%')
    .alignItems(HorizontalAlign.Start)
    .justifyContent(FlexAlign.Start)
  }
}

子组件中:使用@ObjectLink监听

import { Pet,Student } from "../viewmodel/StudentModel"
@Component
export struct ObjectLinkList {
  @ObjectLink item:Student
  build() {
    Column() {
      Text(`姓名 :${this.item.name},   宠物名称 :${this.item.pet.name},   宠物类型 :${this.item.pet.type}`)
        .fontColor(Color.Brown)
      Row() {
        Button("修改").onClick(() => {
          this.item.name = "默认名称"
        }).width(70).height(20).fontSize(14)
      }
    }
  }
}

子组件接收的数据就是你要监听的哪个对象,配合ObjectLink配合Observed就能实现对指定的这个对象进行深度监听。

数组对象深度更新解决方案效果:

边实战边学习一、:掌握ArkTS中的关键装饰器

嵌套对象深度更新解决方案:

import { Pet, Student } from "../viewmodel/StudentModel"

@Entry
@Component
struct Page14_ObjectLink {
  @State stu: Student = new Student("1", "王何凤", new Pet("001", "快快", "猫"))

  build() {
    Column() {
      Column() {
        Text(`学生信息为:${JSON.stringify(this.stu)}`).fontSize(25)
        Button("父组件修改学生宠物名字").onClick((event: ClickEvent) => {
          this.stu.pet.name = "小白" 
        })
        ObservedChildren({pet:this.stu.pet})
      }
    }
    .height('100%')
    .alignItems(HorizontalAlign.Start)
    .justifyContent(FlexAlign.Start)
  }
}

@Component
export struct ObservedChildren{
  @ObjectLink pet:Pet
  build() {
    Column(){
      Text(`宠物信息${this.pet.name}}`)
        .fontSize(20)
        .fontColor(Color.Orange)
      Button("子组件修改学生宠物名字").onClick((event: ClickEvent) => {
        this.pet.name = "乐乐"
      })
    }.width("100%").height("100%")
  }
}

嵌套对象深度更新解决效果:

边实战边学习一、:掌握ArkTS中的关键装饰器

总结:

如果页面上面只是渲染嵌套对象,那我们无需进行干预,直接渲染出来即可,但是如果要针对嵌套的对象进行修改,一定创建一个子组件,将这个对象传递给子组件专门进行页面渲染以及修改,也需要给model数据约束增加@Observerd以及@ObjectLink来接收修改对象。

@Provider与@Consume装饰器:

在鸿蒙开发过程中,会遇到跨组件进行通信,爷孙节点进行通信。
深入鸿蒙开发:掌握ArkTS中的关键装饰器

使用注意:

  1. @Provider在父组件中定义。
  2. @Comsume可以在子组件或者孙节点使用。

使用格式案例:

@Entry
@Component
struct Page15_Provider {
  @Provide msg:string = "harmonyos"
  build() {
    Column() {
      Text("我是Page15_Provider组件").fontSize(20).fontColor(Color.Red)
      Text(this.msg).fontColor(Color.Blue)
      Button("父组件修改msg").onClick((event: ClickEvent) => {
        this.msg = "鸿蒙开发"
      })
      CompA()
    }.width("100%")
    .height("100%")
  }
}

@Component
export struct CompA{
  @Consume msg:string
  build() {
    Column() {
      Text("我是CompA组件").fontSize(20).fontColor(Color.Red)
      Text(this.msg).fontColor(Color.Blue)
      CompB()
    }.width("100%")
    .height("100%")
  }
}

@Component
export struct CompB{
  // 给获取的数据去一个别名
  @Consume("msg") mymsg:string
  build() {
    Column() {
      Text("我是CompB组件").fontSize(20).fontColor(Color.Red)
      Text(this.mymsg).fontColor(Color.Blue)
      Button("子组件修改msg").onClick((event: ClickEvent) => {
        this.mymsg = "harmonyos"
      })
    }.width("100%")
    .height("100%")
  }
}

使用格式效果:

深入鸿蒙开发:掌握ArkTS中的关键装饰器

总结

  1. @Consume装饰器在定义过程中无需初始化,值必须来自于@Provide装饰器,
  2. @Consume获取数据有两种写法,@Consume便令名称,@Consume(“变量名称”) 别名。
  3. @Consume@Link一样,都具有双向数据更新的特点。

鸿蒙开发中,如何进行组件通信

  1. @Prop的方式可以实现父子组件单项数据更新。
  2. @Link装饰器可以实现父子组件的双向数据更新。
  3. @Provide@Consume可以实现夸组件之间的通信。
  4. LocalStorageAppstorage来实现页面数据的共享。

@Build装饰器

前言:

类似一个组件封装,使用@Builder 定义的模块,直接使用this.调用即可。

简单使用格式案例

  @Builder tabBuilder(){
    Column({space:3}) {
      Image($r("app.media.home"))
        .size({ width: 25, height: 25 })
      Text("首页")
        .fontColor("#3DA9F1")
        .fontWeight(400)
    }
    .width('100%')
    .height(50)
    .justifyContent(FlexAlign.Center)
  }
 @Builder myBuilder(){
    Text("首页")
      .fontSize(30)
      .fontColor(Color.Red)
  }

  build() {
    Column() {
      Tabs({ barPosition: BarPosition.End }) {
        TabContent() {
          // Text('首页的内容').fontSize(30)
          this.myBuilder()
        }
        // 提取公共的布局代码
        .tabBar(this.tabBuilder())

        TabContent() {
          Text('推荐的内容').fontSize(30)
        }
        .tabBar('产品')

        TabContent() {
          Text('购物车').fontSize(30)
        }
        .tabBar('购物车')

        TabContent() {
          Text('我的内容').fontSize(30)
        }
        .tabBar("我的")
      }
    }
    .height('100%')
    .width('100%')
  }
}

简单使用效果

编写进度 100% 深入鸿蒙开发:掌握ArkTS中的关键装饰器

可传参使用案例

实现应用底部菜单

import { BreakPointSystem } from '../utils/BreakPointSystem';
import { BasicConstants } from "../Constans/BasicConstans"

@Entry
@Component
struct Index {
  @State message: string = 'Entry Index';
  @StorageProp("currentBreakpoint") currentBreakpoint: string = "sm"
  @State currentIndex:number = 0
  private breakPointSystem = new BreakPointSystem()
  private tabsController: TabsController = new TabsController()

  aboutToAppear(): void {
    // 注册屏幕监听
    this.breakPointSystem.register()
  }

  // 这个函数里面存放的是布局代码
  @Builder
  tabBuilder(title: string, targetIndex: number, selectImage: Resource, normalImg: Resource) {
    Column({ space: 3 }) {
      Image(this.currentIndex==targetIndex?selectImage:normalImg)
        .size({ width: 25, height: 25 })
      Text(title)
        .fontColor(this.currentIndex==targetIndex?"#3DA9F1":"black")
        .fontWeight(400)
    }
    .width('100%')
    .height(50)
    .justifyContent(FlexAlign.Center)
    .onClick(()=>{
      this.currentIndex = targetIndex
      // 控制器的下标也更新,控制器显示页面下标
      this.tabsController.changeIndex(targetIndex)
    })
  }

  @Builder
  myBuilder() {
    Text("首页")
      .fontSize(30)
      .fontColor(Color.Red)
  }

  build() {
    Column() {
      Tabs({ barPosition: BarPosition.End,controller:this.tabsController }) {
        TabContent() {
          // Text('首页的内容').fontSize(30)
          this.myBuilder()
        }
        // 提取公共的布局代码
        .tabBar(this.tabBuilder("首页",0,$r("app.media.homeActive"),$r("app.media.home")))

        TabContent() {
          Text('推荐的内容').fontSize(30)
        }
        .tabBar(this.tabBuilder("产品",1,$r("app.media.productActive"),$r("app.media.product")))

        TabContent() {
          Text('购物车').fontSize(30)
        }
        .tabBar(this.tabBuilder("购物车",2,$r("app.media.gwcActiv"),$r("app.media.gwc")))

        TabContent() {
          Text('我的内容').fontSize(30)
        }
        .tabBar(this.tabBuilder("我的",3,$r("app.media.myActive"),$r("app.media.my")))
      }
      .onChange((index:number)=>{
        // tab滑动的时候
        this.currentIndex = index
      })
    }
    .height(BasicConstants.FULL_WIDTH)
    .width(BasicConstants.FULL_HEIGHT)
    .backgroundColor("#f1f3f5")
  }
}

传参案例实现效果:

深入鸿蒙开发:掌握ArkTS中的关键装饰器

项目数据封装

拿到项目后,根据项目页面设计进行数据的抽象,需要定义好数据的约束,如果后端还没提供接口,我还需要构造app的数据,需要采用面向对象类来定义我们数据的约束。

import { formatDate,getRandomDateCurrentMonth } from "../utils/DateFormat"
enum TaskStatusType{
  DONE = "done",
  UNDONE = "undone",
  FUTURE = "future",
  DOING = "doing",
}
export class TaskModel{
  // 每个任务的编号
  id:string = ""
  // 每个任务的名称
  taskName:string = ""
  // 每个任务的日期
  taskTime:string = ""
  // 任务详情
  taskMsg:string = ""
  // 每个任务的开始时间
  taskBeginDate:string = ""
  // 每个任务的结束时间
  taskEndData:string = ""
  // 每个任务的类型,1:生活类型,2:工作类型
  TaskType:number = 1
  // 任务的标签名称集,学习,生活,工作,娱乐
  taskTitle:number = 1
  // 人物的状态 done完成,undone未完成,doing正在进行 future 未来任务
  taskStatus:string = ""
  constructor(id: string, taskName: string, taskTime: string, taskBeginDate: string, taskEndData: string,
    TaskType: number, taskTitle: number,taskMsg:string,) {
    this.id = id
    this.taskName = taskName
    this.taskTime = taskTime
    this.taskBeginDate = taskBeginDate
    this.taskEndData = taskEndData
    this.TaskType = TaskType
    this.taskTitle = taskTitle
    this.taskMsg = taskMsg
    // 状态不用传递,自己计算状态
    this.updateTaskStatus()
  }
  // 用于根据当前任务时间,计算我们的任务状态
  updateTaskStatus(){
    const currentData = formatDate(new Date().getTime(),"yy-mm-dd")
    // 计划完成时间,比当前时间更大,future状态
    if(this.taskTime > currentData){
      this.taskStatus = TaskStatusType.FUTURE
      // 计划完成时间,等于当前时间,doing状态
    }else if(this.taskTime === currentData){
      this.taskStatus= TaskStatusType.DOING
    //   计划完成时间小于当前时间,状态undone
    }else if(this.taskTime <= currentData){
      this.taskStatus = TaskStatusType.UNDONE
    }
  }
}

需要将数据约束的类给暴露出去,在页面中就可以引入进来约束。

import CreateTaskModel ,{ TaskModel } from "../viewmodel/TaskModel"

@State tasks:Array<TaskModel> = []

在进行数据构造的时候,考虑了日期需要频繁的使用,定义了日期格式化的工具来进行处理。
数据结构定义好了过后,为了让页面功能能够基于目前的数据进行实战,在Model中构造了测试数据。

// 目前临时用,等以后有接口,发送网络请求去后端获取数据
class CreateTaskModel{
  private tasks:TaskModel[] = []
  initData(){
    for(let index=0;index<20;index++){
      const fullDate = getRandomDateCurrentMonth()
      const task = new TaskModel(
        `${index+1}`,
        `计划要完成的任务${index+1}`,
        fullDate,
        "9:00",
        "12:00",
        1,
        1,
        `这是人物的详情${index}`)
      this.tasks.push(task)
    }
    return this.tasks
  }
}
export default new CreateTaskModel()

页面中要使用我们钩爪的数据,可以在生命周期中调用

@State tasks:Array<TaskModel> = []
  aboutToAppear(): void {
    const tasks = CreateTaskModel.initData()
    this.tasks = tasks
  }

使用:

  • @ObjectLinkObserved装饰器一般要一起使用,才能实现对象监听。
  • @Observed放在类上面(model数据约束上面)。
  • ObjectLink放在组件中定义数据,无法在entry组件中使用。

工具的封装

封装了常用的日期格式化工具

export class Opt{
  yy:string = ''
  mm:string = ''
  dd:string = ''
  HH:string = ''
  MM:string = ''
  SS:string = ''
}
export function formatDate(timestamp:number,format = 'yyy-mm-dd'){
  let res = ""
  try {
    // 转化为日期对象
    const date = new Date(timestamp);
    // 定义了日期字段
    const opt:Opt = {
      yy:date.getFullYear().toString(),
      mm:(date.getMonth()+1).toString(),
      dd:date.getDate().toString(),
      HH:date.getHours().toString(),
      MM:date.getMinutes().toString(),
      SS:date.getSeconds().toString(),
    }
    // 存放正则表达式要匹配的字段, y+代表一个或者多个y
    const regKeys:string[] = ['y+','m+','d+','H+','M+','S+'];
    for(let i = 0;i<regKeys.length;i++){
      const regKey = regKeys[i];
      // 钩爪正则表达式对象 new RegExp
      const reg = new RegExp(regKey);
      let ret = reg.exec(format)
      if(ret){
        switch (regKey){
          case "y+":
            format = format.replace(reg,ret.length !==1?opt.yy : opt.yy.padStart(ret.length,"0"))
          case "m+":
            format = format.replace(reg,ret.length !==1?opt.mm :opt.mm.padStart(2,"0") )
          case "d+":
            format = format.replace(reg,ret.length !==1?opt.dd : opt.dd.padStart(2,"0"))
          case "H+":
            format = format.replace(reg,ret.length !==1?opt.HH : opt.HH.padStart(2,"0"))
          case "M+":
            format = format.replace(reg,ret.length !==1?opt.MM : opt.MM.padStart(2,"0"))
          case "S+":
            format = format.replace(reg,ret.length !==1?opt.SS : opt.SS.padStart(2,"0"))
        }
      }
    }
    res = format
  }catch (err){
    console.log("Error FormatData")
  }
  return res
}

/*
 * 为了获取到随机的一个日期
 * */
export function getRandomDateCurrentMonth(){
  let today = new Date()
  const year = today.getFullYear()
  let mouth = today.getMonth()+1
  let minDay = 1
  let maxDay = new Date(year,mouth+1,0).getDate()
  let randomDay = Math.floor(Math.random()*(maxDay-minDay+1))+minDay
  return `${year}-${String(mouth).padStart(2,"0")}-${String(randomDay).padStart(2,"0")}`


}

页面中渲染数据

父组件:

import { TaskStatisties } from "../view/TaskStatisties"
import { TaskItem } from "../view/TaskItem"
import CreateTaskModel ,{ TaskModel } from "../viewmodel/TaskModel"
@Entry
@Component
struct Page02_TaskList{
  @State tasks:Array<TaskModel> = []
  aboutToAppear(): void {
    const tasks = CreateTaskModel.initData()
    this.tasks = tasks
    console.log("tasks",this.tasks)
  }
   build() {
	Column(){
		TaskStatisties({tasks:this.tasks})
	}
   }
  }

子组件:


import { TaskModel } from "../viewmodel/TaskModel"
@Component
export struct TaskStatisties{
  @Prop tasks:Array<TaskModel>
  computedStatusCount(status:string){
    const count = this.tasks.reduce((sum:number,item:TaskModel)=>{
      if(item.taskStatus === status){
        sum+=1
      }
      return sum
    },0)
    return count
  }
  build() {
    // 我的计划
   Column(){
     Row(){
       // 我的计划左侧
       Column({space:10}){
         // 存放已完成的任务
         Column(){
           Image($r("app.media.startIcon"))
             .width(70)
             .margin({top:10,left:10,bottom:10})
           Text("已完成")
             .fontWeight(FontWeight.Bold)
             .fontSize(18)
             .fontColor($r("app.color.text_color"))
             .margin({left:15})
           Text(`${this.computedStatusCount("done")}任务`)
             .fontWeight(FontWeight.Bold)
             .fontSize(18)
             .fontColor($r("app.color.text_color"))
             .margin({left:15,top:5})
         }
         .height(164)
         .width(168)
         .backgroundColor("#83C7E3")
         .borderRadius(15)
         .alignItems(HorizontalAlign.Start)
       }
       .width("50%")
       .height("100%")
     }
     .height(300)
     .width("100%")
   }
  }
}

效果:

 深入理解ArkTS:从装饰器到条件渲染

ForEach循环渲染

前言

在鸿蒙开发中如果要涉及循环渲染数据可以使用两种方案。

  1. ForEach的方式来实现,直接封装了循环的语法,接受数组来进行动态遍历。
  2. LazyForEach来实现,循环懒加载,可以实现大数据量的情况下,实现谢娜功能优化。

格式

forEach这个接口需要传递三个参数

ForEach(this.tasks,(item:TaskModel,index:number)=>{},(item:TaskModel)=>item.id)

第一个参数:必须提供一个数组。
第二个参数:循环体,接受遍历产生的对象,index下表。
第三个参数:可选项,用于指定一个字段为循环的key。

案例效果:

 深入理解ArkTS:从装饰器到条件渲染

案例代码:

import { formatDate } from '../utils/DateFormat'
import { TaskModel } from '../viewmodel/TaskModel'

@Component
export struct TaskItem {
  @Prop tasks: Array<TaskModel>

  computedTodayTask() {
    // 获取当前日期
    const today = formatDate(new Date().getTime(), "yyyy-mm-dd")
    // tasks中赛选满足条件的数据
    return this.tasks.filter((item: TaskModel) => item.taskTime === today)
  }

  build() {
    Column({ space: 10 }) {
      ForEach(this.tasks, (item: TaskModel, index: number) => {
        Column({ space: 10 }) {
          // 存放图片和文字
          Row({ space: 20 }) {
            Image($r("app.media.startIcon"))
              .width(30)
              .height(30)
            Text(item.taskName)
              .fontColor($r("app.color.text_color"))
              .fontWeight(FontWeight.Bold)
              .fontSize(18)
          }
          .width("100%")

          Text(`${item.taskBeginDate}-${item.taskEndData}`)
            .fontWeight(FontWeight.Bold)
            .fontColor(Color.Gray)
          Row() {
            Text(`${item.TaskType==1?"生活":"工作"}`)
              .fontColor(Color.White)
          }
          .width(80)
          .height(30)
          .backgroundColor(Color.Brown)
          .borderRadius("50%")
          .justifyContent(FlexAlign.Center)
        }
        .width("100%")
        .height(120)
        .backgroundColor(Color.White)
        .borderRadius(10)
        .alignItems(HorizontalAlign.Start)
        .padding({ left: 15, top: 10 })
      }, (item: TaskModel) => item.id)
    }
  }
}

if / else条件渲染

前言:

ifelse if后跟随的状态判断中使用的状态变量值变化时,条件渲染语句会进行更新,更新步骤如下:

  1. 评估ifelse if的状态判断条件,如果分支没有变化,无需执行以下步骤。如果分支有变化,则执行23步骤。
  2. 删除此前构建的所有子组件。
  3. 执行新分支的构造函数,将获取到的组件添加到if父容器中。如果缺少适用的else分支,则不构建任何内容。

格式:

 if(item.taskStatus === "done"){
	Row() {
      Text(`${item.TaskType==1?"生活":"工作"}`)
        .fontColor(Color.White)
    }
   .width(80)
    .height(30)
    .backgroundColor(Color.Brown)
    .borderRadius("50%")
    .justifyContent(FlexAlign.Center)
  }else if(item.taskStatus === "undone"){
    Text("未完成")
  }

效果:
 深入理解ArkTS:从装饰器到条件渲染
条件可以包括Typescript表达式。对于构造函数中的表达式,此类表达式不得更改应用程序状态。
最大特点可以直接在build这个函数中使用,用于判断页面组件是否需要进行渲染。

等待完善添加~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值