鸿蒙组件相关介绍(api12核心语法概念知识齐全版配套demo解释篇幅较长)

136 阅读9分钟

目标概览图

组件.png

组件(Component)

组件(Component)是界面搭建与显示的最小单位,组件根据功能可以分为以下五大类:基础组件、容器组件、媒体组件、绘制组件、画布组件。其中基础组件是视图层的基本组成单元。在ArkUI中,UI显示的内容均为组件,由框架直接提供的称为系统组件,由开发者定义的称为自定义组件

Web组件的生命周期

Web组件的状态主要包括:Controller绑定到Web组件、网页加载开始、网页加载进度、网页加载结束、页面即将可见等。

image.png

自定义组件的生命周期

image.png

自定义组件特点

  • 可组合:允许开发者组合使用系统组件、及其属性和方法。
  • 可重用:自定义组件可以被其他组件重用,并作为不同的实例在不同的父组件或容器中使用。
  • 数据驱动UI更新:通过状态变量的改变,来驱动UI的刷新。
  • 自定义组件通过“.”链式调用的形式设置通用样式

自定义组件的基本结构

  • 自定义组件名、类名、函数名不能和系统组件名相同
  • @Component装饰器仅能装饰struct关键字声明的数据结构。struct被@Component装饰后具备组件化的能力,需要实现build方法描述UI,一个struct只能被一个@Component装饰
  • @Entry装饰的自定义组件将作为UI页面的入口。在单个UI页面中,最多可以使用@Entry装饰一个自定义组件
  • @Entry装饰的自定义组件,其build()函数下的根节点唯一且必要,且必须为容器组件,其中ForEach禁止作为根节点
  • @Component装饰的自定义组件,其build()函数下的根节点唯一且必要,可以为非容器组件,其中ForEach禁止作为根节点
@Component
struct MyComponent {

}

不建议在生命周期aboutToDisappear内使用async await,如果在生命周期的aboutToDisappear使用异步操作(Promise或者回调方法),自定义组件将被保留在Promise的闭包中,直到回调方法被执行完,这个行为阻止了自定义组件的垃圾回收

生命周期案例&自定义组件案例

// 推荐方式
import { HMNavigation,HMRouterMgr } from '@hadss/hmrouter'

// 测试数据源 model 一般是别的文件定义导入即可
class Model {
  public value:string

  constructor(value:string) {
    this.value = value
  }
}

@Component
export struct Demo{
  @State message: string = '子组件初始化的State值';

  /*
   * build内部
   * 不允许声明本地变量
   * 不允许console.info
   * 不允许本地作用域
   * 不能调用没有用@Builder装饰的方法
   * 不允许使用switch语法
   * 如果需要使用条件判断,请使用if
   * 不允许使用表达式
   * 不允许直接改变状态变量
   * */
  build() {
    // 子组件 根节点唯一且必要,可为非容器组件
    Column(){
      Text(this.message)
    }
  }
}

@Entry
@Component
struct Index {
  // @State 声明的变量是组件外访问是不支持的 只能在组件内访问
  @State message: string = 'Hello World';
  @State count: number = 0
  @State title: Model = new Model('我是鸿蒙啊');
  @State list: Map<number, string> = new Map([[0, "vue"], [1, "react"], [3, "node"]]);

  // 创建自定义组件的新实例后,在执行其build()函数之前执行。允许在aboutToAppear函数中改变状态变量,更改将在后续执行build()函数中生效
  aboutToAppear(): void {
    console.log("aboutToAppear---初始化")
  }
  // 当用户点击返回按钮时触发,仅@Entry装饰的自定义组件生效。返回true表示页面自己处理返回逻辑,不进行页面路由;返回false表示使用默认的路由返回逻辑,不设置返回值按照false处理
  onBackPress():void{
    console.log("onBackPress---当用户点击返回按钮时触发")
  }

  // 页面每次显示时触发一次,包括路由过程、应用进入前台等场景,仅@Entry装饰的自定义组件生效
  onPageShow(): void {
    console.log("onPageShow---页面显示")
  }

