鸿蒙HarmonyOS应用开发旅程ArkUI实战绘制(五)@--装饰器
ArkUI提供了一种轻量的UI元素复用机制@Builder,其内部UI结构固定,仅与使用方进行数据传递,开发者可以将重复使用的UI元素抽象成一个方法,在build方法里调用。为了简化语言,我们将@Builder装饰的函数也称为“自定义构建函数”。允许在自定义组件内定义一个或多个@Builder方法,该方法被认为是该组件的私有、特殊类型的成员函数。私有自定义构建函数允许在自定义组件内、build方法
在前面的文章中,我们已经介绍了HarmonyOS应用开发中的一些基础知识,以及使用ArkUI进行界面绘制的方法。在本篇文章中,我们将介绍一种在HarmonyOS应用开发中常用的装饰器,以及如何使用装饰器来简化代码的编写。
装饰器,顾名思义,是用来为函数或者类增加额外功能的一种方法。在HarmonyOS应用开发中,我们经常会遇到需要在某些函数或者类中增加一些额外逻辑的情况,比如权限检查、日志记录等。使用装饰器可以使我们的代码更加简洁、易读,同时提高代码的复用性。
在阅读本文章之前,建议提前阅读之前的文章
@Builder装饰器:自定义构建函数
ArkUI提供了一种轻量的UI元素复用机制@Builder,其内部UI结构固定,仅与使用方进行数据传递,开发者可以将重复使用的UI元素抽象成一个方法,在build方法里调用。
为了简化语言,我们将@Builder装饰的函数也称为“自定义构建函数”。
装饰器使用说明
私有自定义构建函数
定义的语法:
@Builder MyBuilderFunction() {}
使用方法:
this.MyBuilderFunction()
-
允许在自定义组件内定义一个或多个@Builder方法,该方法被认为是该组件的私有、特殊类型的成员函数。
-
私有自定义构建函数允许在自定义组件内、build方法和其他自定义构建函数中调用。
-
在自定义函数体中,this指代当前所属组件,组件的状态变量可以在自定义构建函数内访问。建议通过this访问自定义组件的状态变量而不是参数传递。
全局自定义构建函数
定义的语法
@Builder function MyGlobalBuilderFunction() { ... }
使用方法:
MyGlobalBuilderFunction()
-
如果不涉及组件状态变化,建议使用全局的自定义构建方法。
-
全局自定义构建函数允许在build方法和其他自定义构建函数中调用
参数传递规则
自定义构建函数的参数传递有按值传递和按引用传递两种,均需遵守以下规则:
参数的类型必须与参数声明的类型一致,不允许undefined、null和返回undefined、null的表达式。
在@Builder修饰的函数内部,不允许改变参数值。
@Builder内UI语法遵循UI语法规则。
只有传入一个参数,且参数需要直接传入对象字面量才会按引用传递该参数,其余传递方式均为按值传递。
按引用传递参数
按引用传递参数时,传递的参数可为状态变量,且状态变量的改变会引起@Builder方法内的UI刷新。
class Tmp {
paramA1: string = ''
}
@Builder function overBuilder(params: Tmp) {
Row() {
Text(`UseStateVarByReference: ${params.paramA1} `)
}
}
@Entry
@Component
struct Parent {
@State label: string = 'Hello';
build() {
Column() {
// 在父组件中调用overBuilder组件时,
// 把this.label通过引用传递的方式传给overBuilder组件。
overBuilder({ paramA1: this.label })
Button('Click me').onClick(() => {
// 单击Click me后,UI文本从Hello更改为ArkUI。
this.label = 'ArkUI';
})
}
}
}
按引用传递参数时,如果在@Builder方法内调用自定义组件,ArkUI提供$$作为按引用传递参数的范式。
class Tmp {
paramA1: string = ''
}
@Builder function overBuilder($$: Tmp) {
Row() {
Column() {
Text(`overBuilder===${$$.paramA1}`)
HelloComponent({message: $$.paramA1})
}
}
}
@Component
struct HelloComponent {
@Prop message: string;
build() {
Row() {
Text(`HelloComponent===${this.message}`)
}
}
}
@Entry
@Component
struct Parent {
@State label: string = 'Hello';
build() {
Column() {
// 在父组件中调用overBuilder组件时,
// 把this.label通过引用传递的方式传给overBuilder组件。
overBuilder({paramA1: this.label})
Button('Click me').onClick(() => {
// 单击Click me后,UI文本从Hello更改为ArkUI。
this.label = 'ArkUI';
})
}
}
}
按值传递参数
调用@Builder装饰的函数默认按值传递。当传递的参数为状态变量时,状态变量的改变不会引起@Builder方法内的UI刷新。所以当使用状态变量的时候,推荐使用按引用传递。
@Builder function overBuilder(paramA1: string) {
Row() {
Text(`UseStateVarByValue: ${paramA1} `)
}
}
@Entry
@Component
struct Parent {
@State label: string = 'Hello';
build() {
Column() {
overBuilder(this.label)
}
}
}
@Builder存在两个或者两个以上参数
当参数存在两个或者两个以上的时候,就算通过对象字面量的形式传递,值的改变也不会引起UI刷新。
class GlobalTmp {
str_value: string = 'Hello';
}
@Builder function overBuilder(param: GlobalTmp, num: number) {
Column() {
Text(`str_value: ${param.str_value}`)
Text(`num: ${num}`)
}
}
@Entry
@Component
struct Parent {
@State objParam: GlobalTmp = new GlobalTmp();
@State num: number = 0;
build() {
Column() {
Text('通过调用@Builder渲染UI界面')
.fontSize(20)
// 使用了两个参数,用法错误。
overBuilder({str_value: this.objParam.str_value}, this.num)
Line()
.width('100%')
.height(10)
.backgroundColor('#000000').margin(10)
Button('点击改变参数值').onClick(() => {
this.objParam.str_value = 'Hello World';
this.num = 1;
})
}
}
}
@Builder只接受一个参数,当传入一个参数的时候,通过对象字面量的形式传递,值的改变会引起UI的刷新。
@LocalBuilder装饰器: 维持组件父子关系
当开发者使用@Builder做引用数据传递时,会考虑组件的父子关系,使用了bind(this)之后,组件的父子关系和状态管理的父子关系并不一致。为了解决组件的父子关系和状态管理的父子关系保持一致的问题,引入@LocalBuilder装饰器。@LocalBuilder拥有和局部@Builder相同的功能,且比局部@Builder能够更好的确定组件的父子关系和状态管理的父子关系。
装饰器使用说明
自定义组件内自定义构建函数
定义的语法:
@LocalBuilder MyBuilderFunction() { ... }
使用方法:
this.MyBuilderFunction()
- 允许在自定义组件内定义一个或多个@LocalBuilder方法,该方法被认为是该组件的私有、特殊类型的成员函数。
- 自定义构建函数可以在所属组件的build方法和其他自定义构建函数中调用,但不允许在组件外调用。
- 在自定义函数体中,this指代当前所属组件,组件的状态变量可以在自定义构建函数内访问。建议通过this访问自定义组件的状态变量而不是参数传递。
限制条件
-
@LocalBuilder只能在所属组件内声明,不允许全局声明。
-
@LocalBuilder不能被内置装饰器和自定义装饰器使用。
-
自定义组件内的静态方法不能和@LocalBuilder一起使用
@Builder方法引用传参时,为了改变this指向,使用bind(this)后,会导致组件的父子关系和状态管理的父子关系不一致,但是@LocalBuilder是否使用bind(this),都不会改变组件的父子关系
参数传递规则
@LocalBuilder函数的参数传递有按值传递和按引用传递两种,均需遵守以下规则:
参数的类型必须与参数声明的类型一致,不允许undefined、null和返回undefined、null的表达式。
在@LocalBuilder修饰的函数内部,不允许改变参数值。
@LocalBuilder内UI语法遵循UI语法规则。
只有传入一个参数,且参数需要直接传入对象字面量才会按引用传递该参数,其余传递方式均为按值传递。
按引用传递参数
按引用传递参数时,传递的参数可为状态变量,且状态变量的改变会引起@LocalBuilder方法内的UI刷新。
若子组件调用父组件的@LocalBuilder函数,传入的参数发生变化,不会引起@LocalBuilder方法内的UI刷新。
使用场景:
组件Parent内的@LocalBuilder方法在build函数内调用,按键值对写法进行传值,当点击Click me 时,@LocalBuilder内的Text文本内容会随着状态变量内容的改变而改变。
class ReferenceType {
paramString: string = '';
}
@Entry
@Component
struct Parent {
@State variableValue: string = 'Hello World';
@LocalBuilder
citeLocalBuilder(params: ReferenceType) {
Row() {
Text(`UseStateVarByReference: ${params.paramString} `)
}
};
build() {
Column() {
this.citeLocalBuilder({ paramString: this.variableValue });
Button('Click me').onClick(() => {
this.variableValue = 'Hi World';
})
}
}
}
按引用传递参数时,如果在@LocalBuilder方法内调用自定义组件,ArkUI提供$$作为按引用传递参数的范式。
使用场景:
组件Parent内的@LocalBuilder方法内调用自定义组件,且按照引用传递参数将值传递到自定义组件,当Parent组件内状态变量值发生变化时,@LocalBuilder方法内的自定义组件HelloComponent的message值也会发生变化。
class ReferenceType {
paramString: string = '';
}
@Component
struct HelloComponent {
@Prop message: string;
build() {
Row() {
Text(`HelloComponent===${this.message}`);
}
}
}
@Entry
@Component
struct Parent {
@State variableValue: string = 'Hello World';
@LocalBuilder
citeLocalBuilder($$: ReferenceType) {
Row() {
Column() {
Text(`citeLocalBuilder===${$$.paramString}`);
HelloComponent({ message: $$.paramString });
}
}
}
build() {
Column() {
this.citeLocalBuilder({ paramString: this.variableValue });
Button('Click me').onClick(() => {
this.variableValue = 'Hi World';
})
}
}
}
子组件引用父组件的@LocalBuilder函数,传入的参数为状态变量,状态变量的改变不会引发@LocalBuilder方法内的UI刷新,原因是@Localbuilder装饰的函数绑定在父组件上,状态变量刷新机制是刷新本组件以及其子组件,对父组件无影响,故无法引发刷新。若使用@Builder修饰则可引发刷新,原因是@Builder改变了函数的this指向,此时函数被绑定到子组件上,故能引发UI刷新。
使用场景:
组件Child将@State修饰的label值按照函数传参方式传递到Parent的@Builder和@LocalBuilder函数内,在被@Builder修饰的函数内,this指向Child,参数变化能引发UI刷新,在被@LocalBuilder修饰的函数内,this指向Parent,参数变化不能引发UI刷新。
class LayoutSize {
size:number = 0;
}
@Entry
@Component
struct Parent {
label:string = 'parent';
@State layoutSize:LayoutSize = {size:0};
@LocalBuilder
// @Builder
componentBuilder($$:LayoutSize) {
Text(`${'this :'+this.label}`);
Text(`${'size :'+$$.size}`);
}
build() {
Column() {
Child({contentBuilder: this.componentBuilder });
}
}
}
@Component
struct Child {
label:string = 'child';
@BuilderParam contentBuilder:((layoutSize: LayoutSize) => void);
@State layoutSize:LayoutSize = {size:0};
build() {
Column() {
this.contentBuilder({size: this.layoutSize.size});
Button("add child size").onClick(()=>{
this.layoutSize.size += 1;
})
}
}
}
使用场景:
组件Child将@Link引用Parent的@State修饰的label值按照函数传参方式传递到Parent的@Builder和@LocalBuilder函数内,在被@Builder修饰的函数内,this指向Child,参数变化能引发UI刷新,在被@LocalBuilder修饰的函数内,this指向Parent,参数变化不能引发UI刷新。
class LayoutSize {
size:number = 0;
}
@Entry
@Component
struct Parent {
label:string = 'parent';
@State layoutSize:LayoutSize = {size:0};
@LocalBuilder
// @Builder
componentBuilder($$:LayoutSize) {
Text(`${'this :'+this.label}`);
Text(`${'size :'+$$.size}`);
}
build() {
Column() {
Child({contentBuilder: this.componentBuilder,layoutSize:this.layoutSize});
}
}
}
@Component
struct Child {
label:string = 'child';
@BuilderParam contentBuilder:((layoutSize: LayoutSize) => void);
@Link layoutSize:LayoutSize;
build() {
Column() {
this.contentBuilder({size: this.layoutSize.size});
Button("add child size").onClick(()=>{
this.layoutSize.size += 1;
})
}
}
}
按值传递参数
调用@LocalBuilder装饰的函数默认按值传递。当传递的参数为状态变量时,状态变量的改变不会
引起@LocalBuilder方法内的UI刷新。所以当使用状态变量的时候,推荐使用按引用传递
使用场景:
组件Parent将@State修饰的label值按照函数传参方式传递到@LocalBuilder函数内,此时@LocalBuilder函数获取到的值为普通变量值,所以改变@State修饰的label值时,@LocalBuilder函数内的值不会发生改变。
@Entry
@Component
struct Parent {
@State label: string = 'Hello';
@LocalBuilder
citeLocalBuilder(paramA1: string) {
Row() {
Text(`UseStateVarByValue: ${paramA1} `)
}
}
build() {
Column() {
this.citeLocalBuilder(this.label);
}
}
}
@LocalBuilder和@Builder区别说明
函数componentBuilder被@Builder修饰时,显示效果是 “Child”,函数componentBuilder被@LocalBuilder修饰时,显示效果是“Parent”。
说明:
@Builder componentBuilder()通过this.componentBuilder的形式传给子组件@BuilderParam customBuilderParam,this指向在Child的label,即“Child”。
@LocalBuilder componentBuilder()通过this.componentBuilder的形式传给子组件@BuilderParam customBuilderParam,this指向Parent的label,即“Parent”。
@Component
struct Child {
label: string = `Child`;
@BuilderParam customBuilderParam: () => void;
build() {
Column() {
this.customBuilderParam()
}
}
}
@Entry
@Component
struct Parent {
label: string = `Parent`;
@Builder componentBuilder() {
Text(`${this.label}`)
}
// @LocalBuilder componentBuilder() {
// Text(`${this.label}`)
// }
build() {
Column() {
Child({ customBuilderParam: this.componentBuilder })
}
}
}
使用场景
@LocalBuilder在@ComponentV2修饰的自定义组件中使用
使用局部的@LocalBuilder在@ComponentV2修饰的自定义组件中调用,修改变量触发UI刷新。
@ObservedV2
class Info {
@Trace name: string = '';
@Trace age: number = 0;
}
@ComponentV2
struct ChildPage {
@Require @Param childInfo: Info;
build() {
Column() {
Text(`自定义组件 name :${this.childInfo.name}`)
.fontSize(20)
.fontWeight(FontWeight.Bold)
Text(`自定义组件 age :${this.childInfo.age}`)
.fontSize(20)
.fontWeight(FontWeight.Bold)
}
}
}
@Entry
@ComponentV2
struct ParentPage {
info1: Info = { name: "Tom", age: 25 };
@Local info2: Info = { name: "Tom", age: 25 };
@LocalBuilder
privateBuilder() {
Column() {
Text(`局部LocalBuilder@Builder name :${this.info1.name}`)
.fontSize(20)
.fontWeight(FontWeight.Bold)
Text(`局部LocalBuilder@Builder age :${this.info1.age}`)
.fontSize(20)
.fontWeight(FontWeight.Bold)
}
}
@LocalBuilder
privateBuilderSecond() {
Column() {
Text(`局部LocalBuilder@Builder name :${this.info2.name}`)
.fontSize(20)
.fontWeight(FontWeight.Bold)
Text(`局部LocalBuilder@Builder age :${this.info2.age}`)
.fontSize(20)
.fontWeight(FontWeight.Bold)
}
}
build() {
Column() {
Text(`info1: ${this.info1.name} ${this.info1.age}`) // Text1
.fontSize(30)
.fontWeight(FontWeight.Bold)
this.privateBuilder() // 调用局部@Builder
Line()
.width('100%')
.height(10)
.backgroundColor('#000000').margin(10)
Text(`info2: ${this.info2.name} ${this.info2.age}`) // Text1
.fontSize(30)
.fontWeight(FontWeight.Bold)
this.privateBuilderSecond() // 调用局部@Builder
Line()
.width('100%')
.height(10)
.backgroundColor('#000000').margin(10)
Text(`info1: ${this.info1.name} ${this.info1.age}`) // Text1
.fontSize(30)
.fontWeight(FontWeight.Bold)
ChildPage({ childInfo: this.info1}) // 调用自定义组件
Line()
.width('100%')
.height(10)
.backgroundColor('#000000').margin(10)
Text(`info2: ${this.info2.name} ${this.info2.age}`) // Text2
.fontSize(30)
.fontWeight(FontWeight.Bold)
ChildPage({ childInfo: this.info2}) // 调用自定义组件
Line()
.width('100%')
.height(10)
.backgroundColor('#000000').margin(10)
Button("change info1&info2")
.onClick(() => {
this.info1 = { name: "Cat", age: 18} // Text1不会刷新,原因是没有装饰器修饰监听不到值的改变。
this.info2 = { name: "Cat", age: 18} // Text2会刷新,原因是有装饰器修饰,可以监听到值的改变。
})
}
}
}
@BuilderParam装饰器:引用@Builder函数
当开发者创建了自定义组件,并想对该组件添加特定功能时,例如在自定义组件中添加一个点击跳转操作。若直接在组件内嵌入事件方法,将会导致所有引入该自定义组件的地方均增加了该功能。为解决此问题,ArkUI引入了@BuilderParam装饰器,@BuilderParam用来装饰指向@Builder方法的变量(@BuilderParam是用来承接@Builder函数的)。开发者可以在初始化自定义组件时,使用不同的方式(如:参数修改、尾随闭包、借用箭头函数等)对@BuilderParam装饰的自定义构建函数进行传参赋值,在自定义组件内部通过调用@BuilderParam为组件增加特定的功能。该装饰器用于声明任意UI描述的一个元素,类似slot占位符。
装饰器使用说明
初始化@BuilderParam装饰的方法
@BuilderParam装饰的方法只能被自定义构建函数(@Builder装饰的方法)初始化。如果在API 11中和@Require结合使用,则必须父组件构造传参。
使用所属自定义组件的自定义构建函数或者全局的自定义构建函数,在本地初始化@BuilderParam。
@Builder function overBuilder() {}
@Component
struct Child {
@Builder doNothingBuilder() {};
// 使用自定义组件的自定义构建函数初始化@BuilderParam
@BuilderParam customBuilderParam: () => void = this.doNothingBuilder;
// 使用全局自定义构建函数初始化@BuilderParam
@BuilderParam customOverBuilderParam: () => void = overBuilder;
build(){}
}
用父组件自定义构建函数初始化子组件@BuilderParam装饰的方法。
@Component
struct Child {
@Builder customBuilder() {};
// 使用父组件@Builder装饰的方法初始化子组件@BuilderParam
@BuilderParam customBuilderParam: () => void = this.customBuilder;
build() {
Column() {
this.customBuilderParam()
}
}
}
@Entry
@Component
struct Parent {
@Builder componentBuilder() {
Text(`Parent builder `)
}
build() {
Column() {
Child({ customBuilderParam: this.componentBuilder })
}
}
}
以下示例对this的指向做了介绍。
@Component
struct Child {
label: string = `Child`;
@Builder customBuilder() {};
@Builder customChangeThisBuilder() {};
@BuilderParam customBuilderParam: () => void = this.customBuilder;
@BuilderParam customChangeThisBuilderParam: () => void = this.customChangeThisBuilder;
build() {
Column() {
this.customBuilderParam()
this.customChangeThisBuilderParam()
}
}
}
@Entry
@Component
struct Parent {
label: string = `Parent`;
@Builder componentBuilder() {
Text(`${this.label}`)
}
build() {
Column() {
// 调用this.componentBuilder()时,this指向当前@Entry所装饰的Parent组件,即label变量的值为"Parent"。
this.componentBuilder()
Child({
// 把this.componentBuilder传给子组件Child的@BuilderParam customBuilderParam,this指向的是子组件Child,即label变量的值为"Child"。
customBuilderParam: this.componentBuilder,
// 把():void=>{this.componentBuilder()}传给子组件Child的@BuilderParam customChangeThisBuilderParam,
// 因为箭头函数的this指向的是宿主对象,所以label变量的值为"Parent"。
customChangeThisBuilderParam: (): void => { this.componentBuilder() }
})
}
}
}
限制条件
- @BuilderParam装饰的变量接收来自父组件使用@Builder装饰的函数,且@Builder函数是参数传递类型,仅支持局部@Builder函数作为参数传递。
@Component
struct Child {
header: string = '';
@BuilderParam content: () => void;
footer: string = '';
build() {
Column() {
Text(this.header)
this.content();
Text(this.footer)
}
}
}
@Entry
@Component
struct Parent {
@Builder
test() {
Text('Hello')
}
build() {
Column() {
// 错误写法,@BuilderParam需要被初始化
Child()
// 正确写法
Child({ content: this.test })
}
}
}
在自定义组件尾随闭包的场景下,子组件有且仅有一个@BuilderParam用来接收此尾随闭包,且此@BuilderParam不能有参数
使用场景
参数初始化组件
@BuilderParam装饰的方法可以是有参数和无参数的两种形式,需与指向的@Builder方法类型匹配。@BuilderParam装饰的方法类型需要和@Builder方法类型一致。
class Tmp{
label: string = '';
}
@Builder function overBuilder($$: Tmp) {
Text($$.label)
.width(400)
.height(50)
.backgroundColor(Color.Green)
}
@Component
struct Child {
label: string = 'Child';
@Builder customBuilder() {};
// 无参数类型,指向的componentBuilder也是无参数类型
@BuilderParam customBuilderParam: () => void = this.customBuilder;
// 有参数类型,指向的overBuilder也是有参数类型的方法
@BuilderParam customOverBuilderParam: ($$: Tmp) => void = overBuilder;
build() {
Column() {
this.customBuilderParam()
this.customOverBuilderParam({label: 'global Builder label' } )
}
}
}
@Entry
@Component
struct Parent {
label: string = 'Parent';
@Builder componentBuilder() {
Text(`${this.label}`)
}
build() {
Column() {
this.componentBuilder()
Child({ customBuilderParam: this.componentBuilder, customOverBuilderParam: overBuilder })
}
}
}
尾随闭包初始化组件
在自定义组件中使用@BuilderParam装饰的属性时也可通过尾随闭包进行初始化。在初始化自定义组件时,组件后紧跟一个大括号“{}”形成尾随闭包场景。
-
此场景下自定义组件内有且仅有一个使用@BuilderParam装饰的属性。
-
此场景下自定义组件不支持使用通用属性。
开发者可以将尾随闭包内的内容看做@Builder装饰的函数传给@BuilderParam。
@Component
struct CustomContainer {
@Prop header: string = '';
@Builder closerBuilder(){};
// 使用父组件的尾随闭包{}(@Builder装饰的方法)初始化子组件@BuilderParam
@BuilderParam closer: () => void = this.closerBuilder;
build() {
Column() {
Text(this.header)
.fontSize(30)
this.closer()
}
}
}
@Builder function specificParam(label1: string, label2: string) {
Column() {
Text(label1)
.fontSize(30)
Text(label2)
.fontSize(30)
}
}
@Entry
@Component
struct CustomContainerUser {
@State text: string = 'header';
build() {
Column() {
// 创建CustomContainer,在创建CustomContainer时,通过其后紧跟一个大括号“{}”形成尾随闭包
// 作为传递给子组件CustomContainer @BuilderParam closer: () => void的参数
CustomContainer({ header: this.text }) {
Column() {
specificParam('testA', 'testB')
}.backgroundColor(Color.Yellow)
.onClick(() => {
this.text = 'changeHeader';
})
}
}
}
}
使用全局@Builder和局部@Builder通过尾随闭包的形式去初始化@ComponentV2修饰的自定义组件中的@BuilderParam。
@ComponentV2
struct ChildPage {
@Require @Param message: string = "";
@Builder customBuilder() {};
@BuilderParam customBuilderParam: () => void = this.customBuilder;
build() {
Column() {
Text(this.message)
.fontSize(30)
.fontWeight(FontWeight.Bold)
this.customBuilderParam()
}
}
}
const builder_value: string = 'Hello World';
@Builder function overBuilder() {
Row() {
Text(`全局 Builder: ${builder_value}`)
.fontSize(20)
.fontWeight(FontWeight.Bold)
}
}
@Entry
@ComponentV2
struct ParentPage {
@Local label: string = `Parent Page`;
@Builder componentBuilder() {
Row(){
Text(`局部 Builder :${this.label}`)
.fontSize(20)
.fontWeight(FontWeight.Bold)
}
}
build() {
Column() {
ChildPage({ message: this.label}){
Column() { // 使用局部@Builder,通过组件后紧跟一个大括号“{}”形成尾随闭包去初始化自定义组件@BuilderParam
this.componentBuilder();
}
}
Line()
.width('100%')
.height(10)
.backgroundColor('#000000').margin(10)
ChildPage({ message: this.label}){ // 使用全局@Builder,通过组件后紧跟一个大括号“{}”形成尾随闭包去初始化自定义组件@BuilderParam
Column() {
overBuilder();
}
}
}
}
}
使用全局和局部@Builder初始化@BuilderParam
在自定义组件中,使用@BuilderParam修饰的变量接收来自父组件通过@Builder传递的内容进行初始化,因为父组件的@Builder可以使用箭头函数的形式改变当前的this指向,所以当使用@BuilderParam修饰的变量时,会展示出不同的内容。
@Component
struct ChildPage {
label: string = `Child Page`;
@Builder customBuilder() {};
@BuilderParam customBuilderParam: () => void = this.customBuilder;
@BuilderParam customChangeThisBuilderParam: () => void = this.customBuilder;
build() {
Column() {
this.customBuilderParam()
this.customChangeThisBuilderParam()
}
}
}
const builder_value: string = 'Hello World';
@Builder function overBuilder() {
Row() {
Text(`全局 Builder: ${builder_value}`)
.fontSize(20)
.fontWeight(FontWeight.Bold)
}
}
@Entry
@Component
struct ParentPage {
label: string = `Parent Page`;
@Builder componentBuilder() {
Row(){
Text(`局部 Builder :${this.label}`)
.fontSize(20)
.fontWeight(FontWeight.Bold)
}
}
build() {
Column() {
// 调用this.componentBuilder()时,this指向当前@Entry所装饰的ParentPage组件,所以label变量的值为"Parent Page"。
this.componentBuilder()
ChildPage({
// 把this.componentBuilder传给子组件ChildPage的@BuilderParam customBuilderParam,this指向的是子组件ChildPage,所以label变量的值为"Child Page"。
customBuilderParam: this.componentBuilder,
// 把():void=>{this.componentBuilder()}传给子组件ChildPage的@BuilderParam customChangeThisBuilderParam,
// 因为箭头函数的this指向的是宿主对象,所以label变量的值为"Parent Page"。
customChangeThisBuilderParam: (): void => { this.componentBuilder() }
})
Line()
.width('100%')
.height(10)
.backgroundColor('#000000').margin(10)
// 调用全局overBuilder()时,this指向当前整个活动页,所以展示的内容为"Hello World"。
overBuilder()
ChildPage({
// 把全局overBuilder传给子组件ChildPage的@BuilderParam customBuilderParam,this指向当前整个活动页,所以展示的内容为"Hello World"。
customBuilderParam: overBuilder,
// 把全局overBuilder传给子组件ChildPage的@BuilderParam customChangeThisBuilderParam,this指向当前整个活动页,所以展示的内容为"Hello World"。
customChangeThisBuilderParam: overBuilder
})
}
}
}
在@ComponentV2修饰的自定义组件中使用@BuilderParam
使用全局@Builder和局部@Builder去初始化@CompoentV2修饰的自定义组件中的@BuilderParam属性。
@ComponentV2
struct ChildPage {
@Param label: string = `Child Page`;
@Builder customBuilder() {};
@BuilderParam customBuilderParam: () => void = this.customBuilder;
@BuilderParam customChangeThisBuilderParam: () => void = this.customBuilder;
build() {
Column() {
this.customBuilderParam()
this.customChangeThisBuilderParam()
}
}
}
const builder_value: string = 'Hello World';
@Builder function overBuilder() {
Row() {
Text(`全局 Builder: ${builder_value}`)
.fontSize(20)
.fontWeight(FontWeight.Bold)
}
}
@Entry
@ComponentV2
struct ParentPage {
@Local label: string = `Parent Page`;
@Builder componentBuilder() {
Row(){
Text(`局部 Builder :${this.label}`)
.fontSize(20)
.fontWeight(FontWeight.Bold)
}
}
build() {
Column() {
// 调用this.componentBuilder()时,this指向当前@Entry所装饰的ParentPage组件,所以label变量的值为"Parent Page"。
this.componentBuilder()
ChildPage({
// 把this.componentBuilder传给子组件ChildPage的@BuilderParam customBuilderParam,this指向的是子组件ChildPage,所以label变量的值为"Child Page"。
customBuilderParam: this.componentBuilder,
// 把():void=>{this.componentBuilder()}传给子组件ChildPage的@BuilderParam customChangeThisBuilderPara
// 因为箭头函数的this指向的是宿主对象,所以label变量的值为"Parent Page"。
customChangeThisBuilderParam: (): void => { this.componentBuilder() }
})
Line()
.width('100%')
.height(5)
.backgroundColor('#000000').margin(10)
// 调用全局overBuilder()时,this指向当前整个活动页,所以展示的内容为"Hello World"。
overBuilder()
ChildPage({
// 把全局overBuilder传给子组件ChildPage的@BuilderParam customBuilderParam,this指向当前整个活动页,所以展示的内容为"Hello World"。
customBuilderParam: overBuilder,
// 把全局overBuilder传给子组件ChildPage的@BuilderParam customChangeThisBuilderParam,this指向当前整个活动页,所以展示的内容为"Hello World"。
customChangeThisBuilderParam: overBuilder
})
}
}
}
常见问题
改变内容UI不刷新
当调用自定义组件ChildPage时,把@Builder作为参数通过this.componentBuilder的形式传递,当前this会指向自定义组件内部,所以在父组件里面改变label的值,自定义组件ChildPage是感知不到的。
@Component
struct ChildPage {
@State label: string = `Child Page`;
@Builder customBuilder() {};
@BuilderParam customChangeThisBuilderParam: () => void = this.customBuilder;
build() {
Column() {
this.customChangeThisBuilderParam()
}
}
}
@Entry
@Component
struct ParentPage {
@State label: string = `Parent Page`;
@Builder componentBuilder() {
Row(){
Text(`Builder :${this.label}`)
.fontSize(20)
.fontWeight(FontWeight.Bold)
}
}
build() {
Column() {
ChildPage({
// 当前写法this指向ChildPage组件内
customChangeThisBuilderParam: this.componentBuilder
})
Button('点击改变label内容')
.onClick(() => {
this.label = 'Hello World';
})
}
}
}
使用箭头函数的形式把@Builder传递进自定义组件ChildPage中,当前this指向会停留在父组件ParentPage里,所以在父组件里改变label的值,自定义组件ChildPage会感知到并重新渲染UI。
把@Builder改为@LocalBuilder也能实现动态渲染UI功能。
@Component
struct ChildPage {
@State label: string = `Child Page`;
@Builder customBuilder() {};
@BuilderParam customChangeThisBuilderParam: () => void = this.customBuilder;
build() {
Column() {
this.customChangeThisBuilderParam()
}
}
}
@Entry
@Component
struct ParentPage {
@State label: string = `Parent Page`;
@Builder componentBuilder() {
Row(){
Text(`Builder :${this.label}`)
.fontSize(20)
.fontWeight(FontWeight.Bold)
}
}
build() {
Column() {
ChildPage({
customChangeThisBuilderParam: () => { this.componentBuilder() }
})
Button('点击改变label内容')
.onClick(() => {
this.label = 'Hello World';
})
}
}
}
下节文章我们对ArkUI的状态管理进行讲解
更多推荐
所有评论(0)