1# BuilderNode 2 3提供能够挂载原生组件的自定义节点BuilderNode。BuilderNode仅可作为叶子节点使用。使用方式参考[BuilderNode开发指南](../../ui/arkts-user-defined-arktsNode-builderNode.md)。 4 5> **说明:** 6> 7> 本模块首批接口从API version 11开始支持。后续版本的新增接口,采用上角标单独标记接口的起始版本。 8> 9> 如果在跨页面复用BuilderNode时显示异常,可参考[跨页面复用注意事项](../../ui/arkts-user-defined-arktsNode-builderNode.md#跨页面复用注意事项)。 10> 11> 当前不支持在预览器中使用BuilderNode。 12 13## 导入模块 14 15```ts 16import { BuilderNode, RenderOptions, NodeRenderType } from "@kit.ArkUI"; 17``` 18 19## NodeRenderType 20 21节点渲染类型枚举。 22 23**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 24 25**系统能力:** SystemCapability.ArkUI.ArkUI.Full 26 27| 名称 | 值 | 说明 | 28| ------------------- | --- | ---------------------------- | 29| RENDER_TYPE_DISPLAY | 0 | 表示该节点将被显示到屏幕上。 | 30| RENDER_TYPE_TEXTURE | 1 | 表示该节点将被导出为纹理。 | 31 32> **说明:** 33> 34> RENDER_TYPE_TEXTURE类型目前仅在[BuilderNode](#buildernode-1)持有组件树的根节点为自定义组件时以及[XComponentNode](./js-apis-arkui-xcomponentNode.md)中设置生效。 35> 36> 在[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。 37> 38> 从API version 12开始,新增以下组件支持纹理导出:DatePicker、ForEach、Grid、IfElse、LazyForEach、List、Scroll、Swiper、TimePicker、@Component修饰的自定义组件、NodeContainer以及NodeContainer下挂载的FrameNode和RenderNode。 39> 40> 使用方式可参考[同层渲染绘制](../../web/web-same-layer.md)。 41 42## RenderOptions 43 44创建BuilderNode时的可选参数。 45 46**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 47 48**系统能力:** SystemCapability.ArkUI.ArkUI.Full 49 50| 名称 | 类型 | 必填 | 说明 | 51| ------------- | -------------------------------------- | ---- | ------------------------------------------------------------ | 52| selfIdealSize | [Size](js-apis-arkui-graphics.md#size) | 否 | 节点的理想大小。 | 53| type | [NodeRenderType](#noderendertype) | 否 | 节点的渲染类型。 | 54| surfaceId | string | 否 | 纹理接收方的surfaceId。纹理接收方一般为[OH_NativeImage](../apis-arkgraphics2d/_o_h___native_image.md#oh_nativeimage)。 | 55 56## BuilderNode 57 58class BuilderNode\<Args extends Object[]> 59 60BuilderNode支持通过无状态的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)的接口对其进行属性设置与子节点操作。 61 62**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 63 64**系统能力:** SystemCapability.ArkUI.ArkUI.Full 65 66### constructor 67 68constructor(uiContext: UIContext, options?: RenderOptions) 69 70当将BuilderNode生成的内容嵌入到其它RenderNode中显示时,即将BuilderNode对应的RenderNode挂载到另一个RenderNode中显示,需要显式指定RenderOptions中的selfIdealSize,否则Builder内的节点默认父组件布局约束为[0,0],即不设置selfIdealSize则认为BuilderNode中子树的根节点大小为[0,0]。 71 72**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 73 74**系统能力:** SystemCapability.ArkUI.ArkUI.Full 75 76| 参数名 | 类型 | 必填 | 说明 | 77| --------- | --------------------------------------- | ---- | ----------------------------------------------------------------- | 78| uiContext | [UIContext](js-apis-arkui-UIContext.md) | 是 | UI上下文,获取方式可参考[UIContext获取方法](./js-apis-arkui-node.md#uicontext获取方法)。 | 79| options | [RenderOptions](#renderoptions) | 否 | BuilderNode的构造可选参数。默认值:undefined。 | 80 81> **说明** 82> uiContext的入参需要为一个有效的值,即UI上下文正确,如果传入非法值或者未设置,会导致创建失败。 83 84### build 85 86build(builder: WrappedBuilder\<Args>, arg?: Object): void 87 88依照传入的对象创建组件树,并持有组件树的根节点。无状态的UI方法[@Builder](../../quick-start/arkts-builder.md)最多拥有一个根节点。 89支持自定义组件。不支持自定义组件使用[@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中自定义组件的状态。 90 91> **说明** 92> 93> @Builder嵌套使用的时候需要保证内外的@Builder方法的入参对象一致。 94> 95> 最外层的@Builder只支持一个入参。 96> 97> 需要操作BuilderNode中的对象时,需要保证其引用不被回收。当BuilderNode对象被虚拟机回收之后,它的FrameNode、RenderNode对象也会与后端节点解引用。即从BuilderNode中获取的FrameNode对象不对应任何一个节点。 98 99**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 100 101**系统能力:** SystemCapability.ArkUI.ArkUI.Full 102 103**参数:** 104 105| 参数名 | 类型 | 必填 | 说明 | 106| ------- | --------------------------------------------------------------- | ---- | -------------------------------------------------------------------------------------- | 107| builder | [WrappedBuilder\<Args>](../../quick-start/arkts-wrapBuilder.md) | 是 | 创建对应节点树的时候所需的无状态UI方法[@Builder](../../quick-start/arkts-builder.md)。 | 108| arg | Object | 否 | builder的入参。当前仅支持一个入参,且入参对象类型与@Builder定义的入参类型保持一致。 | 109 110 111### BuildOptions<sup>12+</sup> 112 113build的可选参数。 114 115**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 116 117**系统能力:** SystemCapability.ArkUI.ArkUI.Full 118 119| 名称 | 类型 | 必填 | 说明 | 120| ------------- | -------------------------------------- | ---- | ------------------------------------------------------------ | 121| nestingBuilderSupported |boolean | 否 | 是否支持Builder嵌套Builder进行使用。其中,false表示Builder使用的入参一致,true表示Builder使用的入参不一致。默认值:false。 | 122 123### build<sup>12+</sup> 124 125build(builder: WrappedBuilder\<Args>, arg: Object, options: [BuildOptions](#buildoptions12)): void 126 127依照传入的对象创建组件树,并持有组件树的根节点。无状态的UI方法[@Builder](../../quick-start/arkts-builder.md)最多拥有一个根节点。 128支持自定义组件。不支持使用自定义组件使用[@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)等装饰器用于当前页面与自定义组件的状态同步。 129 130> **说明** 131> 132> @Builder进行创建和更新的规格参考[@Builder](../../quick-start/arkts-builder.md)。 133> 134> 最外层的@Builder只支持一个入参。 135 136**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 137 138**系统能力:** SystemCapability.ArkUI.ArkUI.Full 139 140**参数:** 141 142| 参数名 | 类型 | 必填 | 说明 | 143| ------- | --------------------------------------------------------------- | ---- | -------------------------------------------------------------------------------------- | 144| builder | [WrappedBuilder\<Args>](../../quick-start/arkts-wrapBuilder.md) | 是 | 创建对应节点树的时候所需的无状态UI方法[@Builder](../../quick-start/arkts-builder.md)。 | 145| arg | Object | 是 | builder的入参。当前仅支持一个入参,且入参对象类型与@Builder定义的入参类型保持一致。 | 146| options | BuildOptions | 是 | build的配置参数,判断是否支持@Builder中嵌套@Builder的行为。 | 147 148**示例:** 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 213获取BuilderNode中的FrameNode。在BuilderNode执行build操作之后,才会生成FrameNode。 214 215**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 216 217**系统能力:** SystemCapability.ArkUI.ArkUI.Full 218 219**返回值:** 220 221| 类型 | 说明 | 222| --------------------------------------------------------- | --------------------------------------------------------------------- | 223| [FrameNode](js-apis-arkui-frameNode.md#framenode) \| null | 一个FrameNode对象。若该BuilderNode不包含FrameNode,则返回空对象null。 | 224 225**示例1:** 226 227BuilderNode作为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**示例2:** 288 289BuilderNode的FrameNode挂到其它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**示例3:** 356 357BuilderNode的RenderNode挂到其它RenderNode下。由于RenderNode不传递布局约束,不推荐通过该方式挂载节点。 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 433根据提供的参数更新BuilderNode,该参数为[build](#build)方法调用时传入的参数类型相同。对自定义组件进行update的时候需要在自定义组件中使用的变量定义为@Prop类型。 434 435**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 436 437**系统能力:** SystemCapability.ArkUI.ArkUI.Full 438 439**参数:** 440 441| 参数名 | 类型 | 必填 | 说明 | 442| ------ | ------ | ---- | ------------------------------------------------------------------------ | 443| arg | Object | 是 | 用于更新BuilderNode的参数,和[build](#build)调用时传入的参数类型一致。 | 444 445**示例:** 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// 自定义组件 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}) // 自定义组件 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 541将原始事件派发到某个BuilderNode创建出的FrameNode上。 542 543postTouchEvent是从组件树的中间节点往下分发,需要变换到父组件坐标系才能分发成功,参考下图。 544 545OffsetA为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的最终结果。 546 547 548 549> **说明:** 550> 551> 传入的坐标值需要转换为px,如果builderNode有仿射变换,则需要再叠加仿射变换。 552> 553> 在[webview](../apis-arkweb/js-apis-webview.md)中,内部已经处理过坐标系变换,可以将TouchEvent事件直接下发。 554> 555> 同一时间戳,postTouchEvent只能调用一次。<!--Del--> 556> 557> 不支持[UIExtensionComponent](arkui-ts/ts-container-ui-extension-component-sys.md)。 558<!--DelEnd--> 559 560**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 561 562**系统能力:** SystemCapability.ArkUI.ArkUI.Full 563 564**参数:** 565 566| 参数名 | 类型 | 必填 | 说明 | 567| ------ | ------------------------------------------------------------------------- | ---- | ---------- | 568| event | [TouchEvent](arkui-ts/ts-universal-events-touch.md#touchevent对象说明) | 是 | 触摸事件。 | 569 570**返回值:** 571 572| 类型 | 说明 | 573| ------- | ------------------ | 574| boolean | 派发事件是否成功。true为已命中响应事件的组件,false为未命中任何可响应事件的组件。<br/>**说明:** <br/>如果未按照预期命中组件,需要确认以下几点:<br/>1.坐标系是否转换正确。<br/>2.组件是否可交互状态。<br/>3.是否绑定事件。 | 575 576**示例:** 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 // 坐标转换示例 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 666立即释放当前BuilderNode。当BuilderNode对象调用dispose接口之后,不仅BuilderNode对象与后端实体节点解除引用关系,BuilderNode中的FrameNode与RenderNode也会同步和实体节点解除引用关系。 667 668**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 669 670**系统能力:** 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 757传递reuse事件到BuilderNode中的自定义组件。 758 759**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 760 761**系统能力:** SystemCapability.ArkUI.ArkUI.Full 762 763**参数:** 764 765| 参数名 | 类型 | 必填 | 说明 | 766| ------ | ------ | ---- | ------------------------------------------------------------------------ | 767| param | Object | 否 | 用于复用BuilderNode的参数,和[build](#build)调用时传入的参数类型一致。 | 768 769### recycle<sup>12+</sup> 770 771recycle(): void 772 773传递recycle事件到BuilderNode中的自定义组件。 774 775**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 776 777**系统能力:** 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 916传递[系统环境变化](../apis-ability-kit/js-apis-app-ability-configuration.md)事件,触发节点的全量更新。 917 918**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 919 920**系统能力:** SystemCapability.ArkUI.ArkUI.Full 921 922> **说明:** 923> 924> updateConfiguration接口用于通知对象更新,更新所使用的系统环境由应用当前的系统环境变化决定。 925 926**示例:** 927```ts 928import { NodeController, BuilderNode, FrameNode, UIContext } from "@kit.ArkUI"; 929import { AbilityConstant, Configuration, EnvironmentCallback } from '@kit.AbilityKit'; 930 931class Params { 932 text: string = "" 933 934 constructor(text: string) { 935 this.text = text; 936 } 937} 938 939// 自定义组件 940@Component 941struct TextBuilder { 942 // 作为自定义组件中需要更新的属性,数据类型为基础属性,定义为@Prop 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 .fontColor($r(`app.color.text_color`)) 953 .backgroundColor($r(`app.color.start_window_background`)) 954 } 955 } 956 } 957} 958 959@Builder 960function buildText(params: Params) { 961 Column() { 962 Text(params.text) 963 .fontSize(50) 964 .fontWeight(FontWeight.Bold) 965 .margin({ bottom: 36 }) 966 .fontColor($r(`app.color.text_color`)) 967 TextBuilder({ message: params.text }) // 自定义组件 968 }.backgroundColor($r(`app.color.start_window_background`)) 969} 970 971class TextNodeController extends NodeController { 972 private textNode: BuilderNode<[Params]> | null = null; 973 private message: string = ""; 974 975 constructor(message: string) { 976 super() 977 this.message = message; 978 } 979 980 makeNode(context: UIContext): FrameNode | null { 981 return this.textNode?.getFrameNode() ? this.textNode?.getFrameNode() : null; 982 } 983 984 createNode(context: UIContext) { 985 this.textNode = new BuilderNode(context); 986 this.textNode.build(wrapBuilder<[Params]>(buildText), new Params(this.message)); 987 builderNodeMap.push(this.textNode); 988 } 989 990 deleteNode() { 991 let node = builderNodeMap.pop(); 992 node?.dispose(); 993 } 994 995 update(message: string) { 996 if (this.textNode !== null) { 997 // 调用update进行更新。 998 this.textNode.update(new Params(message)); 999 } 1000 } 1001} 1002 1003// 记录创建的自定义节点对象 1004const builderNodeMap: Array<BuilderNode<[Params]>> = new Array(); 1005 1006function updateColorMode() { 1007 builderNodeMap.forEach((value, index) => { 1008 // 通知BuilderNode环境变量改变 1009 value.updateConfiguration(); 1010 }) 1011} 1012 1013@Entry 1014@Component 1015struct Index { 1016 @State message: string = "hello" 1017 private textNodeController: TextNodeController = new TextNodeController(this.message); 1018 private count = 0; 1019 1020 aboutToAppear(): void { 1021 let environmentCallback: EnvironmentCallback = { 1022 onMemoryLevel: (level: AbilityConstant.MemoryLevel): void => { 1023 console.log('onMemoryLevel'); 1024 }, 1025 onConfigurationUpdated: (config: Configuration): void => { 1026 console.log('onConfigurationUpdated ' + JSON.stringify(config)); 1027 updateColorMode(); 1028 } 1029 } 1030 // 注册监听回调 1031 this.getUIContext().getHostContext()?.getApplicationContext().on('environment', environmentCallback); 1032 //创建自定义节点并添加至map 1033 this.textNodeController.createNode(this.getUIContext()); 1034 } 1035 1036 aboutToDisappear(): void { 1037 //移除map中的引用,并将自定义节点释放 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 } 1055 .width('100%') 1056 .height('100%') 1057 } 1058 .height('100%') 1059 } 1060} 1061``` 1062