当在一个自定义组件内使用多个全局@Builder函数实现UI的不同效果时,代码维护将变得非常困难,且页面不够整洁。此时,可以使用wrapBuilder封装全局@Builder。但是wrapBuilder不支持动态切换@Builder,引入mutableBuilder实现全局@Builder的动态切换。
从API version 22开始,开发者可以使用mutableBuilder实现全局@Builder的动态切换。
从API version 22开始,mutableBuilder支持在元服务中使用。
当前wrapBuilder不支持二次赋值, 更改@Builder,UI不会发生变化。
class TextContent {
text: string = '';
}
@Builder
function textBuilder(p: TextContent) {
Text(p.text)
}
@Builder
function buttonBuilder(p: TextContent) {
Button(p.text)
}
@Entry
@Component
struct Index {
@State message: string = 'init';
@State text: WrappedBuilder<[TextContent]> = wrapBuilder(textBuilder); // 使用textBuilder初始化
build() {
Column() {
this.text.builder({ text: this.message })
Button().onClick(() => {
this.text = wrapBuilder(buttonBuilder); // 点击Button,将textBuilder替换为buttonBuilder进行二次赋值
})
}
}
} 在上述代码中,使用textBuilder初始化wrapBuilder,点击Button的onClick事件,使用buttonBuilder再次初始化wrapBuilder,不会触发对应的@Builder的更新。
为了解决这一问题,引入mutableBuilder作为动态全局@Builder封装函数。mutableBuilder返回MutableBuilder对象,用于全局@Builder的动态刷新。
mutableBuilder是一个模板函数,返回一个MutableBuilder对象。相比WrappedBuilder,MutableBuilder可以实现动态切换全局@Builder。
declare function mutableBuilder<Args extends Object[]>(builder: BuilderCallback): MutableBuilder<Args>;
同时MutableBuilder是一个模板类,继承自WrappedBuilder。
declare class MutableBuilder<Args extends Object[]> extends WrappedBuilder<Args> {
}
模板参数Args extends Object[]需要匹配@Builder函数参数的类型。
使用方法:
let builderVar: MutableBuilder<[string, number]> = mutableBuilder(MyBuilder); let builderArr: MutableBuilder<[string, number]>[] = [mutableBuilder(MyBuilder)]; // mutableBuilder支持放入数组中
mutableBuilder方法只能传入全局@Builder方法,传入局部@Builder方法编译时报错。
class TextContent {
text: string = '';
}
@Builder
function globalBuilder(p: TextContent) {
Text(p.text)
}
@ComponentV2
struct MyApp {
@Local message: string = 'init';
// 正确用法,使用全局@Builder
@Local switchingBuilder: MutableBuilder<[TextContent]> = mutableBuilder(globalBuilder);
// 错误用法,使用局部@Builder,编译报错
@Local localBuilderObject: MutableBuilder<[TextContent]> = mutableBuilder(this.localBuilder);
@Builder
localBuilder(p: TextContent) {
Text(p.text)
}
build() {
Column() {
this.switchingBuilder.builder({ text: this.message })
}
}
} MutableBuilder对象的builder属性方法仅限在自定义组件内部使用,在自定义组件外面使用会导致程序运行时崩溃。
class TextContent {
text: string = '';
}
@Builder
function globalBuilder(p: TextContent) {
Text(p.text)
}
// 错误用法,MutableBuilder对象的builder属性方法在自定义组件外面使用,运行时崩溃
let outSideBuilder: MutableBuilder<[TextContent]> = mutableBuilder(globalBuilder);
outSideBuilder.builder({ text: 'message' });
@ComponentV2
struct MyApp {
@Local message: string = 'init';
@Local switchingBuilder: MutableBuilder<[TextContent]> = mutableBuilder(globalBuilder);
build() {
Column() {
// 正确用法,MutableBuilder对象的builder属性方法在自定义组件中使用
this.switchingBuilder.builder({ text: this.message })
}
}
} 不建议与wrapBuilder混合使用,因为mutableBuilder创建的对象类型是MutableBuilder类型,会导致不符合预期的更新。
如下为不推荐的用法:
// 在实例化MutableBuilder对象时,建议使用mutableBuilder(builderName)方法
@State switchingBuilder: MutableBuilder<[MutableBinding]> = mutableBuilder(textBuilder);
// 不支持将MutableBuilder类型的变量赋值为undefined或null,会导致运行时crash
@State switchingBuilder: MutableBuilder<[MutableBinding]> | undefined | null = null;
Button(`MutableBuilder`).onClick(() => {
// 不建议将wrapBuilder创建的对象赋值给MutableBuilder类型的对象,赋值后会将textBuilder动态切换成buttonBuilder
this.switchingBuilder = wrapBuilder(buttonBuilder);
}) 如下为推荐用法:
// 在实例化MutableBuilder对象时,建议使用mutableBuilder(builderName)方法
@State switchingBuilder: MutableBuilder<[MutableBinding]> = mutableBuilder(textBuilder);
Button(`MutableBuilder`).onClick(() => {
// 赋值会将textBuilder动态切换成buttonBuilder
this.switchingBuilder = mutableBuilder(buttonBuilder); // 推荐用法
}) 使用@Builder装饰器装饰的方法textBuilder作为mutableBuilder的参数,然后将mutableBuilder的返回值赋值给变量switchingBuilder,在Button的点击事件中,使用@Builder装饰器装饰的方法buttonBuilder作为mutableBuilder的参数,将mutableBuilder的返回值再次赋值给变量switchingBuilder,可实现textBuilder 更新为buttonBuilder,以解决wrapBuilder不支持二次赋值的问题。
class TextContent {
public text: string = '';
}
@Builder
function textBuilder(p: TextContent) {
Text(p.text).margin(20)
}
@Builder
function buttonBuilder(p: TextContent) {
Button(p.text).margin(20)
}
let counter: number = 1;
@Entry
@ComponentV2
struct MyApp {
@Local message: string = 'init';
@Local switchingBuilder: MutableBuilder<[TextContent]> = mutableBuilder(textBuilder);
build() {
Column() {
this.switchingBuilder.builder({ text: this.message })
Button('Click to change')
.onClick(() => {
counter++; // 每次点击按钮修改counter来动态改变全局@Builder
if(counter % 2 === 0) {
this.message += 'B';
this.switchingBuilder = mutableBuilder(buttonBuilder); // textBuilder--->buttonBuilder
} else {
this.message += 'T';
this.switchingBuilder = mutableBuilder(textBuilder); // buttonBuilder--->textBuilder
}
})
}.position({x: 120, y: 60})
}
} 点击Button,可将textBuilder动态更改为buttonBuilder,如下图所示:

