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![postTouchEvent](figures/postTouchEvent.PNG)
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