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](../../ui/state-management/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](../../ui/state-management/arkts-builder.md)最多拥有一个根节点。 89支持自定义组件。不支持自定义组件使用[@Reusable](../../ui/state-management/arkts-create-custom-components.md#自定义组件的基本结构)、[@Link](../../ui/state-management/arkts-link.md)、[@Provide](../../ui/state-management/arkts-provide-and-consume.md)、[@Consume](../../ui/state-management/arkts-provide-and-consume.md)等装饰器,来同步BuilderNode挂载的页面与BuilderNode中自定义组件的状态。 90从API version 12开始,自定义组件支持接收[LocalStorage](../../ui/state-management/arkts-localstorage.md)实例。可以通过[传递LocalStorage实例](../../ui/state-management/arkts-localstorage.md#自定义组件接收localstorage实例)来使用LocalStorage相关的装饰器[@LocalStorageProp](../../ui/state-management/arkts-localstorage.md#localstorageprop)、[@LocalStorageLink](../../ui/state-management/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>](../../ui/state-management/arkts-wrapBuilder.md) | 是 | 创建对应节点树的时候所需的无状态UI方法[@Builder](../../ui/state-management/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](../../ui/state-management/arkts-builder.md)最多拥有一个根节点。 129支持自定义组件。不支持使用自定义组件使用[@Reusable](../../ui/state-management/arkts-create-custom-components.md#自定义组件的基本结构)、[@Link](../../ui/state-management/arkts-link.md)、[@Provide](../../ui/state-management/arkts-provide-and-consume.md)、[@Consume](../../ui/state-management/arkts-provide-and-consume.md)等装饰器用于当前页面与自定义组件的状态同步。 130从API version 12开始,自定义组件支持接收[LocalStorage](../../ui/state-management/arkts-localstorage.md)实例。可以通过[传递LocalStorage实例](../../ui/state-management/arkts-localstorage.md#自定义组件接收localstorage实例)来使用LocalStorage相关的装饰器[@LocalStorageProp](../../ui/state-management/arkts-localstorage.md#localstorageprop)、[@LocalStorageLink](../../ui/state-management/arkts-localstorage.md#localstoragelink)。 131 132> **说明** 133> 134> @Builder进行创建和更新的规格参考[@Builder](../../ui/state-management/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>](../../ui/state-management/arkts-wrapBuilder.md) | 是 | 创建对应节点树的时候所需的无状态UI方法[@Builder](../../ui/state-management/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, uiContext: UIContext): 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 = uiContext.vp2px(offsetX + event.changedTouches[i].x); 630 event.changedTouches[i].y = uiContext.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, this.getUIContext()); 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 790const TEST_TAG: string = "Reuse+Recycle"; 791 792class MyDataSource { 793 private dataArray: string[] = []; 794 private listener: DataChangeListener | null = null 795 796 public totalCount(): number { 797 return this.dataArray.length; 798 } 799 800 public getData(index: number) { 801 return this.dataArray[index]; 802 } 803 804 public pushData(data: string) { 805 this.dataArray.push(data); 806 } 807 808 public reloadListener(): void { 809 this.listener?.onDataReloaded(); 810 } 811 812 public registerDataChangeListener(listener: DataChangeListener): void { 813 this.listener = listener; 814 } 815 816 public unregisterDataChangeListener(): void { 817 this.listener = null; 818 } 819} 820 821class Params { 822 item: string = ''; 823 824 constructor(item: string) { 825 this.item = item; 826 } 827} 828 829@Builder 830function buildNode(param: Params = new Params("hello")) { 831 Row() { 832 Text(`C${param.item} -- `) 833 ReusableChildComponent2({ item: param.item }) //该自定义组件在BuilderNode中无法被正确复用 834 } 835} 836 837class MyNodeController extends NodeController { 838 public builderNode: BuilderNode<[Params]> | null = null; 839 public item: string = ""; 840 841 makeNode(uiContext: UIContext): FrameNode | null { 842 if (this.builderNode == null) { 843 this.builderNode = new BuilderNode(uiContext, { selfIdealSize: { width: 300, height: 200 } }); 844 this.builderNode.build(wrapBuilder<[Params]>(buildNode), new Params(this.item)); 845 } 846 return this.builderNode.getFrameNode(); 847 } 848} 849 850// 被回收复用的自定义组件,其状态变量会更新,而子自定义组件ReusableChildComponent3中的状态变量也会更新,但BuilderNode会阻断这一传递过程 851@Reusable 852@Component 853struct ReusableChildComponent { 854 @Prop item: string = ''; 855 @Prop switch: string = ''; 856 private controller: MyNodeController = new MyNodeController(); 857 858 aboutToAppear() { 859 this.controller.item = this.item; 860 } 861 862 aboutToRecycle(): void { 863 console.log(`${TEST_TAG} ReusableChildComponent aboutToRecycle ${this.item}`); 864 865 // 当开关为open,通过BuilderNode的reuse接口和recycle接口传递给其下的自定义组件,例如ReusableChildComponent2,完成复用 866 if (this.switch === 'open') { 867 this.controller?.builderNode?.recycle(); 868 } 869 } 870 871 aboutToReuse(params: object): void { 872 console.log(`${TEST_TAG} ReusableChildComponent aboutToReuse ${JSON.stringify(params)}`); 873 874 // 当开关为open,通过BuilderNode的reuse接口和recycle接口传递给其下的自定义组件,例如ReusableChildComponent2,完成复用 875 if (this.switch === 'open') { 876 this.controller?.builderNode?.reuse(params); 877 } 878 } 879 880 build() { 881 Row() { 882 Text(`A${this.item}--`) 883 ReusableChildComponent3({ item: this.item }) 884 NodeContainer(this.controller); 885 } 886 } 887} 888 889@Component 890struct ReusableChildComponent2 { 891 @Prop item: string = "false"; 892 893 aboutToReuse(params: Record<string, object>) { 894 console.log(`${TEST_TAG} ReusableChildComponent2 aboutToReuse ${JSON.stringify(params)}`); 895 } 896 897 aboutToRecycle(): void { 898 console.log(`${TEST_TAG} ReusableChildComponent2 aboutToRecycle ${this.item}`); 899 } 900 901 build() { 902 Row() { 903 Text(`D${this.item}`) 904 .fontSize(20) 905 .backgroundColor(Color.Yellow) 906 .margin({ left: 10 }) 907 }.margin({ left: 10, right: 10 }) 908 } 909} 910 911@Component 912struct ReusableChildComponent3 { 913 @Prop item: string = "false"; 914 915 aboutToReuse(params: Record<string, object>) { 916 console.log(`${TEST_TAG} ReusableChildComponent3 aboutToReuse ${JSON.stringify(params)}`); 917 } 918 919 aboutToRecycle(): void { 920 console.log(`${TEST_TAG} ReusableChildComponent3 aboutToRecycle ${this.item}`); 921 } 922 923 build() { 924 Row() { 925 Text(`B${this.item}`) 926 .fontSize(20) 927 .backgroundColor(Color.Yellow) 928 .margin({ left: 10 }) 929 }.margin({ left: 10, right: 10 }) 930 } 931} 932 933 934@Entry 935@Component 936struct Index { 937 @State data: MyDataSource = new MyDataSource(); 938 939 aboutToAppear() { 940 for (let i = 0; i < 100; i++) { 941 this.data.pushData(i.toString()); 942 } 943 } 944 945 build() { 946 Column() { 947 List({ space: 3 }) { 948 LazyForEach(this.data, (item: string) => { 949 ListItem() { 950 ReusableChildComponent({ 951 item: item, 952 switch: 'open' // 将open改为close可观察到,BuilderNode不通过reuse和recycle接口传递复用时,BuilderNode内部的自定义组件的行为表现 953 }) 954 } 955 }, (item: string) => item) 956 } 957 .width('100%') 958 .height('100%') 959 } 960 } 961} 962``` 963 964### updateConfiguration<sup>12+</sup> 965 966updateConfiguration(): void 967 968传递[系统环境变化](../apis-ability-kit/js-apis-app-ability-configuration.md)事件,触发节点的全量更新。 969 970**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 971 972**系统能力:** SystemCapability.ArkUI.ArkUI.Full 973 974> **说明:** 975> 976> updateConfiguration接口用于通知对象更新,更新所使用的系统环境由应用当前的系统环境变化决定。 977 978**示例:** 979```ts 980import { NodeController, BuilderNode, FrameNode, UIContext } from "@kit.ArkUI"; 981import { AbilityConstant, Configuration, ConfigurationConstant, EnvironmentCallback } from '@kit.AbilityKit'; 982 983class Params { 984 text: string = "" 985 986 constructor(text: string) { 987 this.text = text; 988 } 989} 990 991// 自定义组件 992@Component 993struct TextBuilder { 994 // 作为自定义组件中需要更新的属性,数据类型为基础属性,定义为@Prop 995 @Prop message: string = "TextBuilder"; 996 997 build() { 998 Row() { 999 Column() { 1000 Text(this.message) 1001 .fontSize(50) 1002 .fontWeight(FontWeight.Bold) 1003 .margin({ bottom: 36 }) 1004 } 1005 } 1006 } 1007} 1008 1009@Builder 1010function buildText(params: Params) { 1011 Column() { 1012 Text(params.text) 1013 .fontSize(50) 1014 .fontWeight(FontWeight.Bold) 1015 .margin({ bottom: 36 }) 1016 TextBuilder({ message: params.text }) // 自定义组件 1017 }.backgroundColor($r('sys.color.ohos_id_color_background')) 1018} 1019 1020class TextNodeController extends NodeController { 1021 private textNode: BuilderNode<[Params]> | null = null; 1022 private message: string = ""; 1023 1024 constructor(message: string) { 1025 super() 1026 this.message = message; 1027 } 1028 1029 makeNode(context: UIContext): FrameNode | null { 1030 return this.textNode?.getFrameNode() ? this.textNode?.getFrameNode() : null; 1031 } 1032 1033 createNode(context: UIContext) { 1034 this.textNode = new BuilderNode(context); 1035 this.textNode.build(wrapBuilder<[Params]>(buildText), new Params(this.message)); 1036 builderNodeMap.push(this.textNode); 1037 } 1038 1039 deleteNode() { 1040 let node = builderNodeMap.pop(); 1041 node?.dispose(); 1042 } 1043 1044 update(message: string) { 1045 if (this.textNode !== null) { 1046 // 调用update进行更新。 1047 this.textNode.update(new Params(message)); 1048 } 1049 } 1050} 1051 1052// 记录创建的自定义节点对象 1053const builderNodeMap: Array<BuilderNode<[Params]>> = new Array(); 1054 1055function updateColorMode() { 1056 builderNodeMap.forEach((value, index) => { 1057 // 通知BuilderNode环境变量改变 1058 value.updateConfiguration(); 1059 }) 1060} 1061 1062@Entry 1063@Component 1064struct Index { 1065 @State message: string = "hello" 1066 private textNodeController: TextNodeController = new TextNodeController(this.message); 1067 private count = 0; 1068 1069 aboutToAppear(): void { 1070 let environmentCallback: EnvironmentCallback = { 1071 onMemoryLevel: (level: AbilityConstant.MemoryLevel): void => { 1072 console.log('onMemoryLevel'); 1073 }, 1074 onConfigurationUpdated: (config: Configuration): void => { 1075 console.log('onConfigurationUpdated ' + JSON.stringify(config)); 1076 updateColorMode(); 1077 } 1078 } 1079 // 注册监听回调 1080 this.getUIContext().getHostContext()?.getApplicationContext().on('environment', environmentCallback); 1081 // 设置应用深浅色跟随系统 1082 this.getUIContext() 1083 .getHostContext()?.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET); 1084 //创建自定义节点并添加至map 1085 this.textNodeController.createNode(this.getUIContext()); 1086 } 1087 1088 aboutToDisappear(): void { 1089 //移除map中的引用,并将自定义节点释放 1090 this.textNodeController.deleteNode(); 1091 } 1092 1093 build() { 1094 Row() { 1095 Column() { 1096 NodeContainer(this.textNodeController) 1097 .width('100%') 1098 .height(200) 1099 .backgroundColor('#FFF0F0F0') 1100 Button('Update') 1101 .onClick(() => { 1102 this.count += 1; 1103 const message = "Update " + this.count.toString(); 1104 this.textNodeController.update(message); 1105 }) 1106 Button('切换深色') 1107 .onClick(() => { 1108 this.getUIContext() 1109 .getHostContext()?.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_DARK); 1110 }) 1111 Button('设置浅色') 1112 .onClick(() => { 1113 this.getUIContext() 1114 .getHostContext()?.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_LIGHT); 1115 }) 1116 } 1117 .width('100%') 1118 .height('100%') 1119 } 1120 .height('100%') 1121 } 1122} 1123``` 1124