由于MutableBuilder继承自WrappedBuilder,故mutableBuilder对应的@Builder具有跟WrappedBuilder同等能力,如下示例,mutableBuilder对应的@Builder方法可作为bindMenu入参,支持点击弹出菜单。
@Builder
function overBuilder() {
Row() {
Text('全局 Builder')
.fontSize(30)
.fontWeight(FontWeight.Bold)
}
}
@Entry
@Component
struct Index {
@State arr: number[] = [1,2,3,4,5];
mutableBuilderMenu: MutableBuilder<[]> = mutableBuilder<[]>(overBuilder);
build() {
Column() {
List({ space: 10 }) {
ForEach(this.arr, (item: number) => {
ListItem() {
Text(`${item}`)
.width('100%')
.height(100)
.fontSize(16)
.textAlign(TextAlign.Center)
.borderRadius(10)
.backgroundColor(0xFFFFFF)
}
// 使用mutableBuilder显示弹出菜单
.bindMenu(this.mutableBuilderMenu.builder)
}, (item: number) => JSON.stringify(item))
}
}
}
} mutableBuilder对应的@Builder函数中可使用MutableBinding进行包裹来观察状态变量的变化,同时可通过@Monitor或addMonitor监听mutableBuilder中@Builder的变化。
import { UIUtils, MutableBinding } from '@kit.ArkUI';
@Builder
function textBuilder(p: MutableBinding<string>) {
Text(p.value)
.margin(20)
.onClick(() => {
p.value += 't';
})
}
@Builder
function buttonBuilder(p: MutableBinding<string>) {
Button(p.value)
.margin(20)
.onClick(() => {
p.value += 'b';
})
}
let counter: number = 1;
@Entry
@ComponentV2
struct MyApp {
@Local message: string = 'init';
@Local switchingBuilder: MutableBuilder<[MutableBinding<string>]> = mutableBuilder(textBuilder);
@Monitor('switchingBuilder') variableChange(m: IMonitor): void {
console.info(`Builder changed. is buttonBuilder: ${m.value<MutableBuilder<[MutableBinding<string>]>>()?.now.builder === buttonBuilder}`);
}
build() {
Column() {
this.switchingBuilder.builder(UIUtils.makeBinding(()=> this.message, txt => this.message = txt))
Button('Click to change')
.onClick(() => {
counter++;
if(counter % 2 === 0) {
this.message += 'B';
this.switchingBuilder = mutableBuilder(buttonBuilder); // textBuilder--->buttonBuilder,@Monitor会触发回调
} else {
this.message += 'T';
this.switchingBuilder = mutableBuilder(textBuilder); // buttonBuilder--->textBuilder,@Monitor会触发回调
}
})
}.position({x: 120, y: 60})
}
} 点击Click to change按钮,可将textBuilder动态切换为buttonBuilder,this.message将自动加B,界面会显示initB按钮。点击initB按钮,buttonBuilder中的p.value将自动加b,如下图所示:

点击Click to change按钮将textBuilder动态切换为buttonBuilder时,@Monitor将监听到全局@Builder的变化,并打印日志Builder changed. is buttonBuilder: true。