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