• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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![postTouchEvent](figures/postTouchEvent.PNG)
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