1# BuilderNode 2 3提供能够挂载原生组件的自定义节点BuilderNode。BuilderNode仅可作为叶子节点使用。使用方式参考[BuilderNode开发指南](../../ui/arkts-user-defined-arktsNode-builderNode.md)。 4 5> **说明:** 6> 7> 本模块首批接口从API version 11开始支持。后续版本的新增接口,采用上角标单独标记接口的起始版本。 8> 9> 当前不支持在预览器中使用BuilderNode。 10 11## 导入模块 12 13```ts 14import { BuilderNode, RenderOptions, NodeRenderType } from "@kit.ArkUI"; 15``` 16 17## NodeRenderType 18 19节点渲染类型枚举。 20 21**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 22 23**系统能力:** SystemCapability.ArkUI.ArkUI.Full 24 25| 名称 | 值 | 说明 | 26| ------------------- | --- | ---------------------------- | 27| RENDER_TYPE_DISPLAY | 0 | 表示该节点将被显示到屏幕上。 | 28| RENDER_TYPE_TEXTURE | 1 | 表示该节点将被导出为纹理。 | 29 30> **说明:** 31> 32> RENDER_TYPE_TEXTURE类型目前仅在[BuilderNode](#buildernode-1)持有组件树的根节点为自定义组件时以及[XComponentNode](./js-apis-arkui-xcomponentNode.md)中设置生效。 33> 34> 在[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。 35> 36> 从API version 12开始,新增以下组件支持纹理导出:DatePicker、ForEach、Grid、IfElse、LazyForEach、List、Scroll、Swiper、TimePicker、@Component修饰的自定义组件、NodeContainer以及NodeContainer下挂载的FrameNode和RenderNode。 37> 38> 使用方式可参考[同层渲染绘制](../../web/web-same-layer.md)。 39 40## RenderOptions 41 42创建BuilderNode时的可选参数。 43 44**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 45 46**系统能力:** SystemCapability.ArkUI.ArkUI.Full 47 48| 名称 | 类型 | 必填 | 说明 | 49| ------------- | -------------------------------------- | ---- | ------------------------------------------------------------ | 50| selfIdealSize | [Size](js-apis-arkui-graphics.md#size) | 否 | 节点的理想大小。 | 51| type | [NodeRenderType](#noderendertype) | 否 | 节点的渲染类型。 | 52| surfaceId | string | 否 | 纹理接收方的surfaceId。纹理接收方一般为[OH_NativeImage](../apis-arkgraphics2d/_o_h___native_image.md#oh_nativeimage)。 | 53 54## BuilderNode 55 56class BuilderNode\<Args extends Object[]> 57 58BuilderNode支持通过无状态的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)的接口对其进行属性设置与子节点操作。 59 60**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 61 62**系统能力:** SystemCapability.ArkUI.ArkUI.Full 63 64### constructor 65 66constructor(uiContext: UIContext, options?: RenderOptions) 67 68当将BuilderNode生成的内容嵌入到其它RenderNode中显示时,即将BuilderNode对应的RenderNode挂载到另一个RenderNode中显示,需要显式指定RenderOptions中的selfIdealSize,否则Builder内的节点默认父组件布局约束为[0,0],即不设置selfIdealSize则认为BuilderNode中子树的根节点大小为[0,0]。 69 70**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 71 72**系统能力:** SystemCapability.ArkUI.ArkUI.Full 73 74| 参数名 | 类型 | 必填 | 说明 | 75| --------- | --------------------------------------- | ---- | ----------------------------------------------------------------- | 76| uiContext | [UIContext](js-apis-arkui-UIContext.md) | 是 | UI上下文,获取方式可参考[UIContext获取方法](./js-apis-arkui-node.md#uicontext获取方法)。 | 77| options | [RenderOptions](#renderoptions) | 否 | BuilderNode的构造可选参数。默认值:undefined。 | 78 79> **说明** 80> uiContext的入参需要为一个有效的值,即UI上下文正确,如果传入非法值或者未设置,会导致创建失败。 81 82### build 83 84build(builder: WrappedBuilder\<Args>, arg?: Object): void 85 86依照传入的对象创建组件树,并持有组件树的根节点。无状态的UI方法[@Builder](../../quick-start/arkts-builder.md)最多拥有一个根节点。 87支持自定义组件。不支持自定义组件使用[@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中自定义组件的状态。 88 89> **说明** 90> 91> @Builder嵌套使用的时候需要保证内外的@Builder方法的入参对象一致。 92> 93> 最外层的@Builder只支持一个入参。 94> 95> 需要操作BuilderNode中的对象时,需要保证其引用不被回收。当BuilderNode对象被虚拟机回收之后,它的FrameNode、RenderNode对象也会与后端节点解引用。即从BuilderNode中获取的FrameNode对象不对应任何一个节点。 96 97**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 98 99**系统能力:** SystemCapability.ArkUI.ArkUI.Full 100 101**参数:** 102 103| 参数名 | 类型 | 必填 | 说明 | 104| ------- | --------------------------------------------------------------- | ---- | -------------------------------------------------------------------------------------- | 105| builder | [WrappedBuilder\<Args>](../../quick-start/arkts-wrapBuilder.md) | 是 | 创建对应节点树的时候所需的无状态UI方法[@Builder](../../quick-start/arkts-builder.md)。 | 106| arg | Object | 否 | builder的入参。当前仅支持一个入参,且入参对象类型与@Builder定义的入参类型保持一致。 | 107 108 109### BuildOptions<sup>12+</sup> 110 111build的可选参数。 112 113**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 114 115**系统能力:** SystemCapability.ArkUI.ArkUI.Full 116 117| 名称 | 类型 | 必填 | 说明 | 118| ------------- | -------------------------------------- | ---- | ------------------------------------------------------------ | 119| nestingBuilderSupported |boolean | 否 | 是否支持Builder嵌套Builder进行使用。其中,false表示Builder使用的入参一致,true表示Builder使用的入参不一致。默认值:false。 | 120 121### build<sup>12+</sup> 122 123build(builder: WrappedBuilder\<Args>, arg: Object, options: [BuildOptions](#buildoptions12)): void 124 125依照传入的对象创建组件树,并持有组件树的根节点。无状态的UI方法[@Builder](../../quick-start/arkts-builder.md)最多拥有一个根节点。 126支持自定义组件。不支持使用自定义组件使用[@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)等装饰器用于当前页面与自定义组件的状态同步。 127 128> **说明** 129> 130> @Builder进行创建和更新的规格参考[@Builder](../../quick-start/arkts-builder.md)。 131> 132> 最外层的@Builder只支持一个入参。 133 134**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 135 136**系统能力:** SystemCapability.ArkUI.ArkUI.Full 137 138**参数:** 139 140| 参数名 | 类型 | 必填 | 说明 | 141| ------- | --------------------------------------------------------------- | ---- | -------------------------------------------------------------------------------------- | 142| builder | [WrappedBuilder\<Args>](../../quick-start/arkts-wrapBuilder.md) | 是 | 创建对应节点树的时候所需的无状态UI方法[@Builder](../../quick-start/arkts-builder.md)。 | 143| arg | Object | 是 | builder的入参。当前仅支持一个入参,且入参对象类型与@Builder定义的入参类型保持一致。 | 144| options | BuildOptions | 是 | build的配置参数,判断是否支持@Builder中嵌套@Builder的行为。 | 145 146**示例:** 147```ts 148import { BuilderNode, NodeContent } from "@kit.ArkUI"; 149 150interface ParamsInterface { 151 text: string; 152 func: Function; 153} 154 155@Builder 156function buildTextWithFunc(fun: Function) { 157 Text(fun()) 158 .fontSize(50) 159 .fontWeight(FontWeight.Bold) 160 .margin({ bottom: 36 }) 161} 162 163@Builder 164function buildText(params: ParamsInterface) { 165 Column() { 166 Text(params.text) 167 .fontSize(50) 168 .fontWeight(FontWeight.Bold) 169 .margin({ bottom: 36 }) 170 buildTextWithFunc(params.func) 171 } 172} 173 174 175@Entry 176@Component 177struct Index { 178 @State message: string = "HELLO" 179 private content: NodeContent = new NodeContent(); 180 181 build() { 182 Row() { 183 Column() { 184 Button('addBuilderNode') 185 .onClick(() => { 186 let buildNode = new BuilderNode<[ParamsInterface]>(this.getUIContext()); 187 buildNode.build(wrapBuilder<[ParamsInterface]>(buildText), { 188 text: this.message, func: () => { 189 return "FUNCTION" 190 } 191 }, { nestingBuilderSupported: true }); 192 this.content.addFrameNode(buildNode.getFrameNode()); 193 buildNode.dispose(); 194 }) 195 ContentSlot(this.content) 196 } 197 .id("column") 198 .width('100%') 199 .height('100%') 200 } 201 .height('100%') 202 } 203} 204``` 205 206 207### getFrameNode 208 209getFrameNode(): FrameNode | null 210 211获取BuilderNode中的FrameNode。在BuilderNode执行build操作之后,才会生成FrameNode。 212 213**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 214 215**系统能力:** SystemCapability.ArkUI.ArkUI.Full 216 217**返回值:** 218 219| 类型 | 说明 | 220| --------------------------------------------------------- | --------------------------------------------------------------------- | 221| [FrameNode](js-apis-arkui-frameNode.md#framenode) \| null | 一个FrameNode对象。若该BuilderNode不包含FrameNode,则返回空对象null。 | 222 223**示例1:** 224 225BuilderNode作为NodeContainer的根节点返回。 226 227```ts 228import { NodeController, BuilderNode, FrameNode, UIContext } from "@kit.ArkUI"; 229 230class Params { 231 text: string = "" 232 constructor(text: string) { 233 this.text = text; 234 } 235} 236 237@Builder 238function buildText(params: Params) { 239 Column() { 240 Text(params.text) 241 .fontSize(50) 242 .fontWeight(FontWeight.Bold) 243 .margin({bottom: 36}) 244 } 245} 246 247class TextNodeController extends NodeController { 248 private textNode: BuilderNode<[Params]> | null = null; 249 private message: string = "DEFAULT"; 250 251 constructor(message: string) { 252 super(); 253 this.message = message; 254 } 255 256 makeNode(context: UIContext): FrameNode | null { 257 this.textNode = new BuilderNode(context); 258 this.textNode.build(wrapBuilder<[Params]>(buildText), new Params(this.message)) 259 260 return this.textNode.getFrameNode(); 261 } 262} 263 264@Entry 265@Component 266struct Index { 267 @State message: string = "hello" 268 269 build() { 270 Row() { 271 Column() { 272 NodeContainer(new TextNodeController(this.message)) 273 .width('100%') 274 .height(100) 275 .backgroundColor('#FFF0F0F0') 276 } 277 .width('100%') 278 .height('100%') 279 } 280 .height('100%') 281 } 282} 283``` 284 285**示例2:** 286 287BuilderNode的FrameNode挂到其它FrameNode下。 288 289```ts 290import { NodeController, BuilderNode, FrameNode, UIContext } from "@kit.ArkUI"; 291 292class Params { 293 text: string = "" 294 295 constructor(text: string) { 296 this.text = text; 297 } 298} 299 300@Builder 301function buildText(params: Params) { 302 Column() { 303 Text(params.text) 304 .fontSize(50) 305 .fontWeight(FontWeight.Bold) 306 .margin({ bottom: 36 }) 307 } 308} 309 310class TextNodeController extends NodeController { 311 private rootNode: FrameNode | null = null; 312 private textNode: BuilderNode<[Params]> | null = null; 313 private message: string = "DEFAULT"; 314 315 constructor(message: string) { 316 super(); 317 this.message = message; 318 } 319 320 makeNode(context: UIContext): FrameNode | null { 321 this.rootNode = new FrameNode(context); 322 this.textNode = new BuilderNode(context, { selfIdealSize: { width: 150, height: 150 } }); 323 this.textNode.build(wrapBuilder<[Params]>(buildText), new Params(this.message)); 324 if (this.rootNode !== null) { 325 this.rootNode.appendChild(this.textNode?.getFrameNode()); 326 } 327 328 return this.rootNode; 329 } 330} 331 332@Entry 333@Component 334struct Index { 335 @State message: string = "hello" 336 337 build() { 338 Row() { 339 Column() { 340 NodeContainer(new TextNodeController(this.message)) 341 .width('100%') 342 .height(100) 343 .backgroundColor('#FFF0F0F0') 344 } 345 .width('100%') 346 .height('100%') 347 } 348 .height('100%') 349 } 350} 351``` 352 353**示例3:** 354 355BuilderNode的RenderNode挂到其它RenderNode下。由于RenderNode不传递布局约束,不推荐通过该方式挂载节点。 356 357```ts 358import { NodeController, BuilderNode, FrameNode, UIContext, RenderNode } from "@kit.ArkUI"; 359 360class Params { 361 text: string = "" 362 363 constructor(text: string) { 364 this.text = text; 365 } 366} 367 368@Builder 369function buildText(params: Params) { 370 Column() { 371 Text(params.text) 372 .fontSize(50) 373 .fontWeight(FontWeight.Bold) 374 .margin({ bottom: 36 }) 375 } 376} 377 378class TextNodeController extends NodeController { 379 private rootNode: FrameNode | null = null; 380 private textNode: BuilderNode<[Params]> | null = null; 381 private message: string = "DEFAULT"; 382 383 constructor(message: string) { 384 super(); 385 this.message = message; 386 } 387 388 makeNode(context: UIContext): FrameNode | null { 389 this.rootNode = new FrameNode(context); 390 let renderNode = new RenderNode(); 391 renderNode.clipToFrame = false; 392 this.textNode = new BuilderNode(context, { selfIdealSize: { width: 150, height: 150 } }); 393 this.textNode.build(wrapBuilder<[Params]>(buildText), new Params(this.message)); 394 const textRenderNode = this.textNode?.getFrameNode()?.getRenderNode(); 395 396 const rootRenderNode = this.rootNode.getRenderNode(); 397 if (rootRenderNode !== null) { 398 rootRenderNode.appendChild(renderNode); 399 renderNode.appendChild(textRenderNode); 400 } 401 402 return this.rootNode; 403 } 404} 405 406@Entry 407@Component 408struct Index { 409 @State message: string = "hello" 410 411 build() { 412 Row() { 413 Column() { 414 NodeContainer(new TextNodeController(this.message)) 415 .width('100%') 416 .height(100) 417 .backgroundColor('#FFF0F0F0') 418 } 419 .width('100%') 420 .height('100%') 421 } 422 .height('100%') 423 } 424} 425``` 426 427### update 428 429update(arg: Object): void 430 431根据提供的参数更新BuilderNode,该参数为[build](#build)方法调用时传入的参数类型相同。对自定义组件进行update的时候需要在自定义组件中使用的变量定义为@Prop类型。 432 433**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 434 435**系统能力:** SystemCapability.ArkUI.ArkUI.Full 436 437**参数:** 438 439| 参数名 | 类型 | 必填 | 说明 | 440| ------ | ------ | ---- | ------------------------------------------------------------------------ | 441| arg | Object | 是 | 用于更新BuilderNode的参数,和[build](#build)调用时传入的参数类型一致。 | 442 443**示例:** 444```ts 445import { NodeController, BuilderNode, FrameNode, UIContext } from "@kit.ArkUI"; 446 447class Params { 448 text: string = "" 449 constructor(text: string) { 450 this.text = text; 451 } 452} 453 454// 自定义组件 455@Component 456struct TextBuilder { 457 @Prop message: string = "TextBuilder"; 458 459 build() { 460 Row() { 461 Column() { 462 Text(this.message) 463 .fontSize(50) 464 .fontWeight(FontWeight.Bold) 465 .margin({bottom: 36}) 466 .backgroundColor(Color.Gray) 467 } 468 } 469 } 470} 471 472@Builder 473function buildText(params: Params) { 474 Column() { 475 Text(params.text) 476 .fontSize(50) 477 .fontWeight(FontWeight.Bold) 478 .margin({ bottom: 36 }) 479 TextBuilder({message: params.text}) // 自定义组件 480 } 481} 482 483class TextNodeController extends NodeController { 484 private rootNode: FrameNode | null = null; 485 private textNode: BuilderNode<[Params]> | null = null; 486 private message: string = ""; 487 488 constructor(message: string) { 489 super() 490 this.message = message 491 } 492 493 makeNode(context: UIContext): FrameNode | null { 494 this.textNode = new BuilderNode(context); 495 this.textNode.build(wrapBuilder<[Params]>(buildText), new Params(this.message)) 496 return this.textNode.getFrameNode(); 497 } 498 499 update(message: string) { 500 if (this.textNode !== null) { 501 this.textNode.update(new Params(message)); 502 } 503 } 504} 505 506@Entry 507@Component 508struct Index { 509 @State message: string = "hello" 510 private textNodeController: TextNodeController = new TextNodeController(this.message); 511 private count = 0; 512 513 build() { 514 Row() { 515 Column() { 516 NodeContainer(this.textNodeController) 517 .width('100%') 518 .height(200) 519 .backgroundColor('#FFF0F0F0') 520 Button('Update') 521 .onClick(() => { 522 this.count += 1; 523 const message = "Update " + this.count.toString(); 524 this.textNodeController.update(message); 525 }) 526 } 527 .width('100%') 528 .height('100%') 529 } 530 .height('100%') 531 } 532} 533``` 534 535### postTouchEvent 536 537postTouchEvent(event: TouchEvent): boolean 538 539将原始事件派发到某个BuilderNode创建出的FrameNode上。 540 541postTouchEvent是从组件树的中间节点往下分发,需要变换到父组件坐标系才能分发成功,参考下图。 542 543OffsetA为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的最终结果。 544 545 546 547> **说明:** 548> 549> 传入的坐标值需要转换为px,如果builderNode有仿射变换,则需要再叠加仿射变换。 550> 551> 在[webview](../apis-arkweb/js-apis-webview.md)中,内部已经处理过坐标系变换,可以将TouchEvent事件直接下发。 552> 553> 同一时间戳,postTouchEvent只能调用一次。<!--Del--> 554> 555> 不支持[UIExtensionComponent](arkui-ts/ts-container-ui-extension-component-sys.md)。 556<!--DelEnd--> 557 558**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 559 560**系统能力:** SystemCapability.ArkUI.ArkUI.Full 561 562**参数:** 563 564| 参数名 | 类型 | 必填 | 说明 | 565| ------ | ------------------------------------------------------------------------- | ---- | ---------- | 566| event | [TouchEvent](arkui-ts/ts-universal-events-touch.md#touchevent对象说明) | 是 | 触摸事件。 | 567 568**返回值:** 569 570| 类型 | 说明 | 571| ------- | ------------------ | 572| boolean | 派发事件是否成功。true为已命中响应事件的组件,false为未命中任何可响应事件的组件。<br/>**说明:** <br/>如果未按照预期命中组件,需要确认以下几点:<br/>1.坐标系是否转换正确。<br/>2.组件是否可交互状态。<br/>3.是否绑定事件。 | 573 574**示例:** 575 576```ts 577import { NodeController, BuilderNode, FrameNode, UIContext } from '@kit.ArkUI'; 578 579class Params { 580 text: string = "this is a text" 581} 582 583@Builder 584function ButtonBuilder(params: Params) { 585 Column() { 586 Button(`button ` + params.text) 587 .borderWidth(2) 588 .backgroundColor(Color.Orange) 589 .width("100%") 590 .height("100%") 591 .gesture( 592 TapGesture() 593 .onAction((event: GestureEvent) => { 594 console.log("TapGesture"); 595 }) 596 ) 597 } 598 .width(500) 599 .height(300) 600 .backgroundColor(Color.Gray) 601} 602 603class MyNodeController extends NodeController { 604 private rootNode: BuilderNode<[Params]> | null = null; 605 private wrapBuilder: WrappedBuilder<[Params]> = wrapBuilder(ButtonBuilder); 606 607 makeNode(uiContext: UIContext): FrameNode | null { 608 this.rootNode = new BuilderNode(uiContext); 609 this.rootNode.build(this.wrapBuilder, { text: "this is a string" }) 610 return this.rootNode.getFrameNode(); 611 } 612 613 // 坐标转换示例 614 postTouchEvent(event: TouchEvent): boolean { 615 if (this.rootNode == null) { 616 return false; 617 } 618 let node: FrameNode | null = this.rootNode.getFrameNode(); 619 let offsetX: number | null | undefined = node?.getPositionToParent().x; 620 let offsetY: number | null | undefined = node?.getPositionToParent().y; 621 ; 622 let changedTouchLen = event.changedTouches.length; 623 for (let i = 0; i < changedTouchLen; i++) { 624 if (offsetX != null && offsetY != null && offsetX != undefined && offsetY != undefined) { 625 event.changedTouches[i].x = vp2px(offsetX + event.changedTouches[i].x); 626 event.changedTouches[i].y = vp2px(offsetY + event.changedTouches[i].y); 627 } 628 } 629 let result = this.rootNode.postTouchEvent(event); 630 console.log("result " + result); 631 return result; 632 } 633} 634 635@Entry 636@Component 637struct MyComponent { 638 private nodeController: MyNodeController = new MyNodeController(); 639 640 build() { 641 Column() { 642 NodeContainer(this.nodeController) 643 .height(300) 644 .width(500) 645 646 Column() 647 .width(500) 648 .height(300) 649 .backgroundColor(Color.Pink) 650 .onTouch((event) => { 651 if (event != undefined) { 652 this.nodeController.postTouchEvent(event); 653 } 654 }) 655 } 656 } 657} 658``` 659 660### dispose<sup>12+</sup> 661 662dispose(): void 663 664立即释放当前BuilderNode。当BuilderNode对象调用dispose接口之后,不仅BuilderNode对象与后端实体节点解除引用关系,BuilderNode中的FrameNode与RenderNode也会同步和实体节点解除引用关系。 665 666**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 667 668**系统能力:** SystemCapability.ArkUI.ArkUI.Full 669 670```ts 671import { RenderNode, FrameNode, NodeController, BuilderNode } from "@kit.ArkUI"; 672 673@Component 674struct TestComponent { 675 build() { 676 Column() { 677 Text('This is a BuilderNode.') 678 .fontSize(16) 679 .fontWeight(FontWeight.Bold) 680 } 681 .width('100%') 682 .backgroundColor(Color.Gray) 683 } 684 685 aboutToAppear() { 686 console.error('aboutToAppear'); 687 } 688 689 aboutToDisappear() { 690 console.error('aboutToDisappear'); 691 } 692} 693 694@Builder 695function buildComponent() { 696 TestComponent() 697} 698 699class MyNodeController extends NodeController { 700 private rootNode: FrameNode | null = null; 701 private builderNode: BuilderNode<[]> | null = null; 702 703 makeNode(uiContext: UIContext): FrameNode | null { 704 this.rootNode = new FrameNode(uiContext); 705 this.builderNode = new BuilderNode(uiContext, { selfIdealSize: { width: 200, height: 100 } }); 706 this.builderNode.build(new WrappedBuilder(buildComponent)); 707 708 const rootRenderNode = this.rootNode!.getRenderNode(); 709 if (rootRenderNode !== null) { 710 rootRenderNode.size = { width: 200, height: 200 }; 711 rootRenderNode.backgroundColor = 0xff00ff00; 712 rootRenderNode.appendChild(this.builderNode!.getFrameNode()!.getRenderNode()); 713 } 714 715 return this.rootNode; 716 } 717 718 dispose() { 719 if (this.builderNode !== null) { 720 this.builderNode.dispose(); 721 } 722 } 723 724 removeBuilderNode() { 725 const rootRenderNode = this.rootNode!.getRenderNode(); 726 if (rootRenderNode !== null && this.builderNode !== null && this.builderNode.getFrameNode() !== null) { 727 rootRenderNode.removeChild(this.builderNode!.getFrameNode()!.getRenderNode()); 728 } 729 } 730} 731 732@Entry 733@Component 734struct Index { 735 private myNodeController: MyNodeController = new MyNodeController(); 736 737 build() { 738 Column({ space: 4 }) { 739 NodeContainer(this.myNodeController) 740 Button('BuilderNode dispose') 741 .onClick(() => { 742 this.myNodeController.removeBuilderNode(); 743 this.myNodeController.dispose(); 744 }) 745 .width('100%') 746 } 747 } 748} 749``` 750 751### reuse<sup>12+</sup> 752 753reuse(param?: Object): void 754 755传递reuse事件到BuilderNode中的自定义组件。 756 757**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 758 759**系统能力:** SystemCapability.ArkUI.ArkUI.Full 760 761**参数:** 762 763| 参数名 | 类型 | 必填 | 说明 | 764| ------ | ------ | ---- | ------------------------------------------------------------------------ | 765| param | Object | 否 | 用于复用BuilderNode的参数,和[build](#build)调用时传入的参数类型一致。 | 766 767### recycle<sup>12+</sup> 768 769recycle(): void 770 771传递recycle事件到BuilderNode中的自定义组件。 772 773**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 774 775**系统能力:** SystemCapability.ArkUI.ArkUI.Full 776 777```ts 778import { FrameNode,NodeController,BuilderNode,UIContext } from "@kit.ArkUI"; 779 780class MyDataSource { 781 private dataArray: string[] = []; 782 private listener: DataChangeListener | null = null 783 784 public totalCount(): number { 785 return this.dataArray.length; 786 } 787 788 public getData(index: number) { 789 return this.dataArray[index]; 790 } 791 792 public pushData(data: string) { 793 this.dataArray.push(data); 794 } 795 796 public reloadListener(): void { 797 this.listener?.onDataReloaded(); 798 } 799 800 public registerDataChangeListener(listener: DataChangeListener): void { 801 this.listener = listener; 802 } 803 804 public unregisterDataChangeListener(): void { 805 this.listener = null; 806 } 807} 808 809class Params { 810 item: string = ''; 811 812 constructor(item: string) { 813 this.item = item; 814 } 815} 816 817@Builder 818function buildNode(param: Params = new Params("hello")) { 819 ReusableChildComponent2({ item: param.item }); 820} 821 822class MyNodeController extends NodeController { 823 public builderNode: BuilderNode<[Params]> | null = null; 824 public item: string = ""; 825 826 makeNode(uiContext: UIContext): FrameNode | null { 827 if (this.builderNode == null) { 828 this.builderNode = new BuilderNode(uiContext, { selfIdealSize: { width: 300, height: 200 } }); 829 this.builderNode.build(wrapBuilder<[Params]>(buildNode), new Params(this.item)); 830 } 831 return this.builderNode.getFrameNode(); 832 } 833} 834 835@Reusable 836@Component 837struct ReusableChildComponent { 838 @State item: string = ''; 839 private controller: MyNodeController = new MyNodeController(); 840 841 aboutToAppear() { 842 this.controller.item = this.item; 843 } 844 845 aboutToRecycle(): void { 846 console.log("ReusableChildComponent aboutToRecycle " + this.item); 847 this.controller?.builderNode?.recycle(); 848 } 849 850 aboutToReuse(params: object): void { 851 console.log("ReusableChildComponent aboutToReuse " + JSON.stringify(params)); 852 this.controller?.builderNode?.reuse(params); 853 } 854 855 build() { 856 NodeContainer(this.controller); 857 } 858} 859 860@Component 861struct ReusableChildComponent2 { 862 @Prop item: string = "false"; 863 864 aboutToReuse(params: Record<string, object>) { 865 console.log("ReusableChildComponent2 Reusable 2 " + JSON.stringify(params)); 866 } 867 868 aboutToRecycle(): void { 869 console.log("ReusableChildComponent2 aboutToRecycle 2 " + this.item); 870 } 871 872 build() { 873 Row() { 874 Text(this.item) 875 .fontSize(20) 876 .backgroundColor(Color.Yellow) 877 .margin({ left: 10 }) 878 }.margin({ left: 10, right: 10 }) 879 } 880} 881 882 883@Entry 884@Component 885struct Index { 886 @State data: MyDataSource = new MyDataSource(); 887 888 aboutToAppear() { 889 for (let i = 0;i < 100; i++) { 890 this.data.pushData(i.toString()); 891 } 892 } 893 894 build() { 895 Column() { 896 List({ space: 3 }) { 897 LazyForEach(this.data, (item: string) => { 898 ListItem() { 899 ReusableChildComponent({ item: item }) 900 } 901 }, (item: string) => item) 902 } 903 .width('100%') 904 .height('100%') 905 } 906 } 907} 908``` 909 910### updateConfiguration<sup>12+</sup> 911 912updateConfiguration(): void 913 914传递[系统环境变化](../apis-ability-kit/js-apis-app-ability-configuration.md)事件,触发节点的全量更新。 915 916**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 917 918**系统能力:** SystemCapability.ArkUI.ArkUI.Full 919 920> **说明:** 921> 922> updateConfiguration接口用于通知对象更新,更新所使用的系统环境由应用当前的系统环境变化决定。 923 924**示例:** 925```ts 926import { NodeController, BuilderNode, FrameNode, UIContext } from "@kit.ArkUI"; 927import { AbilityConstant, Configuration, EnvironmentCallback } from '@kit.AbilityKit'; 928 929class Params { 930 text: string = "" 931 932 constructor(text: string) { 933 this.text = text; 934 } 935} 936 937// 自定义组件 938@Component 939struct TextBuilder { 940 // 作为自定义组件中需要更新的属性,数据类型为基础属性,定义为@Prop 941 @Prop message: string = "TextBuilder"; 942 943 build() { 944 Row() { 945 Column() { 946 Text(this.message) 947 .fontSize(50) 948 .fontWeight(FontWeight.Bold) 949 .margin({ bottom: 36 }) 950 .fontColor($r(`app.color.text_color`)) 951 .backgroundColor($r(`app.color.start_window_background`)) 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 .fontColor($r(`app.color.text_color`)) 965 TextBuilder({ message: params.text }) // 自定义组件 966 }.backgroundColor($r(`app.color.start_window_background`)) 967} 968 969class TextNodeController extends NodeController { 970 private textNode: BuilderNode<[Params]> | null = null; 971 private message: string = ""; 972 973 constructor(message: string) { 974 super() 975 this.message = message; 976 } 977 978 makeNode(context: UIContext): FrameNode | null { 979 return this.textNode?.getFrameNode() ? this.textNode?.getFrameNode() : null; 980 } 981 982 createNode(context: UIContext) { 983 this.textNode = new BuilderNode(context); 984 this.textNode.build(wrapBuilder<[Params]>(buildText), new Params(this.message)); 985 builderNodeMap.push(this.textNode); 986 } 987 988 deleteNode() { 989 let node = builderNodeMap.pop(); 990 node?.dispose(); 991 } 992 993 update(message: string) { 994 if (this.textNode !== null) { 995 // 调用update进行更新。 996 this.textNode.update(new Params(message)); 997 } 998 } 999} 1000 1001// 记录创建的自定义节点对象 1002const builderNodeMap: Array<BuilderNode<[Params]>> = new Array(); 1003 1004function updateColorMode() { 1005 builderNodeMap.forEach((value, index) => { 1006 // 通知BuilderNode环境变量改变 1007 value.updateConfiguration(); 1008 }) 1009} 1010 1011@Entry 1012@Component 1013struct Index { 1014 @State message: string = "hello" 1015 private textNodeController: TextNodeController = new TextNodeController(this.message); 1016 private count = 0; 1017 1018 aboutToAppear(): void { 1019 let environmentCallback: EnvironmentCallback = { 1020 onMemoryLevel: (level: AbilityConstant.MemoryLevel): void => { 1021 console.log('onMemoryLevel'); 1022 }, 1023 onConfigurationUpdated: (config: Configuration): void => { 1024 console.log('onConfigurationUpdated ' + JSON.stringify(config)); 1025 updateColorMode(); 1026 } 1027 } 1028 // 注册监听回调 1029 this.getUIContext().getHostContext()?.getApplicationContext().on('environment', environmentCallback); 1030 //创建自定义节点并添加至map 1031 this.textNodeController.createNode(this.getUIContext()); 1032 } 1033 1034 aboutToDisappear(): void { 1035 //移除map中的引用,并将自定义节点释放 1036 this.textNodeController.deleteNode(); 1037 } 1038 1039 build() { 1040 Row() { 1041 Column() { 1042 NodeContainer(this.textNodeController) 1043 .width('100%') 1044 .height(200) 1045 .backgroundColor('#FFF0F0F0') 1046 Button('Update') 1047 .onClick(() => { 1048 this.count += 1; 1049 const message = "Update " + this.count.toString(); 1050 this.textNodeController.update(message); 1051 }) 1052 } 1053 .width('100%') 1054 .height('100%') 1055 } 1056 .height('100%') 1057 } 1058} 1059``` 1060