  // 页面每次隐藏时触发一次,包括路由过程、应用进入后台等场景,仅@Entry装饰的自定义组件生效
  onPageHide(): void {
    console.log("onPageHide---页面隐藏")
  }

  // aboutToDisappear函数在自定义组件析构销毁之前执行。不允许在aboutToDisappear函数中改变状态变量,特别是@Link变量的修改可能会导致应用程序行为不稳定
  aboutToDisappear(): void {
    console.log("aboutToDisappear---页面销毁")
  }


  build() {
      RelativeContainer() {
        // 使用HMNavigation容器
        HMNavigation({
          navigationId: 'mainNavigation', options: {
            // standardAnimator: HMDefaultGlobalAnimator.STANDARD_ANIMATOR,
            // dialogAnimator: HMDefaultGlobalAnimator.DIALOG_ANIMATOR,
            // modifier: modifier
          }
        }) {
          Column(){
            ForEach(Array.from(this.list.entries()), (item: [number, string]) => {
              Text(`${item[0]}`).fontSize(30)
              Text(`${item[1]}`).fontSize(30)
              Divider()
            })
          }
          // 自定义组件
          Demo({message:'父组件初始化过来给子组件显示的内容,如果父组件没初始化就默认去取子组件的初始化值'})
          // set/clear/delete 都是一样的可以操作的 Set 类型的 add/clear/delete一样可以操作
          Button('向Map添加一项').onClick(() => {
            this.list.set(4, "java");
          })
          Column() {
            Text(`${this.title.value}`)
            Text(`${this.count}`)
            Text(this.message)
            Button('更新状态数据').width(100).height(100).backgroundColor('#eeeeee').onClick(()=>{
              this.message = 'hello 鸿蒙'
              this.count++
              this.title.value = this.title.value === '我是鸿蒙啊' ? '我准备更改model里面的数据' : '我是鸿蒙啊';
            })
            Button('NextPage').width(100).height(100).backgroundColor('#eeeeee').onClick(()=>{
              HMRouterMgr.push({pageUrl: 'SendPage'})
            })
          }
          .width('100%')
          .height('100%')
        }
      }
      .height('100%')
      .width('100%')
  }
}

@Builder装饰器:自定义构建函数

ArkUI提供了一种轻量的UI元素复用机制@Builder,该自定义组件内部UI结构固定,仅与使用方进行数据传递,开发者可以将重复使用的UI元素抽象成一个方法,在build方法里调用

  • @Builder通过按引用传递的方式传入参数,才会触发动态渲染UI,并且参数只能是一个
  • @Builder如果传入的参数是两个或两个以上,不会触发动态渲染UI
  • @Builder传入的参数中同时包含按值传递和按引用传递两种方式,不会触发动态渲染UI
  • @Builder的参数必须按照对象字面量的形式,把所需要的属性一一传入,才会触发动态渲染UI
  • 在@Builder方法内调用自定义组件或者其他@Builder方法,ArkUI提供$$作为按引用传递参数的范式
  • 创建全局的@Builder方法,在Column里面使用overBuilder()方式调用,通过以对象字面量的形式传递参数,无论是简单类型还是复杂类型,值的改变都会引起UI界面的刷新
import { HMRouter, HMRouterMgr } from '@hadss/hmrouter'

@Builder function btnBuilder(str: string) {
  Column() {
    Text(`全局Builder接受的值: ${str} `)
  }
}

// 模拟数据源一般放单独文件这里这是演示
class DataView {
  name: string = '小明'
  addr: string = '广东省深圳市南山区'
  age: number = 30
}

@Builder function manBuilder(params: DataView) {
  Row() {
    Text(`姓名: ${params.name} `)
    Text(`地址: ${params.addr} `)
    Text(`年龄: ${params.age} `)
  }
}
// 按引用传递参数时,如果在@Builder方法内调用自定义组件,ArkUI提供$$作为按引用传递参数的范式
@Builder function myBuilder($$: DataView) {
  Row() {
    Text(`姓名: ${$$.name} `)
    HelloComponent({name: $$.name, addr:$$.addr, age:$$.age})
  }
}

