1# BuilderNode 2 3The **BuilderNode** module provides APIs for a BuilderNode – a custom node that can be used to mount native components. A BuilderNode can be used only as a leaf node. For details, see [BuilderNode Development](../../ui/arkts-user-defined-arktsNode-builderNode.md). 4 5> **NOTE** 6> 7> The initial APIs of this module are supported since API version 11. Newly added APIs will be marked with a superscript to indicate their earliest API version. 8> 9> If you encounter display issues when reusing a BuilderNode across pages, see [Cross-Page Reuse Considerations](../../ui/arkts-user-defined-arktsNode-builderNode.md#cross-page-reuse-considerations) for guidance. 10> 11> **BuilderNode** is not available in DevEco Studio Previewer. 12 13## Modules to Import 14 15```ts 16import { BuilderNode, RenderOptions, NodeRenderType } from "@kit.ArkUI"; 17``` 18 19## NodeRenderType 20 21Enumerates the node rendering types. 22 23**Atomic service API**: This API can be used in atomic services since API version 12. 24 25**System capability**: SystemCapability.ArkUI.ArkUI.Full 26 27| Name | Value | Description | 28| ------------------- | --- | ---------------------------- | 29| RENDER_TYPE_DISPLAY | 0 | The node is displayed on the screen.| 30| RENDER_TYPE_TEXTURE | 1 | The node is exported as a texture. | 31 32> **NOTE** 33> 34> Currently, the **RENDER_TYPE_TEXTURE** type takes effect only for the [XComponentNode](./js-apis-arkui-xcomponentNode.md) and the [BuilderNode](#buildernode-1) holding a component tree whose root node is a custom component. 35> 36> In the case of [BuilderNode](#buildernode-1), the following custom components that function as the root node support texture export: 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 (not supporting the native full-screen mode), Web, XComponent 37> 38> The following components support texture export since API version 12: DatePicker, ForEach, Grid, IfElse, LazyForEach, List, Scroll, Swiper, TimePicker, @Component decorated custom components, NodeContainer, and FrameNode and RenderNode mounted to a NodeContainer. 39> 40> For details, see [Rendering and Drawing Video and Button Components at the Same Layer](../../web/web-same-layer.md). 41 42## RenderOptions 43 44Provides optional parameters for creating a BuilderNode. 45 46**Atomic service API**: This API can be used in atomic services since API version 12. 47 48**System capability**: SystemCapability.ArkUI.ArkUI.Full 49 50| Name | Type | Mandatory| Description | 51| ------------- | -------------------------------------- | ---- | ------------------------------------------------------------ | 52| selfIdealSize | [Size](js-apis-arkui-graphics.md#size) | No | Ideal size of the node. | 53| type | [NodeRenderType](#noderendertype) | No | Rendering type of the node. | 54| surfaceId | string | No | Surface ID of the texture receiver. Generally, the texture receiver is an [OH_NativeImage](../apis-arkgraphics2d/_o_h___native_image.md#oh_nativeimage) instance.| 55 56## BuilderNode 57 58class BuilderNode\<Args extends Object[]> 59 60Implements a BuilderNode, which can create a component tree through the stateless UI method [@Builder](../../quick-start/arkts-builder.md) and hold the root node of the component tree. A BuilderNode cannot be defined as a state variable. The FrameNode held in the BuilderNode is only used to mount the BuilderNode to other FrameNodes as a child node. Undefined behavior may occur if you set attributes or perform operations on subnodes of the FrameNode held by the BuilderNode. Therefore, after you have obtained a [RenderNode](js-apis-arkui-renderNode.md#rendernode) through the [getFrameNode](#getframenode) method of the BuilderNode and the [getRenderNode](js-apis-arkui-frameNode.md#getrendernode) method of the [FrameNode](js-apis-arkui-frameNode.md#framenode), avoid setting the attributes or operating the subnodes through APIs of the [RenderNode](js-apis-arkui-renderNode.md#rendernode). 61 62**Atomic service API**: This API can be used in atomic services since API version 12. 63 64**System capability**: SystemCapability.ArkUI.ArkUI.Full 65 66### constructor 67 68constructor(uiContext: UIContext, options?: RenderOptions) 69 70Constructor for creating a BuilderNode. When the content generated by the BuilderNode is embedded in another RenderNode for display, that is, the RenderNode corresponding to the BuilderNode is mounted to another RenderNode for display, **selfIdealSize** in **RenderOptions** must be explicitly specified. If **selfIdealSize** is not set, the node in the builder follows the default parent component layout constraint [0, 0], which means that the size of the root node of the subtree in BuilderNode is [0, 0]. 71 72**Atomic service API**: This API can be used in atomic services since API version 12. 73 74**System capability**: SystemCapability.ArkUI.ArkUI.Full 75 76| Name | Type | Mandatory| Description | 77| --------- | --------------------------------------- | ---- | ----------------------------------------------------------------- | 78| uiContext | [UIContext](js-apis-arkui-UIContext.md) | Yes | UI context. For details about how to obtain it, see [Obtaining UI Context](./js-apis-arkui-node.md#obtaining-ui-context).| 79| options | [RenderOptions](#renderoptions) | No | Parameters for creating a BuilderNode. Default value: **undefined** | 80 81> **NOTE** 82> The input parameter for **uiContext** must be a valid value, that is, the UI context must be correct. If an invalid value is passed in or if no value is specified, creation will fail. 83 84### build 85 86build(builder: WrappedBuilder\<Args>, arg?: Object): void 87 88Creates a component tree based on the passed object and holds the root node of the component tree. The stateless UI method [@Builder](../../quick-start/arkts-builder.md) has at most one root node. 89Custom components are allowed. Yet, the custom components cannot use decorators, such as [@Reusable](../../quick-start/arkts-create-custom-components.md#basic-structure-of-a-custom-component), [@Link](../../quick-start/arkts-link.md), [@Provide](../../quick-start/arkts-provide-and-consume.md), and [@Consume](../../quick-start/arkts-provide-and-consume.md), for state synchronization with the page to which the BuilderNode is mounted. 90 91> **NOTE** 92> 93> When nesting @Builder, ensure that the input objects for the inner and outer @Builder methods are consistent. 94> 95> The outermost @Builder supports only one input argument. 96> 97> To operate objects in a BuilderNode, ensure that the reference to the BuilderNode is not garbage collected. Once a BuilderNode object is collected by the virtual machine, its FrameNode and RenderNode objects will also be dereferenced from the backend nodes. This means that any FrameNode objects obtained from a BuilderNode will no longer correspond to any actual node if the BuilderNode is garbage collected. 98 99**Atomic service API**: This API can be used in atomic services since API version 12. 100 101**System capability**: SystemCapability.ArkUI.ArkUI.Full 102 103**Parameters** 104 105| Name | Type | Mandatory| Description | 106| ------- | --------------------------------------------------------------- | ---- | -------------------------------------------------------------------------------------- | 107| builder | [WrappedBuilder\<Args>](../../quick-start/arkts-wrapBuilder.md) | Yes | Stateless UI method [@Builder](../../quick-start/arkts-builder.md) required for creating a component tree.| 108| arg | Object | No | Argument of the builder. Only one input argument is supported, and the type of the input argument must be consistent with the type defined by @Builder. | 109 110 111### BuildOptions<sup>12+</sup> 112 113Defines the optional build options. 114 115**Atomic service API**: This API can be used in atomic services since API version 12. 116 117**System capability**: SystemCapability.ArkUI.ArkUI.Full 118 119| Name | Type | Mandatory| Description | 120| ------------- | -------------------------------------- | ---- | ------------------------------------------------------------ | 121| nestingBuilderSupported |boolean | No | Whether to support nesting **@Builder** within **@Builder**. The value **false** means that the input arguments for **@Builder** are consistent, and **true** means the opposite.<br> Default value: **false** | 122 123### build<sup>12+</sup> 124 125build(builder: WrappedBuilder\<Args>, arg: Object, options: [BuildOptions](#buildoptions12)): void 126 127Creates a component tree based on the passed object and holds the root node of the component tree. The stateless UI method [@Builder](../../quick-start/arkts-builder.md) has at most one root node. 128Custom components are allowed. Yet, the custom components cannot use decorators, such as [@Reusable](../../quick-start/arkts-create-custom-components.md#basic-structure-of-a-custom-component), [@Link](../../quick-start/arkts-link.md), [@Provide](../../quick-start/arkts-provide-and-consume.md), and [@Consume](../../quick-start/arkts-provide-and-consume.md), for state synchronization with the owning page. 129 130> **NOTE** 131> 132> For details about the creation and update using @Builder, see [@Builder](../../quick-start/arkts-builder.md). 133> 134> The outermost @Builder supports only one input argument. 135 136**Atomic service API**: This API can be used in atomic services since API version 12. 137 138**System capability**: SystemCapability.ArkUI.ArkUI.Full 139 140**Parameters** 141 142| Name | Type | Mandatory| Description | 143| ------- | --------------------------------------------------------------- | ---- | -------------------------------------------------------------------------------------- | 144| builder | [WrappedBuilder\<Args>](../../quick-start/arkts-wrapBuilder.md) | Yes | Stateless UI method [@Builder](../../quick-start/arkts-builder.md) required for creating a component tree. | 145| arg | Object | Yes | Argument of the builder. Only one input argument is supported, and the type of the input argument must be consistent with the type defined by @Builder. | 146| options | BuildOptions | Yes | Build options, which determine whether to support nesting **@Builder** within **@Builder**. | 147 148**Example** 149```ts 150import { BuilderNode, NodeContent } from "@kit.ArkUI"; 151 152interface ParamsInterface { 153 text: string; 154 func: Function; 155} 156 157@Builder 158function buildTextWithFunc(fun: Function) { 159 Text(fun()) 160 .fontSize(50) 161 .fontWeight(FontWeight.Bold) 162 .margin({ bottom: 36 }) 163} 164 165@Builder 166function buildText(params: ParamsInterface) { 167 Column() { 168 Text(params.text) 169 .fontSize(50) 170 .fontWeight(FontWeight.Bold) 171 .margin({ bottom: 36 }) 172 buildTextWithFunc(params.func) 173 } 174} 175 176 177@Entry 178@Component 179struct Index { 180 @State message: string = "HELLO" 181 private content: NodeContent = new NodeContent(); 182 183 build() { 184 Row() { 185 Column() { 186 Button('addBuilderNode') 187 .onClick(() => { 188 let buildNode = new BuilderNode<[ParamsInterface]>(this.getUIContext()); 189 buildNode.build(wrapBuilder<[ParamsInterface]>(buildText), { 190 text: this.message, func: () => { 191 return "FUNCTION" 192 } 193 }, { nestingBuilderSupported: true }); 194 this.content.addFrameNode(buildNode.getFrameNode()); 195 buildNode.dispose(); 196 }) 197 ContentSlot(this.content) 198 } 199 .id("column") 200 .width('100%') 201 .height('100%') 202 } 203 .height('100%') 204 } 205} 206``` 207 208 209### getFrameNode 210 211getFrameNode(): FrameNode | null 212 213Obtains the FrameNode in the BuilderNode. The FrameNode is generated only after the BuilderNode executes the build operation. 214 215**Atomic service API**: This API can be used in atomic services since API version 12. 216 217**System capability**: SystemCapability.ArkUI.ArkUI.Full 218 219**Return value** 220 221| Type | Description | 222| --------------------------------------------------------- | --------------------------------------------------------------------- | 223| [FrameNode](js-apis-arkui-frameNode.md#framenode) \| null | **FrameNode** object. If no such object is held by the **BuilderNode** instance, null is returned.| 224 225**Example 1** 226 227In this example, the BuilderNode is returned as the root node of the **NodeContainer**. 228 229```ts 230import { NodeController, BuilderNode, FrameNode, UIContext } from "@kit.ArkUI"; 231 232class Params { 233 text: string = "" 234 constructor(text: string) { 235 this.text = text; 236 } 237} 238 239@Builder 240function buildText(params: Params) { 241 Column() { 242 Text(params.text) 243 .fontSize(50) 244 .fontWeight(FontWeight.Bold) 245 .margin({bottom: 36}) 246 } 247} 248 249class TextNodeController extends NodeController { 250 private textNode: BuilderNode<[Params]> | null = null; 251 private message: string = "DEFAULT"; 252 253 constructor(message: string) { 254 super(); 255 this.message = message; 256 } 257 258 makeNode(context: UIContext): FrameNode | null { 259 this.textNode = new BuilderNode(context); 260 this.textNode.build(wrapBuilder<[Params]>(buildText), new Params(this.message)) 261 262 return this.textNode.getFrameNode(); 263 } 264} 265 266@Entry 267@Component 268struct Index { 269 @State message: string = "hello" 270 271 build() { 272 Row() { 273 Column() { 274 NodeContainer(new TextNodeController(this.message)) 275 .width('100%') 276 .height(100) 277 .backgroundColor('#FFF0F0F0') 278 } 279 .width('100%') 280 .height('100%') 281 } 282 .height('100%') 283 } 284} 285``` 286 287**Example 2** 288 289This example shows how to mount a FrameNode within a BuilderNode to another FrameNode. 290 291```ts 292import { NodeController, BuilderNode, FrameNode, UIContext } from "@kit.ArkUI"; 293 294class Params { 295 text: string = "" 296 297 constructor(text: string) { 298 this.text = text; 299 } 300} 301 302@Builder 303function buildText(params: Params) { 304 Column() { 305 Text(params.text) 306 .fontSize(50) 307 .fontWeight(FontWeight.Bold) 308 .margin({ bottom: 36 }) 309 } 310} 311 312class TextNodeController extends NodeController { 313 private rootNode: FrameNode | null = null; 314 private textNode: BuilderNode<[Params]> | null = null; 315 private message: string = "DEFAULT"; 316 317 constructor(message: string) { 318 super(); 319 this.message = message; 320 } 321 322 makeNode(context: UIContext): FrameNode | null { 323 this.rootNode = new FrameNode(context); 324 this.textNode = new BuilderNode(context, { selfIdealSize: { width: 150, height: 150 } }); 325 this.textNode.build(wrapBuilder<[Params]>(buildText), new Params(this.message)); 326 if (this.rootNode !== null) { 327 this.rootNode.appendChild(this.textNode?.getFrameNode()); 328 } 329 330 return this.rootNode; 331 } 332} 333 334@Entry 335@Component 336struct Index { 337 @State message: string = "hello" 338 339 build() { 340 Row() { 341 Column() { 342 NodeContainer(new TextNodeController(this.message)) 343 .width('100%') 344 .height(100) 345 .backgroundColor('#FFF0F0F0') 346 } 347 .width('100%') 348 .height('100%') 349 } 350 .height('100%') 351 } 352} 353``` 354 355**Example 3** 356 357This example shows how to mount a BuilderNode's RenderNode under another RenderNode. Since the RenderNode does not pass layout constraints, this mode of mounting nodes is not recommended. 358 359```ts 360import { NodeController, BuilderNode, FrameNode, UIContext, RenderNode } from "@kit.ArkUI"; 361 362class Params { 363 text: string = "" 364 365 constructor(text: string) { 366 this.text = text; 367 } 368} 369 370@Builder 371function buildText(params: Params) { 372 Column() { 373 Text(params.text) 374 .fontSize(50) 375 .fontWeight(FontWeight.Bold) 376 .margin({ bottom: 36 }) 377 } 378} 379 380class TextNodeController extends NodeController { 381 private rootNode: FrameNode | null = null; 382 private textNode: BuilderNode<[Params]> | null = null; 383 private message: string = "DEFAULT"; 384 385 constructor(message: string) { 386 super(); 387 this.message = message; 388 } 389 390 makeNode(context: UIContext): FrameNode | null { 391 this.rootNode = new FrameNode(context); 392 let renderNode = new RenderNode(); 393 renderNode.clipToFrame = false; 394 this.textNode = new BuilderNode(context, { selfIdealSize: { width: 150, height: 150 } }); 395 this.textNode.build(wrapBuilder<[Params]>(buildText), new Params(this.message)); 396 const textRenderNode = this.textNode?.getFrameNode()?.getRenderNode(); 397 398 const rootRenderNode = this.rootNode.getRenderNode(); 399 if (rootRenderNode !== null) { 400 rootRenderNode.appendChild(renderNode); 401 renderNode.appendChild(textRenderNode); 402 } 403 404 return this.rootNode; 405 } 406} 407 408@Entry 409@Component 410struct Index { 411 @State message: string = "hello" 412 413 build() { 414 Row() { 415 Column() { 416 NodeContainer(new TextNodeController(this.message)) 417 .width('100%') 418 .height(100) 419 .backgroundColor('#FFF0F0F0') 420 } 421 .width('100%') 422 .height('100%') 423 } 424 .height('100%') 425 } 426} 427``` 428 429### update 430 431update(arg: Object): void 432 433Updates this BuilderNode based on the provided parameter, which is of the same type as the input parameter passed to the [build](#build) API. To call this API on a custom component, the variable used in the component must be defined as the @Prop type. 434 435**Atomic service API**: This API can be used in atomic services since API version 12. 436 437**System capability**: SystemCapability.ArkUI.ArkUI.Full 438 439**Parameters** 440 441| Name| Type | Mandatory| Description | 442| ------ | ------ | ---- | ------------------------------------------------------------------------ | 443| arg | Object | Yes | Parameter used to update the BuilderNode. It is of the same type as the parameter passed to the [build](#build) API.| 444 445**Example** 446```ts 447import { NodeController, BuilderNode, FrameNode, UIContext } from "@kit.ArkUI"; 448 449class Params { 450 text: string = "" 451 constructor(text: string) { 452 this.text = text; 453 } 454} 455 456// Custom component 457@Component 458struct TextBuilder { 459 @Prop message: string = "TextBuilder"; 460 461 build() { 462 Row() { 463 Column() { 464 Text(this.message) 465 .fontSize(50) 466 .fontWeight(FontWeight.Bold) 467 .margin({bottom: 36}) 468 .backgroundColor(Color.Gray) 469 } 470 } 471 } 472} 473 474@Builder 475function buildText(params: Params) { 476 Column() { 477 Text(params.text) 478 .fontSize(50) 479 .fontWeight(FontWeight.Bold) 480 .margin({ bottom: 36 }) 481 TextBuilder({message: params.text}) // Custom component 482 } 483} 484 485class TextNodeController extends NodeController { 486 private rootNode: FrameNode | null = null; 487 private textNode: BuilderNode<[Params]> | null = null; 488 private message: string = ""; 489 490 constructor(message: string) { 491 super() 492 this.message = message 493 } 494 495 makeNode(context: UIContext): FrameNode | null { 496 this.textNode = new BuilderNode(context); 497 this.textNode.build(wrapBuilder<[Params]>(buildText), new Params(this.message)) 498 return this.textNode.getFrameNode(); 499 } 500 501 update(message: string) { 502 if (this.textNode !== null) { 503 this.textNode.update(new Params(message)); 504 } 505 } 506} 507 508@Entry 509@Component 510struct Index { 511 @State message: string = "hello" 512 private textNodeController: TextNodeController = new TextNodeController(this.message); 513 private count = 0; 514 515 build() { 516 Row() { 517 Column() { 518 NodeContainer(this.textNodeController) 519 .width('100%') 520 .height(200) 521 .backgroundColor('#FFF0F0F0') 522 Button('Update') 523 .onClick(() => { 524 this.count += 1; 525 const message = "Update " + this.count.toString(); 526 this.textNodeController.update(message); 527 }) 528 } 529 .width('100%') 530 .height('100%') 531 } 532 .height('100%') 533 } 534} 535``` 536 537### postTouchEvent 538 539postTouchEvent(event: TouchEvent): boolean 540 541Posts a raw touch event to the FrameNode created by this BuilderNode. 542 543**postTouchEvent** dispatches the event from a middle node in the component tree downwards. To ensure the event is dispatched correctly, it needs to be transformed into the coordinate system of the parent component, as shown in the figure below. 544 545**OffsetA** indicates the offset of the BuildNode relative to the parent component. You can obtain this offset by calling [getPositionToParent](js-apis-arkui-frameNode.md#getpositiontoparent12) in the FrameNode. **OffsetB** indicates the offset of the touch point relative to the BuildNode. You can obtain this offset from the [TouchEvent](arkui-ts/ts-universal-events-touch.md#touchevent) object. **OffsetC** is the sum of **OffsetA** and **OffsetB**. It represents the final offset that you need to pass to **postTouchEvent**. 546 547 548 549> **NOTE** 550> 551> The coordinates you pass in need to be converted to pixel values (px). If the BuilderNode has any affine transformations applied to it, they must be taken into account and combined with the touch event coordinates. 552> 553> In [Webview](../apis-arkweb/js-apis-webview.md), coordinate system transformations are already handled internally, so you can directly dispatch the touch event without additional adjustments. 554> 555> The **postTouchEvent** API can be called only once for the same timestamp.<!--Del--> 556> 557> [UIExtensionComponent](arkui-ts/ts-container-ui-extension-component-sys.md) is not supported. 558<!--DelEnd--> 559 560**Atomic service API**: This API can be used in atomic services since API version 12. 561 562**System capability**: SystemCapability.ArkUI.ArkUI.Full 563 564**Parameters** 565 566| Name| Type | Mandatory| Description | 567| ------ | ------------------------------------------------------------------------- | ---- | ---------- | 568| event | [TouchEvent](arkui-ts/ts-universal-events-touch.md#touchevent) | Yes | Touch event.| 569 570**Return value** 571 572| Type | Description | 573| ------- | ------------------ | 574| boolean | Whether the event is successfully dispatched. The value **true** means the event is consumed by a component that responds to the event, and **false** means that no component responds to the event.<br>**NOTE**<br>If the event does not hit the expected component, ensure the following:<br>1. The coordinate system has been correctly transformed<br>2. The component is in an interactive state.<br>3. The event has been bound to the component.| 575 576**Example** 577 578```ts 579import { NodeController, BuilderNode, FrameNode, UIContext } from '@kit.ArkUI'; 580 581class Params { 582 text: string = "this is a text" 583} 584 585@Builder 586function ButtonBuilder(params: Params) { 587 Column() { 588 Button(`button ` + params.text) 589 .borderWidth(2) 590 .backgroundColor(Color.Orange) 591 .width("100%") 592 .height("100%") 593 .gesture( 594 TapGesture() 595 .onAction((event: GestureEvent) => { 596 console.log("TapGesture"); 597 }) 598 ) 599 } 600 .width(500) 601 .height(300) 602 .backgroundColor(Color.Gray) 603} 604 605class MyNodeController extends NodeController { 606 private rootNode: BuilderNode<[Params]> | null = null; 607 private wrapBuilder: WrappedBuilder<[Params]> = wrapBuilder(ButtonBuilder); 608 609 makeNode(uiContext: UIContext): FrameNode | null { 610 this.rootNode = new BuilderNode(uiContext); 611 this.rootNode.build(this.wrapBuilder, { text: "this is a string" }) 612 return this.rootNode.getFrameNode(); 613 } 614 615 // Coordinate system transformation 616 postTouchEvent(event: TouchEvent): boolean { 617 if (this.rootNode == null) { 618 return false; 619 } 620 let node: FrameNode | null = this.rootNode.getFrameNode(); 621 let offsetX: number | null | undefined = node?.getPositionToParent().x; 622 let offsetY: number | null | undefined = node?.getPositionToParent().y; 623 624 let changedTouchLen = event.changedTouches.length; 625 for (let i = 0; i < changedTouchLen; i++) { 626 if (offsetX != null && offsetY != null && offsetX != undefined && offsetY != undefined) { 627 event.changedTouches[i].x = vp2px(offsetX + event.changedTouches[i].x); 628 event.changedTouches[i].y = vp2px(offsetY + event.changedTouches[i].y); 629 } 630 } 631 let result = this.rootNode.postTouchEvent(event); 632 console.log("result " + result); 633 return result; 634 } 635} 636 637@Entry 638@Component 639struct MyComponent { 640 private nodeController: MyNodeController = new MyNodeController(); 641 642 build() { 643 Column() { 644 NodeContainer(this.nodeController) 645 .height(300) 646 .width(500) 647 648 Column() 649 .width(500) 650 .height(300) 651 .backgroundColor(Color.Pink) 652 .onTouch((event) => { 653 if (event != undefined) { 654 this.nodeController.postTouchEvent(event); 655 } 656 }) 657 } 658 } 659} 660``` 661 662### dispose<sup>12+</sup> 663 664dispose(): void 665 666Releases this BuilderNode immediately. Calling **dispose** on a **BuilderNode** object breaks its reference to the backend entity node, and also simultaneously severs the references of its contained FrameNode and RenderNode to their respective entity nodes. 667 668**Atomic service API**: This API can be used in atomic services since API version 12. 669 670**System capability**: SystemCapability.ArkUI.ArkUI.Full 671 672```ts 673import { RenderNode, FrameNode, NodeController, BuilderNode } from "@kit.ArkUI"; 674 675@Component 676struct TestComponent { 677 build() { 678 Column() { 679 Text('This is a BuilderNode.') 680 .fontSize(16) 681 .fontWeight(FontWeight.Bold) 682 } 683 .width('100%') 684 .backgroundColor(Color.Gray) 685 } 686 687 aboutToAppear() { 688 console.error('aboutToAppear'); 689 } 690 691 aboutToDisappear() { 692 console.error('aboutToDisappear'); 693 } 694} 695 696@Builder 697function buildComponent() { 698 TestComponent() 699} 700 701class MyNodeController extends NodeController { 702 private rootNode: FrameNode | null = null; 703 private builderNode: BuilderNode<[]> | null = null; 704 705 makeNode(uiContext: UIContext): FrameNode | null { 706 this.rootNode = new FrameNode(uiContext); 707 this.builderNode = new BuilderNode(uiContext, { selfIdealSize: { width: 200, height: 100 } }); 708 this.builderNode.build(new WrappedBuilder(buildComponent)); 709 710 const rootRenderNode = this.rootNode!.getRenderNode(); 711 if (rootRenderNode !== null) { 712 rootRenderNode.size = { width: 200, height: 200 }; 713 rootRenderNode.backgroundColor = 0xff00ff00; 714 rootRenderNode.appendChild(this.builderNode!.getFrameNode()!.getRenderNode()); 715 } 716 717 return this.rootNode; 718 } 719 720 dispose() { 721 if (this.builderNode !== null) { 722 this.builderNode.dispose(); 723 } 724 } 725 726 removeBuilderNode() { 727 const rootRenderNode = this.rootNode!.getRenderNode(); 728 if (rootRenderNode !== null && this.builderNode !== null && this.builderNode.getFrameNode() !== null) { 729 rootRenderNode.removeChild(this.builderNode!.getFrameNode()!.getRenderNode()); 730 } 731 } 732} 733 734@Entry 735@Component 736struct Index { 737 private myNodeController: MyNodeController = new MyNodeController(); 738 739 build() { 740 Column({ space: 4 }) { 741 NodeContainer(this.myNodeController) 742 Button('BuilderNode dispose') 743 .onClick(() => { 744 this.myNodeController.removeBuilderNode(); 745 this.myNodeController.dispose(); 746 }) 747 .width('100%') 748 } 749 } 750} 751``` 752 753### reuse<sup>12+</sup> 754 755reuse(param?: Object): void 756 757Passes the reuse event to the custom component in this BuilderNode. 758 759**Atomic service API**: This API can be used in atomic services since API version 12. 760 761**System capability**: SystemCapability.ArkUI.ArkUI.Full 762 763**Parameters** 764 765| Name| Type | Mandatory| Description | 766| ------ | ------ | ---- | ------------------------------------------------------------------------ | 767| param | Object | No | Parameter used to reuse the BuilderNode. It is of the same type as the parameter passed to the [build](#build) API.| 768 769### recycle<sup>12+</sup> 770 771recycle(): void 772 773Passes the recycle event to the custom component in this BuilderNode. 774 775**Atomic service API**: This API can be used in atomic services since API version 12. 776 777**System capability**: SystemCapability.ArkUI.ArkUI.Full 778 779```ts 780import { FrameNode,NodeController,BuilderNode,UIContext } from "@kit.ArkUI"; 781 782class MyDataSource { 783 private dataArray: string[] = []; 784 private listener: DataChangeListener | null = null 785 786 public totalCount(): number { 787 return this.dataArray.length; 788 } 789 790 public getData(index: number) { 791 return this.dataArray[index]; 792 } 793 794 public pushData(data: string) { 795 this.dataArray.push(data); 796 } 797 798 public reloadListener(): void { 799 this.listener?.onDataReloaded(); 800 } 801 802 public registerDataChangeListener(listener: DataChangeListener): void { 803 this.listener = listener; 804 } 805 806 public unregisterDataChangeListener(): void { 807 this.listener = null; 808 } 809} 810 811class Params { 812 item: string = ''; 813 814 constructor(item: string) { 815 this.item = item; 816 } 817} 818 819@Builder 820function buildNode(param: Params = new Params("hello")) { 821 ReusableChildComponent2({ item: param.item }); 822} 823 824class MyNodeController extends NodeController { 825 public builderNode: BuilderNode<[Params]> | null = null; 826 public item: string = ""; 827 828 makeNode(uiContext: UIContext): FrameNode | null { 829 if (this.builderNode == null) { 830 this.builderNode = new BuilderNode(uiContext, { selfIdealSize: { width: 300, height: 200 } }); 831 this.builderNode.build(wrapBuilder<[Params]>(buildNode), new Params(this.item)); 832 } 833 return this.builderNode.getFrameNode(); 834 } 835} 836 837@Reusable 838@Component 839struct ReusableChildComponent { 840 @State item: string = ''; 841 private controller: MyNodeController = new MyNodeController(); 842 843 aboutToAppear() { 844 this.controller.item = this.item; 845 } 846 847 aboutToRecycle(): void { 848 console.log("ReusableChildComponent aboutToRecycle " + this.item); 849 this.controller?.builderNode?.recycle(); 850 } 851 852 aboutToReuse(params: object): void { 853 console.log("ReusableChildComponent aboutToReuse " + JSON.stringify(params)); 854 this.controller?.builderNode?.reuse(params); 855 } 856 857 build() { 858 NodeContainer(this.controller); 859 } 860} 861 862@Component 863struct ReusableChildComponent2 { 864 @Prop item: string = "false"; 865 866 aboutToReuse(params: Record<string, object>) { 867 console.log("ReusableChildComponent2 Reusable 2 " + JSON.stringify(params)); 868 } 869 870 aboutToRecycle(): void { 871 console.log("ReusableChildComponent2 aboutToRecycle 2 " + this.item); 872 } 873 874 build() { 875 Row() { 876 Text(this.item) 877 .fontSize(20) 878 .backgroundColor(Color.Yellow) 879 .margin({ left: 10 }) 880 }.margin({ left: 10, right: 10 }) 881 } 882} 883 884 885@Entry 886@Component 887struct Index { 888 @State data: MyDataSource = new MyDataSource(); 889 890 aboutToAppear() { 891 for (let i = 0;i < 100; i++) { 892 this.data.pushData(i.toString()); 893 } 894 } 895 896 build() { 897 Column() { 898 List({ space: 3 }) { 899 LazyForEach(this.data, (item: string) => { 900 ListItem() { 901 ReusableChildComponent({ item: item }) 902 } 903 }, (item: string) => item) 904 } 905 .width('100%') 906 .height('100%') 907 } 908 } 909} 910``` 911 912### updateConfiguration<sup>12+</sup> 913 914updateConfiguration(): void 915 916Updates the configuration of the entire node by passing in a [system environment change](../apis-ability-kit/js-apis-app-ability-configuration.md) event. 917 918**Atomic service API**: This API can be used in atomic services since API version 12. 919 920**System capability**: SystemCapability.ArkUI.ArkUI.Full 921 922> **NOTE** 923> 924> The **updateConfiguration** API is used to instruct an object to update, with the system environment used for the update being determined by the changes in the application's current system environment. 925 926**Example** 927```ts 928import { NodeController, BuilderNode, FrameNode, UIContext } from "@kit.ArkUI"; 929import { AbilityConstant, Configuration, ConfigurationConstant, EnvironmentCallback } from '@kit.AbilityKit'; 930 931class Params { 932 text: string = "" 933 934 constructor(text: string) { 935 this.text = text; 936 } 937} 938 939// Custom component 940@Component 941struct TextBuilder { 942 // The @Prop decorated attribute is the attribute to be updated in the custom component. It is a basic attribute. 943 @Prop message: string = "TextBuilder"; 944 945 build() { 946 Row() { 947 Column() { 948 Text(this.message) 949 .fontSize(50) 950 .fontWeight(FontWeight.Bold) 951 .margin({ bottom: 36 }) 952 } 953 } 954 } 955} 956 957@Builder 958function buildText(params: Params) { 959 Column() { 960 Text(params.text) 961 .fontSize(50) 962 .fontWeight(FontWeight.Bold) 963 .margin({ bottom: 36 }) 964 TextBuilder({ message: params.text }) // Custom component 965 }.backgroundColor($r('sys.color.ohos_id_color_background')) 966} 967 968class TextNodeController extends NodeController { 969 private textNode: BuilderNode<[Params]> | null = null; 970 private message: string = ""; 971 972 constructor(message: string) { 973 super() 974 this.message = message; 975 } 976 977 makeNode(context: UIContext): FrameNode | null { 978 return this.textNode?.getFrameNode() ? this.textNode?.getFrameNode() : null; 979 } 980 981 createNode(context: UIContext) { 982 this.textNode = new BuilderNode(context); 983 this.textNode.build(wrapBuilder<[Params]>(buildText), new Params(this.message)); 984 builderNodeMap.push(this.textNode); 985 } 986 987 deleteNode() { 988 let node = builderNodeMap.pop(); 989 node?.dispose(); 990 } 991 992 update(message: string) { 993 if (this.textNode !== null) { 994 // Call update to perform an update. 995 this.textNode.update(new Params(message)); 996 } 997 } 998} 999 1000// Record the created custom node object. 1001const builderNodeMap: Array<BuilderNode<[Params]>> = new Array(); 1002 1003function updateColorMode() { 1004 builderNodeMap.forEach((value, index) => { 1005 // Notify BuilderNode of the environment changes. 1006 value.updateConfiguration(); 1007 }) 1008} 1009 1010@Entry 1011@Component 1012struct Index { 1013 @State message: string = "hello" 1014 private textNodeController: TextNodeController = new TextNodeController(this.message); 1015 private count = 0; 1016 1017 aboutToAppear(): void { 1018 let environmentCallback: EnvironmentCallback = { 1019 onMemoryLevel: (level: AbilityConstant.MemoryLevel): void => { 1020 console.log('onMemoryLevel'); 1021 }, 1022 onConfigurationUpdated: (config: Configuration): void => { 1023 console.log('onConfigurationUpdated ' + JSON.stringify(config)); 1024 updateColorMode(); 1025 } 1026 } 1027 // Register a callback. 1028 this.getUIContext().getHostContext()?.getApplicationContext().on('environment', environmentCallback); 1029 // Set the application color mode to follow the system settings. 1030 this.getUIContext() 1031 .getHostContext()?.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET); 1032 // Create a custom node and add it to the map. 1033 this.textNodeController.createNode(this.getUIContext()); 1034 } 1035 1036 aboutToDisappear(): void { 1037 // Remove the reference to the custom node from the map and release the node. 1038 this.textNodeController.deleteNode(); 1039 } 1040 1041 build() { 1042 Row() { 1043 Column() { 1044 NodeContainer(this.textNodeController) 1045 .width('100%') 1046 .height(200) 1047 .backgroundColor('#FFF0F0F0') 1048 Button('Update') 1049 .onClick(() => { 1050 this.count += 1; 1051 const message = "Update " + this.count.toString(); 1052 this.textNodeController.update(message); 1053 }) 1054 Button('Switch to Dark Mode') 1055 .onClick(() => { 1056 this.getUIContext() 1057 .getHostContext()?.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_DARK); 1058 }) 1059 Button('Switch to Light Mode') 1060 .onClick(() => { 1061 this.getUIContext() 1062 .getHostContext()?.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_LIGHT); 1063 }) 1064 } 1065 .width('100%') 1066 .height('100%') 1067 } 1068 .height('100%') 1069 } 1070} 1071``` 1072