# BuilderNode 提供能够挂载原生组件的自定义节点BuilderNode。BuilderNode仅可作为叶子节点使用。使用方式参考[BuilderNode开发指南](../../ui/arkts-user-defined-arktsNode-builderNode.md)。 > **说明:** > > 本模块首批接口从API version 11开始支持。后续版本的新增接口,采用上角标单独标记接口的起始版本。 > > 如果在跨页面复用BuilderNode时显示异常,可参考[跨页面复用注意事项](../../ui/arkts-user-defined-arktsNode-builderNode.md#跨页面复用注意事项)。 > > 当前不支持在预览器中使用BuilderNode。 ## 导入模块 ```ts import { BuilderNode, RenderOptions, NodeRenderType } from "@kit.ArkUI"; ``` ## NodeRenderType 节点渲染类型枚举。 **原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 **系统能力:** SystemCapability.ArkUI.ArkUI.Full | 名称 | 值 | 说明 | | ------------------- | --- | ---------------------------- | | RENDER_TYPE_DISPLAY | 0 | 表示该节点将被显示到屏幕上。 | | RENDER_TYPE_TEXTURE | 1 | 表示该节点将被导出为纹理。 | > **说明:** > > RENDER_TYPE_TEXTURE类型目前仅在[BuilderNode](#buildernode-1)持有组件树的根节点为自定义组件时以及[XComponentNode](./js-apis-arkui-xcomponentNode.md)中设置生效。 > > 在[BuilderNode](#buildernode-1)的情况下,目前在作为根节点的自定义组件中支持纹理导出的有以下组件:Badge、Blank、Button、CanvasGradient、CanvasPattern、CanvasRenderingContext2D、Canvas、CheckboxGroup、Checkbox、Circle、ColumnSplit、Column、ContainerSpan、Counter、DataPanel、Divider、Ellipse、Flex、Gauge、Hyperlink、ImageBitmap、ImageData、Image、Line、LoadingProgress、Marquee、Matrix2D、OffscreenCanvasRenderingContext2D、OffscreenCanvas、Path2D、Path、PatternLock、Polygon、Polyline、Progress、QRCode、Radio、Rating、Rect、RelativeContainer、RowSplit、Row、Shape、Slider、Span、Stack、TextArea、TextClock、TextInput、TextTimer、Text、Toggle、Video(不支持原生的全屏模式)、Web、XComponent。 > > 从API version 12开始,新增以下组件支持纹理导出:DatePicker、ForEach、Grid、IfElse、LazyForEach、List、Scroll、Swiper、TimePicker、@Component修饰的自定义组件、NodeContainer以及NodeContainer下挂载的FrameNode和RenderNode。 > > 使用方式可参考[同层渲染绘制](../../web/web-same-layer.md)。 ## RenderOptions 创建BuilderNode时的可选参数。 **原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 **系统能力:** SystemCapability.ArkUI.ArkUI.Full | 名称 | 类型 | 必填 | 说明 | | ------------- | -------------------------------------- | ---- | ------------------------------------------------------------ | | selfIdealSize | [Size](js-apis-arkui-graphics.md#size) | 否 | 节点的理想大小。 | | type | [NodeRenderType](#noderendertype) | 否 | 节点的渲染类型。 | | surfaceId | string | 否 | 纹理接收方的surfaceId。纹理接收方一般为[OH_NativeImage](../apis-arkgraphics2d/_o_h___native_image.md#oh_nativeimage)。 | ## BuilderNode class BuilderNode\ BuilderNode支持通过无状态的UI方法[@Builder](../../quick-start/arkts-builder.md)生成组件树,并持有组件树的根节点。不支持定义为状态变量。BuilderNode中持有的FrameNode仅用于将该BuilderNode作为子节点挂载到其他FrameNode上。对BuilderNode持有的FrameNode进行属性设置与子节点操作可能会产生未定义行为,因此不建议通过BuilderNode的[getFrameNode](#getframenode)方法和[FrameNode](js-apis-arkui-frameNode.md#framenode)的[getRenderNode](js-apis-arkui-frameNode.md#getrendernode)方法获取RenderNode,并通过[RenderNode](js-apis-arkui-renderNode.md#rendernode)的接口对其进行属性设置与子节点操作。 **原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 **系统能力:** SystemCapability.ArkUI.ArkUI.Full ### constructor constructor(uiContext: UIContext, options?: RenderOptions) 当将BuilderNode生成的内容嵌入到其它RenderNode中显示时,即将BuilderNode对应的RenderNode挂载到另一个RenderNode中显示,需要显式指定RenderOptions中的selfIdealSize,否则Builder内的节点默认父组件布局约束为[0,0],即不设置selfIdealSize则认为BuilderNode中子树的根节点大小为[0,0]。 **原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 **系统能力:** SystemCapability.ArkUI.ArkUI.Full | 参数名 | 类型 | 必填 | 说明 | | --------- | --------------------------------------- | ---- | ----------------------------------------------------------------- | | uiContext | [UIContext](js-apis-arkui-UIContext.md) | 是 | UI上下文,获取方式可参考[UIContext获取方法](./js-apis-arkui-node.md#uicontext获取方法)。 | | options | [RenderOptions](#renderoptions) | 否 | BuilderNode的构造可选参数。默认值:undefined。 | > **说明** > uiContext的入参需要为一个有效的值,即UI上下文正确,如果传入非法值或者未设置,会导致创建失败。 ### build build(builder: WrappedBuilder\, arg?: Object): void 依照传入的对象创建组件树,并持有组件树的根节点。无状态的UI方法[@Builder](../../quick-start/arkts-builder.md)最多拥有一个根节点。 支持自定义组件。不支持自定义组件使用[@Reusable](../../quick-start/arkts-create-custom-components.md#自定义组件的基本结构)、[@Link](../../quick-start/arkts-link.md)、[@Provide](../../quick-start/arkts-provide-and-consume.md)、[@Consume](../../quick-start/arkts-provide-and-consume.md)等装饰器,来同步BuilderNode挂载的页面与BuilderNode中自定义组件的状态。 > **说明** > > @Builder嵌套使用的时候需要保证内外的@Builder方法的入参对象一致。 > > 最外层的@Builder只支持一个入参。 > > 需要操作BuilderNode中的对象时,需要保证其引用不被回收。当BuilderNode对象被虚拟机回收之后,它的FrameNode、RenderNode对象也会与后端节点解引用。即从BuilderNode中获取的FrameNode对象不对应任何一个节点。 **原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 **系统能力:** SystemCapability.ArkUI.ArkUI.Full **参数:** | 参数名 | 类型 | 必填 | 说明 | | ------- | --------------------------------------------------------------- | ---- | -------------------------------------------------------------------------------------- | | builder | [WrappedBuilder\](../../quick-start/arkts-wrapBuilder.md) | 是 | 创建对应节点树的时候所需的无状态UI方法[@Builder](../../quick-start/arkts-builder.md)。 | | arg | Object | 否 | builder的入参。当前仅支持一个入参,且入参对象类型与@Builder定义的入参类型保持一致。 | ### BuildOptions12+ build的可选参数。 **原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 **系统能力:** SystemCapability.ArkUI.ArkUI.Full | 名称 | 类型 | 必填 | 说明 | | ------------- | -------------------------------------- | ---- | ------------------------------------------------------------ | | nestingBuilderSupported |boolean | 否 | 是否支持Builder嵌套Builder进行使用。其中,false表示Builder使用的入参一致,true表示Builder使用的入参不一致。默认值:false。 | ### build12+ build(builder: WrappedBuilder\, arg: Object, options: [BuildOptions](#buildoptions12)): void 依照传入的对象创建组件树,并持有组件树的根节点。无状态的UI方法[@Builder](../../quick-start/arkts-builder.md)最多拥有一个根节点。 支持自定义组件。不支持使用自定义组件使用[@Reusable](../../quick-start/arkts-create-custom-components.md#自定义组件的基本结构)、[@Link](../../quick-start/arkts-link.md)、[@Provide](../../quick-start/arkts-provide-and-consume.md)、[@Consume](../../quick-start/arkts-provide-and-consume.md)等装饰器用于当前页面与自定义组件的状态同步。 > **说明** > > @Builder进行创建和更新的规格参考[@Builder](../../quick-start/arkts-builder.md)。 > > 最外层的@Builder只支持一个入参。 **原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 **系统能力:** SystemCapability.ArkUI.ArkUI.Full **参数:** | 参数名 | 类型 | 必填 | 说明 | | ------- | --------------------------------------------------------------- | ---- | -------------------------------------------------------------------------------------- | | builder | [WrappedBuilder\](../../quick-start/arkts-wrapBuilder.md) | 是 | 创建对应节点树的时候所需的无状态UI方法[@Builder](../../quick-start/arkts-builder.md)。 | | arg | Object | 是 | builder的入参。当前仅支持一个入参,且入参对象类型与@Builder定义的入参类型保持一致。 | | options | BuildOptions | 是 | build的配置参数,判断是否支持@Builder中嵌套@Builder的行为。 | **示例:** ```ts import { BuilderNode, NodeContent } from "@kit.ArkUI"; interface ParamsInterface { text: string; func: Function; } @Builder function buildTextWithFunc(fun: Function) { Text(fun()) .fontSize(50) .fontWeight(FontWeight.Bold) .margin({ bottom: 36 }) } @Builder function buildText(params: ParamsInterface) { Column() { Text(params.text) .fontSize(50) .fontWeight(FontWeight.Bold) .margin({ bottom: 36 }) buildTextWithFunc(params.func) } } @Entry @Component struct Index { @State message: string = "HELLO" private content: NodeContent = new NodeContent(); build() { Row() { Column() { Button('addBuilderNode') .onClick(() => { let buildNode = new BuilderNode<[ParamsInterface]>(this.getUIContext()); buildNode.build(wrapBuilder<[ParamsInterface]>(buildText), { text: this.message, func: () => { return "FUNCTION" } }, { nestingBuilderSupported: true }); this.content.addFrameNode(buildNode.getFrameNode()); buildNode.dispose(); }) ContentSlot(this.content) } .id("column") .width('100%') .height('100%') } .height('100%') } } ``` ### getFrameNode getFrameNode(): FrameNode | null 获取BuilderNode中的FrameNode。在BuilderNode执行build操作之后,才会生成FrameNode。 **原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 **系统能力:** SystemCapability.ArkUI.ArkUI.Full **返回值:** | 类型 | 说明 | | --------------------------------------------------------- | --------------------------------------------------------------------- | | [FrameNode](js-apis-arkui-frameNode.md#framenode) \| null | 一个FrameNode对象。若该BuilderNode不包含FrameNode,则返回空对象null。 | **示例1:** BuilderNode作为NodeContainer的根节点返回。 ```ts import { NodeController, BuilderNode, FrameNode, UIContext } from "@kit.ArkUI"; class Params { text: string = "" constructor(text: string) { this.text = text; } } @Builder function buildText(params: Params) { Column() { Text(params.text) .fontSize(50) .fontWeight(FontWeight.Bold) .margin({bottom: 36}) } } class TextNodeController extends NodeController { private textNode: BuilderNode<[Params]> | null = null; private message: string = "DEFAULT"; constructor(message: string) { super(); this.message = message; } makeNode(context: UIContext): FrameNode | null { this.textNode = new BuilderNode(context); this.textNode.build(wrapBuilder<[Params]>(buildText), new Params(this.message)) return this.textNode.getFrameNode(); } } @Entry @Component struct Index { @State message: string = "hello" build() { Row() { Column() { NodeContainer(new TextNodeController(this.message)) .width('100%') .height(100) .backgroundColor('#FFF0F0F0') } .width('100%') .height('100%') } .height('100%') } } ``` **示例2:** BuilderNode的FrameNode挂到其它FrameNode下。 ```ts import { NodeController, BuilderNode, FrameNode, UIContext } from "@kit.ArkUI"; class Params { text: string = "" constructor(text: string) { this.text = text; } } @Builder function buildText(params: Params) { Column() { Text(params.text) .fontSize(50) .fontWeight(FontWeight.Bold) .margin({ bottom: 36 }) } } class TextNodeController extends NodeController { private rootNode: FrameNode | null = null; private textNode: BuilderNode<[Params]> | null = null; private message: string = "DEFAULT"; constructor(message: string) { super(); this.message = message; } makeNode(context: UIContext): FrameNode | null { this.rootNode = new FrameNode(context); this.textNode = new BuilderNode(context, { selfIdealSize: { width: 150, height: 150 } }); this.textNode.build(wrapBuilder<[Params]>(buildText), new Params(this.message)); if (this.rootNode !== null) { this.rootNode.appendChild(this.textNode?.getFrameNode()); } return this.rootNode; } } @Entry @Component struct Index { @State message: string = "hello" build() { Row() { Column() { NodeContainer(new TextNodeController(this.message)) .width('100%') .height(100) .backgroundColor('#FFF0F0F0') } .width('100%') .height('100%') } .height('100%') } } ``` **示例3:** BuilderNode的RenderNode挂到其它RenderNode下。由于RenderNode不传递布局约束,不推荐通过该方式挂载节点。 ```ts import { NodeController, BuilderNode, FrameNode, UIContext, RenderNode } from "@kit.ArkUI"; class Params { text: string = "" constructor(text: string) { this.text = text; } } @Builder function buildText(params: Params) { Column() { Text(params.text) .fontSize(50) .fontWeight(FontWeight.Bold) .margin({ bottom: 36 }) } } class TextNodeController extends NodeController { private rootNode: FrameNode | null = null; private textNode: BuilderNode<[Params]> | null = null; private message: string = "DEFAULT"; constructor(message: string) { super(); this.message = message; } makeNode(context: UIContext): FrameNode | null { this.rootNode = new FrameNode(context); let renderNode = new RenderNode(); renderNode.clipToFrame = false; this.textNode = new BuilderNode(context, { selfIdealSize: { width: 150, height: 150 } }); this.textNode.build(wrapBuilder<[Params]>(buildText), new Params(this.message)); const textRenderNode = this.textNode?.getFrameNode()?.getRenderNode(); const rootRenderNode = this.rootNode.getRenderNode(); if (rootRenderNode !== null) { rootRenderNode.appendChild(renderNode); renderNode.appendChild(textRenderNode); } return this.rootNode; } } @Entry @Component struct Index { @State message: string = "hello" build() { Row() { Column() { NodeContainer(new TextNodeController(this.message)) .width('100%') .height(100) .backgroundColor('#FFF0F0F0') } .width('100%') .height('100%') } .height('100%') } } ``` ### update update(arg: Object): void 根据提供的参数更新BuilderNode,该参数为[build](#build)方法调用时传入的参数类型相同。对自定义组件进行update的时候需要在自定义组件中使用的变量定义为@Prop类型。 **原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 **系统能力:** SystemCapability.ArkUI.ArkUI.Full **参数:** | 参数名 | 类型 | 必填 | 说明 | | ------ | ------ | ---- | ------------------------------------------------------------------------ | | arg | Object | 是 | 用于更新BuilderNode的参数,和[build](#build)调用时传入的参数类型一致。 | **示例:** ```ts import { NodeController, BuilderNode, FrameNode, UIContext } from "@kit.ArkUI"; class Params { text: string = "" constructor(text: string) { this.text = text; } } // 自定义组件 @Component struct TextBuilder { @Prop message: string = "TextBuilder"; build() { Row() { Column() { Text(this.message) .fontSize(50) .fontWeight(FontWeight.Bold) .margin({bottom: 36}) .backgroundColor(Color.Gray) } } } } @Builder function buildText(params: Params) { Column() { Text(params.text) .fontSize(50) .fontWeight(FontWeight.Bold) .margin({ bottom: 36 }) TextBuilder({message: params.text}) // 自定义组件 } } class TextNodeController extends NodeController { private rootNode: FrameNode | null = null; private textNode: BuilderNode<[Params]> | null = null; private message: string = ""; constructor(message: string) { super() this.message = message } makeNode(context: UIContext): FrameNode | null { this.textNode = new BuilderNode(context); this.textNode.build(wrapBuilder<[Params]>(buildText), new Params(this.message)) return this.textNode.getFrameNode(); } update(message: string) { if (this.textNode !== null) { this.textNode.update(new Params(message)); } } } @Entry @Component struct Index { @State message: string = "hello" private textNodeController: TextNodeController = new TextNodeController(this.message); private count = 0; build() { Row() { Column() { NodeContainer(this.textNodeController) .width('100%') .height(200) .backgroundColor('#FFF0F0F0') Button('Update') .onClick(() => { this.count += 1; const message = "Update " + this.count.toString(); this.textNodeController.update(message); }) } .width('100%') .height('100%') } .height('100%') } } ``` ### postTouchEvent postTouchEvent(event: TouchEvent): boolean 将原始事件派发到某个BuilderNode创建出的FrameNode上。 postTouchEvent是从组件树的中间节点往下分发,需要变换到父组件坐标系才能分发成功,参考下图。 OffsetA为buildNode相对于父组件的偏移量,可以通过FrameNode中的[getPositionToParent](js-apis-arkui-frameNode.md#getpositiontoparent12)获取。OffsetB为point点相对于buildNode的偏移量,可以通过[TouchEvent](arkui-ts/ts-universal-events-touch.md#touchevent对象说明) 获取。OffsetC为OffsetA与OffsetB的和,是传给postTouchEvent的最终结果。 ![postTouchEvent](figures/postTouchEvent.PNG) > **说明:** > > 传入的坐标值需要转换为px,如果builderNode有仿射变换,则需要再叠加仿射变换。 > > 在[webview](../apis-arkweb/js-apis-webview.md)中,内部已经处理过坐标系变换,可以将TouchEvent事件直接下发。 > > 同一时间戳,postTouchEvent只能调用一次。 > > 不支持[UIExtensionComponent](arkui-ts/ts-container-ui-extension-component-sys.md)。 **原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 **系统能力:** SystemCapability.ArkUI.ArkUI.Full **参数:** | 参数名 | 类型 | 必填 | 说明 | | ------ | ------------------------------------------------------------------------- | ---- | ---------- | | event | [TouchEvent](arkui-ts/ts-universal-events-touch.md#touchevent对象说明) | 是 | 触摸事件。 | **返回值:** | 类型 | 说明 | | ------- | ------------------ | | boolean | 派发事件是否成功。true为已命中响应事件的组件,false为未命中任何可响应事件的组件。
**说明:**
如果未按照预期命中组件,需要确认以下几点:
1.坐标系是否转换正确。
2.组件是否可交互状态。
3.是否绑定事件。 | **示例:** ```ts import { NodeController, BuilderNode, FrameNode, UIContext } from '@kit.ArkUI'; class Params { text: string = "this is a text" } @Builder function ButtonBuilder(params: Params) { Column() { Button(`button ` + params.text) .borderWidth(2) .backgroundColor(Color.Orange) .width("100%") .height("100%") .gesture( TapGesture() .onAction((event: GestureEvent) => { console.log("TapGesture"); }) ) } .width(500) .height(300) .backgroundColor(Color.Gray) } class MyNodeController extends NodeController { private rootNode: BuilderNode<[Params]> | null = null; private wrapBuilder: WrappedBuilder<[Params]> = wrapBuilder(ButtonBuilder); makeNode(uiContext: UIContext): FrameNode | null { this.rootNode = new BuilderNode(uiContext); this.rootNode.build(this.wrapBuilder, { text: "this is a string" }) return this.rootNode.getFrameNode(); } // 坐标转换示例 postTouchEvent(event: TouchEvent): boolean { if (this.rootNode == null) { return false; } let node: FrameNode | null = this.rootNode.getFrameNode(); let offsetX: number | null | undefined = node?.getPositionToParent().x; let offsetY: number | null | undefined = node?.getPositionToParent().y; ; let changedTouchLen = event.changedTouches.length; for (let i = 0; i < changedTouchLen; i++) { if (offsetX != null && offsetY != null && offsetX != undefined && offsetY != undefined) { event.changedTouches[i].x = vp2px(offsetX + event.changedTouches[i].x); event.changedTouches[i].y = vp2px(offsetY + event.changedTouches[i].y); } } let result = this.rootNode.postTouchEvent(event); console.log("result " + result); return result; } } @Entry @Component struct MyComponent { private nodeController: MyNodeController = new MyNodeController(); build() { Column() { NodeContainer(this.nodeController) .height(300) .width(500) Column() .width(500) .height(300) .backgroundColor(Color.Pink) .onTouch((event) => { if (event != undefined) { this.nodeController.postTouchEvent(event); } }) } } } ``` ### dispose12+ dispose(): void 立即释放当前BuilderNode。当BuilderNode对象调用dispose接口之后,不仅BuilderNode对象与后端实体节点解除引用关系,BuilderNode中的FrameNode与RenderNode也会同步和实体节点解除引用关系。 **原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 **系统能力:** SystemCapability.ArkUI.ArkUI.Full ```ts import { RenderNode, FrameNode, NodeController, BuilderNode } from "@kit.ArkUI"; @Component struct TestComponent { build() { Column() { Text('This is a BuilderNode.') .fontSize(16) .fontWeight(FontWeight.Bold) } .width('100%') .backgroundColor(Color.Gray) } aboutToAppear() { console.error('aboutToAppear'); } aboutToDisappear() { console.error('aboutToDisappear'); } } @Builder function buildComponent() { TestComponent() } class MyNodeController extends NodeController { private rootNode: FrameNode | null = null; private builderNode: BuilderNode<[]> | null = null; makeNode(uiContext: UIContext): FrameNode | null { this.rootNode = new FrameNode(uiContext); this.builderNode = new BuilderNode(uiContext, { selfIdealSize: { width: 200, height: 100 } }); this.builderNode.build(new WrappedBuilder(buildComponent)); const rootRenderNode = this.rootNode!.getRenderNode(); if (rootRenderNode !== null) { rootRenderNode.size = { width: 200, height: 200 }; rootRenderNode.backgroundColor = 0xff00ff00; rootRenderNode.appendChild(this.builderNode!.getFrameNode()!.getRenderNode()); } return this.rootNode; } dispose() { if (this.builderNode !== null) { this.builderNode.dispose(); } } removeBuilderNode() { const rootRenderNode = this.rootNode!.getRenderNode(); if (rootRenderNode !== null && this.builderNode !== null && this.builderNode.getFrameNode() !== null) { rootRenderNode.removeChild(this.builderNode!.getFrameNode()!.getRenderNode()); } } } @Entry @Component struct Index { private myNodeController: MyNodeController = new MyNodeController(); build() { Column({ space: 4 }) { NodeContainer(this.myNodeController) Button('BuilderNode dispose') .onClick(() => { this.myNodeController.removeBuilderNode(); this.myNodeController.dispose(); }) .width('100%') } } } ``` ### reuse12+ reuse(param?: Object): void 传递reuse事件到BuilderNode中的自定义组件。 **原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 **系统能力:** SystemCapability.ArkUI.ArkUI.Full **参数:** | 参数名 | 类型 | 必填 | 说明 | | ------ | ------ | ---- | ------------------------------------------------------------------------ | | param | Object | 否 | 用于复用BuilderNode的参数,和[build](#build)调用时传入的参数类型一致。 | ### recycle12+ recycle(): void 传递recycle事件到BuilderNode中的自定义组件。 **原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 **系统能力:** SystemCapability.ArkUI.ArkUI.Full ```ts import { FrameNode,NodeController,BuilderNode,UIContext } from "@kit.ArkUI"; class MyDataSource { private dataArray: string[] = []; private listener: DataChangeListener | null = null public totalCount(): number { return this.dataArray.length; } public getData(index: number) { return this.dataArray[index]; } public pushData(data: string) { this.dataArray.push(data); } public reloadListener(): void { this.listener?.onDataReloaded(); } public registerDataChangeListener(listener: DataChangeListener): void { this.listener = listener; } public unregisterDataChangeListener(): void { this.listener = null; } } class Params { item: string = ''; constructor(item: string) { this.item = item; } } @Builder function buildNode(param: Params = new Params("hello")) { ReusableChildComponent2({ item: param.item }); } class MyNodeController extends NodeController { public builderNode: BuilderNode<[Params]> | null = null; public item: string = ""; makeNode(uiContext: UIContext): FrameNode | null { if (this.builderNode == null) { this.builderNode = new BuilderNode(uiContext, { selfIdealSize: { width: 300, height: 200 } }); this.builderNode.build(wrapBuilder<[Params]>(buildNode), new Params(this.item)); } return this.builderNode.getFrameNode(); } } @Reusable @Component struct ReusableChildComponent { @State item: string = ''; private controller: MyNodeController = new MyNodeController(); aboutToAppear() { this.controller.item = this.item; } aboutToRecycle(): void { console.log("ReusableChildComponent aboutToRecycle " + this.item); this.controller?.builderNode?.recycle(); } aboutToReuse(params: object): void { console.log("ReusableChildComponent aboutToReuse " + JSON.stringify(params)); this.controller?.builderNode?.reuse(params); } build() { NodeContainer(this.controller); } } @Component struct ReusableChildComponent2 { @Prop item: string = "false"; aboutToReuse(params: Record) { console.log("ReusableChildComponent2 Reusable 2 " + JSON.stringify(params)); } aboutToRecycle(): void { console.log("ReusableChildComponent2 aboutToRecycle 2 " + this.item); } build() { Row() { Text(this.item) .fontSize(20) .backgroundColor(Color.Yellow) .margin({ left: 10 }) }.margin({ left: 10, right: 10 }) } } @Entry @Component struct Index { @State data: MyDataSource = new MyDataSource(); aboutToAppear() { for (let i = 0;i < 100; i++) { this.data.pushData(i.toString()); } } build() { Column() { List({ space: 3 }) { LazyForEach(this.data, (item: string) => { ListItem() { ReusableChildComponent({ item: item }) } }, (item: string) => item) } .width('100%') .height('100%') } } } ``` ### updateConfiguration12+ updateConfiguration(): void 传递[系统环境变化](../apis-ability-kit/js-apis-app-ability-configuration.md)事件,触发节点的全量更新。 **原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 **系统能力:** SystemCapability.ArkUI.ArkUI.Full > **说明:** > > updateConfiguration接口用于通知对象更新,更新所使用的系统环境由应用当前的系统环境变化决定。 **示例:** ```ts import { NodeController, BuilderNode, FrameNode, UIContext } from "@kit.ArkUI"; import { AbilityConstant, Configuration, EnvironmentCallback } from '@kit.AbilityKit'; class Params { text: string = "" constructor(text: string) { this.text = text; } } // 自定义组件 @Component struct TextBuilder { // 作为自定义组件中需要更新的属性,数据类型为基础属性,定义为@Prop @Prop message: string = "TextBuilder"; build() { Row() { Column() { Text(this.message) .fontSize(50) .fontWeight(FontWeight.Bold) .margin({ bottom: 36 }) .fontColor($r(`app.color.text_color`)) .backgroundColor($r(`app.color.start_window_background`)) } } } } @Builder function buildText(params: Params) { Column() { Text(params.text) .fontSize(50) .fontWeight(FontWeight.Bold) .margin({ bottom: 36 }) .fontColor($r(`app.color.text_color`)) TextBuilder({ message: params.text }) // 自定义组件 }.backgroundColor($r(`app.color.start_window_background`)) } class TextNodeController extends NodeController { private textNode: BuilderNode<[Params]> | null = null; private message: string = ""; constructor(message: string) { super() this.message = message; } makeNode(context: UIContext): FrameNode | null { return this.textNode?.getFrameNode() ? this.textNode?.getFrameNode() : null; } createNode(context: UIContext) { this.textNode = new BuilderNode(context); this.textNode.build(wrapBuilder<[Params]>(buildText), new Params(this.message)); builderNodeMap.push(this.textNode); } deleteNode() { let node = builderNodeMap.pop(); node?.dispose(); } update(message: string) { if (this.textNode !== null) { // 调用update进行更新。 this.textNode.update(new Params(message)); } } } // 记录创建的自定义节点对象 const builderNodeMap: Array> = new Array(); function updateColorMode() { builderNodeMap.forEach((value, index) => { // 通知BuilderNode环境变量改变 value.updateConfiguration(); }) } @Entry @Component struct Index { @State message: string = "hello" private textNodeController: TextNodeController = new TextNodeController(this.message); private count = 0; aboutToAppear(): void { let environmentCallback: EnvironmentCallback = { onMemoryLevel: (level: AbilityConstant.MemoryLevel): void => { console.log('onMemoryLevel'); }, onConfigurationUpdated: (config: Configuration): void => { console.log('onConfigurationUpdated ' + JSON.stringify(config)); updateColorMode(); } } // 注册监听回调 this.getUIContext().getHostContext()?.getApplicationContext().on('environment', environmentCallback); //创建自定义节点并添加至map this.textNodeController.createNode(this.getUIContext()); } aboutToDisappear(): void { //移除map中的引用,并将自定义节点释放 this.textNodeController.deleteNode(); } build() { Row() { Column() { NodeContainer(this.textNodeController) .width('100%') .height(200) .backgroundColor('#FFF0F0F0') Button('Update') .onClick(() => { this.count += 1; const message = "Update " + this.count.toString(); this.textNodeController.update(message); }) } .width('100%') .height('100%') } .height('100%') } } ```