@Component
export struct HelloComponent {
  @Prop name: string;
  @Prop addr: string;
  @Prop age: number;

  build() {
    Row() {
      Text(`姓名:${this.name}`)
      Text(`地址:${this.addr}`)
      Text(`年龄:${this.age}`)
    }
  }
}

// 路由装饰器要在@Builder下面
@HMRouter({ pageUrl: 'SendPage' })


@Entry
@Component
export struct Send {
  @State message: string = '我是第二个页面';
  @State str: string = '描述全局的方法'

  // 私有自定义构建函数
  @Builder testBuilder(message: string) {
    Column() {
      Text(`接受的值: ${message} `)
    }
  }

  build() {
    RelativeContainer() {
      Column(){
        // 按值传递
        // 私有@Builder 通过this.xxx 调用
        this.testBuilder(this.message)
        // 全局 @Builder 无需this调用
        btnBuilder(this.str)
        // 按引用传递
        manBuilder(new DataView())
        // 按引用传递参数时,如果在@Builder方法内调用自定义组件,ArkUI提供$$作为按引用传递参数的范式
        myBuilder({name: '小花', addr:'广东省深圳市宝安区', age:28})
        Column(){
          Button('返回').width(100).height(100).backgroundColor('#eeeeee').onClick(()=>{
            HMRouterMgr.pop()
          })
        }
      }
    }
    .height('100%')
    .width('100%')
  }
}

@LocalBuilder装饰器: 维持组件父子关系

当开发者使用@Builder做引用数据传递时,会考虑组件的父子关系,使用了bind(this)之后,组件的父子关系和状态管理的父子关系并不一致。为了解决组件的父子关系和状态管理的父子关系保持一致,引入@LocalBuilder装饰器。@LocalBuilder拥有和局部@Builder相同的功能,且比局部@Builder能够更好的确定组件的父子关系和状态管理的父子关系。

  • 允许在自定义组件内定义一个或多个@LocalBuilder方法,该方法被认为是该组件的私有、特殊类型的成员函数。
  • 自定义构建函数可以在所属组件的build方法和其他自定义构建函数中调用,但不允许在组件外调用。
  • 在自定义函数体中,this指代当前所属组件,组件的状态变量可以在自定义构建函数内访问。建议通过this访问自定义组件的状态变量而不是参数传递。
  • @LocalBuilder只能在所属组件内声明,不允许全局声明
  • @LocalBuilder不能被内置装饰器和自定义装饰器使用
  • 自定义组件内的静态方法不能和@LocalBuilder一起使用

@LocalBuilder和局部@Builder使用区别

@Builder方法引用传参时,为了改变this指向,使用bind(this)后,会导致组件的父子关系和状态管理的父子关系不一致,但是@LocalBuilder是否使用bind(this),都不会改变组件的父子关系。

import { HMRouter, HMRouterMgr } from '@hadss/hmrouter'

// 模拟数据源一般放单独文件这里这是演示
class DataView {
  name: string = '小明'
  addr?: string = '广东省深圳市南山区'
  age?: number = 30
}

// 子组件
@Component
export struct HelloComponent {
  @Prop name: string;
  @Prop addr: string;
  @Prop age: number;

  build() {
    Row() {
      Text(`姓名:${this.name}`)
    }
  }
}

// 路由装饰器要在@Builder下面
@HMRouter({ pageUrl: 'SendPage' })


@Entry
@Component
export struct Send {
  @State message: string = '我是第二个页面';
  @State str: string = '描述全局的方法'

  // 按值传递 @LocalBuilder只能在所属组件内声明,不允许全局声明
  @LocalBuilder localBuilderStr(message:string){
    Column(){
      Text(`${message}`)
    }
  }

  @LocalBuilder localBuilderTest(params:DataView){
    Column(){
      Text(`姓名:${params.name}`)
    }
  }

