目录
ArkUI中的装饰器概述
ArkUI提供轻量的UI元素复用机制@Builder,其内部UI结构固定,仅与使用方进行数据传递。开发者可将重复使用的UI元素抽象成函数,在build函数中调用。
@Builder装饰的函数也称为"自定义构造函数"。
它主要的优点如下:
1.封装可以服用的UI结构,提高开发效率。
2.保持内部组件变量的定义,还能完整管理组件的生命周期。
1.@Builder装饰器的用法
1.私有自定义构造函数
我们可以在自己的Component中直接使用Builder修饰构建的函数。
图1.私有自定义构造函数
代码如下:
@Component
export struct BuilderExamplePage {
@State label: string = 'Hello';
pageInfos: NavPathStack = new NavPathStack();
@Builder
showTextBuilder() {
// @Builder装饰此函数,使其能以链式调用的方式配置并构建Text组件
Button("Hello",{type:ButtonType.Capsule})
.fontSize(12)
.fontWeight(FontWeight.Bold)
.height(30)
.width(100).margin({top:20})
}
@Builder
showTextValueBuilder(param: string) {
Button(param,{type:ButtonType.Capsule})
.fontSize(12)
.fontWeight(FontWeight.Bold)
.height(30)
.width(100).margin({top:20})
}
build() {
NavDestination() {
Column(){
// 无参数
this.showTextBuilder()
// 有参数
this.showTextValueBuilder('有参数按钮')
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Start)
}
.title('@builder装饰器用法').onReady((context: NavDestinationContext) => {
this.pageInfos = context.pathStack;
})
}
}
2.全局自定义构建函数
我们还以把一些UI中样式重复的UI组件单独提取出来,写在一个文件中:
@Builder
export function NoParamCommonCapsuleButton() {
// @Builder装饰此函数,使其能以链式调用的方式配置并构建Text组件
Button("无参数按钮",{type:ButtonType.Capsule})
.fontSize(12)
.fontWeight(FontWeight.Bold)
.height(30)
.width(100).margin({top:20})
}
@Builder
export function CommonCapsuleButton(message: string) {
Button(message,{type:ButtonType.Capsule})
.fontSize(12)
.fontWeight(FontWeight.Bold)
.height(30)
.width(100).margin({top:20})
}
然后在Component中直接使用:
build() {
NavDestination() {
Column(){
// 无参数
this.showTextBuilder()
// 有参数
this.showTextValueBuilder('有参数按钮')
CommonCapsuleButton("有参数按钮")
NoParamCommonCapsuleButton()
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Start)
}
.title('@builder装饰器用法').onReady((context: NavDestinationContext) => {
this.pageInfos = context.pathStack;
})
}
2.参数传递规则
1.值传递参数
调用@Builder装饰的函数默认按值传递。当传递的参数为状态变量时,状态变量的改变不会引起@Builder函数内的UI刷新。
例如在上面的例子中,我们自定义的CommonCapsuleButton就是值传递,传一个参数之后,没有在外部修改这个参数。
2.引用传递
所以当使用状态变量的时候,推荐使用引用传递参数。
按引用传递参数时,传递的参数可为状态变量,且状态变量的改变会引起@Builder函数内的UI刷新。
这里贴一下官方的一个demo,我已经运行过了。
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';
})
}
}
}
3.限制条件
使用@builder的时候,有一些限制条件:
-
1.@Builder装饰的函数内部不允许修改参数值,否则框架会抛出运行时错误。但开发者可以在使用@Builder的自定义组件中改变其参数。
-
2.@Builder按引用传递且仅传入一个参数时,才会触发动态渲染UI。
-
3.如果@Builder传入的参数是两个或两个以上,不会触发动态渲染UI,请参考@Builder存在两个或者两个以上参数。
-
4.@Builder传入的参数中同时包含按值传递和按引用传递,不会触发动态渲染UI,请参考@Builder存在两个或者两个以上参数。
-
5.@Builder的参数必须按照对象字面量的形式,把所需属性一一传入,才会触发动态渲染UI,请参考@Builder存在两个或者两个以上参数。
4.@Builder的使用场景
示例:pandas 是基于NumPy 的一种工具,该工具是为了解决数据分析任务而创建的。
5.使用场景
1.代码复用
例如我们要实现如下的UI效果图:
在实现底部的三种方式登陆排列的时候,我们可能会写下面的代码:
Row({ space: 44 }) {
Button({ type: ButtonType.Circle, stateEffect: true }) {
Image($r('app.media.login_method1'))
}
.height('48vp')
.width('48vp')
.backgroundColor('#F1F3F5')
Button({ type: ButtonType.Circle, stateEffect: true }) {
Image($r('app.media.login_method2'))
}
.height('48vp')
.width('48vp')
.backgroundColor('#F1F3F5')
Button({ type: ButtonType.Circle, stateEffect: true }) {
Image($r('app.media.login_method3'))
}
.height('48vp')
.width('48vp')
.backgroundColor('#F1F3F5')
}
当我们使用@Builder之后,我们发现三种按钮样式相同,因此我们可以封装一个通用的方法。
@Builder
imageButton(src: Resource) {
Button({ type: ButtonType.Circle, stateEffect: true }) {
Image(src)
}
.height('48vp')
.width('48vp')
.backgroundColor('#F1F3F5')
}
然后UI中的代码变成了这样:
Row({ space: 44 }) {
this.imageButton($r('app.media.login_method1'))
this.imageButton($r('app.media.login_method2'))
this.imageButton($r('app.media.login_method3'))
}
这大大提高了我们的开发效率,设置这个函数我们还可以在其它页面被复用。
2.基于View-State的设计模式实现UI的刷新
我们可以自定义组件中根据需要修改自定义构造函数,单独或者和其他装饰器一起实现UI刷新的效果。