  // 按引用传递
  @LocalBuilder quoteBuilder($$: DataView) {
    Row() {
      Column() {
        Text(`quoteBuilder===${$$.name}`);
        HelloComponent({ name: $$.name });
      }
    }
  }

  build() {
    RelativeContainer() {
      Column(){
        // 按值传递
        this.localBuilderStr(this.message)
        this.localBuilderTest({name:"王二小"})
        this.quoteBuilder({name:"王二小--引用方式"})
        Column(){
          Button('返回').width(100).height(100).backgroundColor('#eeeeee').onClick(()=>{
            HMRouterMgr.pop()
          })
        }
      }
    }
    .height('100%')
    .width('100%')
  }
}

@BuilderParam装饰器:引用@Builder函数

@BuilderParam用来装饰指向@Builder方法的变量(@BuilderParam是用来承接@Builder函数的)。开发者可以在初始化自定义组件时,使用不同的方式(如:参数修改、尾随闭包、借用箭头函数等)对@BuilderParam装饰的自定义构建函数进行传参赋值,在自定义组件内部通过调用@BuilderParam为组件增加特定的功能。该装饰器用于声明任意UI描述的一个元素,类似slot占位符

  • @BuilderParam装饰的方法只能被自定义构建函数(@Builder装饰的方法)初始化。如果在API 11中和@Require结合使用,则必须父组件构造传参。
  • 使用所属自定义组件的自定义构建函数或者全局的自定义构建函数,在本地初始化@BuilderParam
  • 用父组件自定义构建函数初始化子组件@BuilderParam装饰的方法
  • @BuilderParam装饰的方法可以是有参数和无参数的两种形式,需与指向的@Builder方法类型匹配。@BuilderParam装饰的方法类型需要和@Builder方法类型一致
  • 在自定义组件中使用@BuilderParam装饰的属性时也可通过尾随闭包进行初始化。在初始化自定义组件时,组件后紧跟一个大括号“{}”形成尾随闭包场景。【此场景下自定义组件内有且仅有一个使用@BuilderParam装饰的属性,此场景下自定义组件不支持使用通用属性
import { HMRouter, HMRouterMgr } from '@hadss/hmrouter'

// 模拟数据源一般放单独文件这里这是演示
class DataView {
  name: string = '小明'
  addr?: string = '广东省深圳市南山区'
  age?: number = 30
}

@Builder function overBuilder($$: DataView) {
  Text("有参数的匹配有参数的")
  Text($$.name)
    .backgroundColor(Color.Green)
}

// 子组件
@Component
export struct HelloComponent {
  @Prop name: string;
  @Prop addr: string;
  @Prop age: number;

  @Builder closerBuilder(){};
  // 使用父组件的尾随闭包{}(@Builder装饰的方法)初始化子组件@BuilderParam
  @BuilderParam closer: () => void = this.closerBuilder;

  build() {
    Row() {
      Text(`姓名:${this.name}`)
      // this.xxx 调用
      this.closer()
    }
  }
}

// 路由装饰器要在@Builder下面
@HMRouter({ pageUrl: 'SendPage' })


@Entry
@Component
export struct Send {
  @State message: string = '我是第二个页面';
  @State str: string = '描述全局的方法'

  @Builder customBuilder() {
    Text('没有参数的匹配没有参数的')
  };

  // 无参数类型,指向的@Builder也是无参数类型
  @BuilderParam customBuilderParam: () => void = this.customBuilder;
  // 有参数类型,指向的@Builder也是有参数类型的方法
  @BuilderParam customOverBuilderParam: ($$: DataView) => void = overBuilder;

  build() {
    RelativeContainer() {
      Column(){
        // 创建HelloComponent,在创建HelloComponent时,通过其后紧跟一个大括号“{}”形成尾随闭包
        // 作为传递给子组件HelloComponent @BuilderParam closer: () => void的参数
        HelloComponent({ name: this.message }) {
          Column() {
            Text("我是闭包产生的内容-尾随闭包初始化组件")
          }.backgroundColor(Color.Yellow)
        }
        // this.xxx调用相关的 @BuilderParam
        this.customBuilderParam()
        this.customOverBuilderParam({name: '小花' } )
        Column(){
          Button('返回').width(100).height(100).backgroundColor('#eeeeee').onClick(()=>{
            HMRouterMgr.pop()
          })
        }
      }
    }
    .height('100%')
    .width('100%')
  }
}

wrapBuilder:封装全局@Builder

// 模板参数Args extends Object[]是需要包装的builder函数的参数列表
declare function wrapBuilder< Args extends Object[]>(builder: (...args: Args) => void): WrappedBuilder;

当开发者在一个struct内使用了多个全局@Builder函数,来实现UI的不同效果时,多个全局@Builder函数会使代码维护起来非常困难,并且页面不整洁。此时,开发者可以使用wrapBuilder来封装全局@Builder(核心作用-封装全局@Builder)

  • wrapBuilder的参数返回WrappedBuilder对象(同时 WrappedBuilder对象也是一个模板类),实现全局@Builder可以进行赋值和传递
  • wrapBuilder方法只能传入全局@Builder方法
  • wrapBuilder方法返回的WrappedBuilder对象的builder属性方法只能在struct内部使用
  • wrapBuilder必须传入被@Builder修饰的全局函数
  • 重复定义wrapBuilder失效
import { HMRouter, HMRouterMgr } from '@hadss/hmrouter'

// 模拟数据源一般放单独文件这里这是演示
class DataView {
  name: string = '小明'
  addr?: string = '广东省深圳市南山区'
  age?: number = 30
}

@Builder function testBuilderOne(value: string, size: number) {
  Text(value)
    .fontSize(size)
}

@Builder function testBuilderTwo(value: string, size: number) {
  Text(value)
    .fontSize(size)
    .fontColor(Color.Pink)
}

// 引用传递
@Builder function overBuilder(param: DataView) {
  Column(){
    Text(`wrapBuildervalue:${param.name}`)
  }
}
// 引用传递
const wBuilder: WrappedBuilder<[DataView]> = wrapBuilder(overBuilder);

// @Builder方法赋值给变量 wrapBuilder方法只能传入全局@Builder方法
// wrapBuilder方法返回的WrappedBuilder对象的builder属性方法只能在struct内部使用
let globalBuilder: WrappedBuilder<[string, number]> = wrapBuilder(testBuilderOne);


// 自定义组件Index使用ForEach来进行不同@Builder函数的渲染,可以使用builderArr声明的wrapBuilder数组进行不同@Builder函数效果体现
// 多个@Builder 合并 遍历初始化
const builderArr: WrappedBuilder<[string, number]>[] = [wrapBuilder(testBuilderOne), wrapBuilder(testBuilderTwo)];


// 路由装饰器要在@Builder下面
@HMRouter({ pageUrl: 'SendPage' })


@Entry
@Component
export struct Send {
  @State message: string = '我是第二个页面';
  @State str: string = '描述全局的方法'

  @Builder testBuilder() {
    ForEach(builderArr, (item: WrappedBuilder<[string, number]>) => {
      item.builder('Hello World', 30)
    }
    )
  }

  build() {
    RelativeContainer() {
      Column(){
        wBuilder.builder({name: '小花'})
        globalBuilder.builder(this.message, 50)
        this.testBuilder()
        Column(){
          Button('返回').width(100).height(100).backgroundColor('#eeeeee').onClick(()=>{
            HMRouterMgr.pop()
          })
        }
      }
    }
    .height('100%')
    .width('100%')
  }
}

@Styles装饰器:定义组件重用样式 (封装样式用的)

@Styles装饰器可以将多条样式设置提炼成一个方法,直接在组件声明的位置调用。通过@Styles装饰器可以快速定义并复用自定义样式

  • 当前@Styles仅支持通用属性通用事件
  • @Styles方法不支持参数
  • @Styles可以定义在组件内或全局,在全局定义时需在方法名前面添加function关键字,组件内定义时则不需要添加function关键字。
  • 只能在当前文件内使用,不支持export。如果想实现export功能,推荐使用AttributeModifier
  • 可以在全局,也可以在组件内
  • 定义在组件内的@Styles可以通过this访问组件的常量和状态变量,并可以在@Styles里通过事件来改变状态变量的值
  • 组件内@Styles的优先级高于全局@Styles。框架优先找当前组件内的@Styles,如果找不到,则会全局查找
import { HMRouter, HMRouterMgr } from '@hadss/hmrouter'

// 全局
@Styles function globalFancy() {
  .width(150)
  .height(100)
  .backgroundColor(Color.Pink)
}
// 路由装饰器要在@Builder下面
@HMRouter({ pageUrl: 'SendPage' })


@Entry
@Component
export struct Send {
  @State message: string = '我是第二个页面';
  @State heightValue: number = 100

  // 组件内
  @Styles fancy() {
    .width(150)
    .height(this.heightValue)
    .backgroundColor(Color.Pink)
    .onClick(() => {
      this.heightValue = 200
    })
  }

  build() {
    RelativeContainer() {
      Column(){
        Column(){
          Button('全局样式').width(100).height(100).backgroundColor('#eeeeee').globalFancy()
          Button('组件内样式').width(100).height(100).backgroundColor('#eeeeee').fancy()
          Button('返回').width(100).height(100).backgroundColor('#eeeeee').onClick(()=>{
            HMRouterMgr.pop()
          })
        }
      }
    }
    .height('100%')
    .width('100%')
  }
}

@Extend装饰器:定义扩展组件样式 (也可以抽取公共样式)

@Extend,用于扩展原生组件样式

// 语法
@Extend(UIComponentName) function functionName { ... }
  • 和@Styles不同,@Extend仅支持在全局定义,不支持在组件内部定义
  • 只能在当前文件内使用,不支持export,如果想实现export功能,推荐使用AttributeModifier
  • 和@Styles不同,@Extend支持封装指定组件的私有属性、私有事件和自身定义的全局方法
  • 和@Styles不同,@Extend装饰的方法支持参数,开发者可以在调用时传递参数,调用遵循TS方法传值调用
  • @Extend装饰的方法的参数可以为function,作为Event事件的句柄
  • @Extend的参数可以为状态变量,当状态变量改变时,UI可以正常的被刷新渲染
import { HMRouter, HMRouterMgr } from '@hadss/hmrouter'

// @Extend(Text)可以支持Text的私有属性fontColor
@Extend(Text) function fancyTest () {
  .fontColor(Color.Red)
}
// superFancyText可以调用预定义的fancy 并且支持参数
@Extend(Text) function superFancyText(size:number) {
  .fontSize(size)
  .fancyTest()
}

// 作为Event事件的句柄
@Extend(Text) function handleClick(onClick: () => void) {
  .backgroundColor(Color.Blue)
  .onClick(onClick)
}

// 路由装饰器要在@Builder下面
@HMRouter({ pageUrl: 'SendPage' })


@Entry
@Component
export struct Send {
  @State message: string = '我是第二个页面';
  @State heightValue: number = 100


  onClickHandler() {
    this.message = '随便改点东西吧';
  }

  build() {
    RelativeContainer() {
      Column(){
        Column(){
          Text("随便写点内容").superFancyText(28)
          // 事件句柄
          Text(`${this.message}`).handleClick(() => {this.onClickHandler()})
          Button('返回').width(100).height(100).backgroundColor('#eeeeee').onClick(()=>{
            HMRouterMgr.pop()
          })
        }
      }
    }
    .height('100%')
    .width('100%')
  }
}

stateStyles:多态样式

@Styles仅仅应用于静态页面的样式复用,stateStyles可以依据组件的内部状态的不同,快速设置不同样式

  • stateStyles是属性方法,可以根据UI内部状态来设置样式
  • stateStyles可以通过this绑定组件内的常规变量和状态变量
import { HMRouter, HMRouterMgr } from '@hadss/hmrouter'

// @Extend(Text)可以支持Text的私有属性fontColor
@Extend(Text) function fancyTest () {
  .fontColor(Color.Red)
}
// superFancyText可以调用预定义的fancy 并且支持参数
@Extend(Text) function superFancyText(size:number) {
  .fontSize(size)
  .fancyTest()
}

// 作为Event事件的句柄
@Extend(Text) function handleClick(onClick: () => void) {
  .backgroundColor(Color.Blue)
  .onClick(onClick)
}

// 路由装饰器要在@Builder下面
@HMRouter({ pageUrl: 'SendPage' })


@Entry
@Component
export struct Send {
  @State message: string = '我是第二个页面';
  @State heightValue: number = 100

  @Styles normalStyle() {
    .backgroundColor(Color.Gray)
  }

  @Styles pressedStyle() {
    .backgroundColor(Color.Red)
  }

  onClickHandler() {
    this.message = '随便改点东西吧';
  }

  build() {
    RelativeContainer() {
      Column(){
        Column(){
          // @Styles和stateStyles联合使用
          Button('按钮的事件不一样背景颜色随着改变')
            .stateStyles({
              normal: this.normalStyle,
              pressed: this.pressedStyle
            })
            .margin(20)
          // stateStyles 单独使用 基础用法
          Button('按钮的事件不一样背景颜色随着改变')
            .stateStyles({
              focused: {
                .backgroundColor('#ffffeef0')
              },
              pressed: {
                .backgroundColor('#000000')
              },
              normal: {
                .backgroundColor('#cccccc')
              }
            })
            .margin(20)
          Button('返回').width(100).height(100).backgroundColor('#eeeeee').onClick(()=>{
            HMRouterMgr.pop()
          })
        }
      }
    }
    .height('100%')
    .width('100%')
  }
}

@AnimatableExtend装饰器:定义可动画属性

// 语法 
@AnimatableExtend(UIComponentName) function functionName(value: typeName) {
.propertyName(value)
}

@AnimatableExtend装饰器用于自定义可动画的属性方法,在这个属性方法中修改组件不可动画的属性。在动画执行过程时,通过逐帧回调函数修改不可动画属性值,让不可动画属性也能实现动画效果。也可通过逐帧回调函数每帧修改可动画属性的值,实现逐帧布局的效果。

  • @AnimatableExtend仅支持定义在全局,不支持在组件内部定义。

  • @AnimatableExtend定义的函数参数类型必须为number类型或者实现 AnimatableArithmetic接口的自定义类型。

  • @AnimatableExtend定义的函数体内只能调用@AnimatableExtend括号内组件的属性方法。

@AnimatableExtend(Text)
function animatableWidth(width: number) {
  .width(width)
}

@Entry
@Component
struct AnimatablePropertyExample {
  @State textWidth: number = 80;

  build() {
    Column() {
      Text("AnimatableProperty")
        .animatableWidth(this.textWidth)
        .animation({ duration: 2000, curve: Curve.Ease })
      Button("Play")
        .onClick(() => {
          this.textWidth = this.textWidth == 80 ? 160 : 80;
        })
    }.width("100%")
    .padding(10)
  }
}

@Require装饰器:校验构造传参

@Require是校验@Prop、@State、@Provide、@BuilderParam和普通变量(无状态装饰器修饰的变量)是否需要构造传参的一个装饰器

  • 当@Require装饰器和@Prop、@State、@Provide、@BuilderParam、普通变量(无状态装饰器修饰的变量)结合使用时,在构造该自定义组件时,@Prop、@State、@Provide、@BuilderParam和普通变量(无状态装饰器修饰的变量)必须在构造时传参

  • @Require装饰器仅用于装饰struct内的@Prop、@State、@Provide、@BuilderParam和普通变量(无状态装饰器修饰的变量)