• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# BuilderNode
2<!--Kit: ArkUI-->
3<!--Subsystem: ArkUI-->
4<!--Owner: @xiang-shouxing-->
5<!--Designer: @xiang-shouxing-->
6<!--Tester: @sally__-->
7<!--Adviser: @HelloCrease-->
8
9<!--Kit: ArkUI-->
10<!--Subsystem: ArkUI-->
11<!--Owner: @xiang-shouxing-->
12<!--Designer: @xiang-shouxing-->
13<!--Tester: @sally__-->
14
15提供能够挂载系统组件的自定义节点BuilderNode。BuilderNode仅可作为叶子节点使用。使用方式参考[BuilderNode开发指南](../../ui/arkts-user-defined-arktsNode-builderNode.md)。最佳实践请参考[组件动态创建-组件动态添加、更新和删除](https://developer.huawei.com/consumer/cn/doc/best-practices/bpta-ui-dynamic-operations#section153921947151012)16
17> **说明:**
18>
19> 本模块首批接口从API version 11开始支持。后续版本的新增接口,采用上角标单独标记接口的起始版本。
20>
21> 若传入的Builder的根节点为语法节点([Ifelse](../../ui/state-management/arkts-rendering-control-ifelse.md)/[foreach](../../ui/state-management/arkts-rendering-control-foreach.md)/[lazyforeach](../../ui/state-management/arkts-rendering-control-lazyforeach.md)/[ContentSlot](../../ui/state-management/arkts-rendering-control-contentslot.md)…)、[Span](./arkui-ts/ts-basic-components-span.md)、[ContainerSpan](./arkui-ts/ts-basic-components-containerspan.md)、[SymbolSpan](./arkui-ts/ts-basic-components-symbolSpan.md)或自定义组件,将额外生成一个[FrameNode](./js-apis-arkui-frameNode.md),在节点树中显示为“BuilderProxyNode”,这会导致树结构变化,影响某些测试的传递过程。详情参见[BuilderNode内的BuilderProxyNode导致树结构发生变化](../../ui/arkts-user-defined-arktsNode-builderNode.md#buildernode内的builderproxynode导致树结构发生变化)。
22>
23> 如果在跨页面复用BuilderNode时显示异常,可参考[跨页面复用注意事项](../../ui/arkts-user-defined-arktsNode-builderNode.md#跨页面复用注意事项)。
24>
25> 当前不支持在预览器中使用BuilderNode。
26>
27> BuilderNode下的自定义组件支持使用[@Prop](../../ui/state-management/arkts-prop.md)装饰器。不支持使用[@Link](../../ui/state-management/arkts-link.md)装饰器来跨越BuilderNode同步外界的数据和状态。
28>
29> 如果BuilderNode的子节点是自定义组件,不支持该自定义组件使用[@Reusable](../../ui/state-management/arkts-reusable.md)装饰器,详细内容参见[BuilderNode在子自定义组件中使用@Reusable装饰器](../../ui/arkts-user-defined-arktsNode-builderNode.md#buildernode在子自定义组件中使用reusable装饰器)。
30>
31> 从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)。
32>
33> 从API version 20开始,通过配置[BuildOptions](#buildoptions12),内部自定义组件的[@Consume](../../ui/state-management/arkts-provide-and-consume.md)支持接收所在页面的[@Provide](../../ui/state-management/arkts-provide-and-consume.md)数据。
34>
35> 其余装饰器行为未定义,不建议使用。
36
37## 导入模块
38
39```ts
40import { BuilderNode, RenderOptions, NodeRenderType } from "@kit.ArkUI";
41```
42
43## NodeRenderType
44
45节点渲染类型枚举。
46
47**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
48
49**系统能力:** SystemCapability.ArkUI.ArkUI.Full
50
51| 名称                | 值  | 说明                         |
52| ------------------- | --- | ---------------------------- |
53| RENDER_TYPE_DISPLAY | 0   | 表示该节点将被显示到屏幕上。 |
54| RENDER_TYPE_TEXTURE | 1   | 表示该节点将被导出为纹理。   |
55
56> **说明:**
57>
58> RENDER_TYPE_TEXTURE类型目前仅在[BuilderNode](#buildernode-1)持有组件树的根节点为自定义组件时以及[XComponentNode](./js-apis-arkui-xcomponentNode.md)中设置生效。
59>
60> 在[BuilderNode](#buildernode-1)的情况下,目前在作为根节点的自定义组件中支持纹理导出的有以下组件:[Badge](arkui-ts/ts-container-badge.md)、[Blank](arkui-ts/ts-basic-components-blank.md)、[Button](arkui-ts/ts-basic-components-button.md)、[CanvasGradient](arkui-ts/ts-components-canvas-canvasgradient.md)、[CanvasPattern](arkui-ts/ts-components-canvas-canvaspattern.md)、[CanvasRenderingContext2D](arkui-ts/ts-canvasrenderingcontext2d.md)、[Canvas](arkui-ts/ts-components-canvas-canvas.md)、[CheckboxGroup](arkui-ts/ts-basic-components-checkboxgroup.md)、[Checkbox](arkui-ts/ts-basic-components-checkbox.md)、[Circle](arkui-ts/ts-drawing-components-circle.md)、[ColumnSplit](arkui-ts/ts-container-columnsplit.md)、[Column](arkui-ts/ts-container-column.md)、[ContainerSpan](arkui-ts/ts-basic-components-containerspan.md)、[Counter](arkui-ts/ts-container-counter.md)、[DataPanel](arkui-ts/ts-basic-components-datapanel.md)、[Divider](arkui-ts/ts-basic-components-divider.md)、[Ellipse](arkui-ts/ts-drawing-components-ellipse.md)、[Flex](arkui-ts/ts-container-flex.md)、[Gauge](arkui-ts/ts-basic-components-gauge.md)、[Hyperlink](arkui-ts/ts-container-hyperlink.md)、[ImageBitmap](arkui-ts/ts-components-canvas-imagebitmap.md)、[ImageData](arkui-ts/ts-components-canvas-imagedata.md)、[Image](arkui-ts/ts-basic-components-image.md)、[Line](arkui-ts/ts-drawing-components-line.md)、[LoadingProgress](arkui-ts/ts-basic-components-loadingprogress.md)、[Marquee](arkui-ts/ts-basic-components-marquee.md)、[Matrix2D](arkui-ts/ts-components-canvas-matrix2d.md)、[OffscreenCanvasRenderingContext2D](arkui-ts/ts-offscreencanvasrenderingcontext2d.md)、[OffscreenCanvas](arkui-ts/ts-components-offscreencanvas.md)、[Path2D](arkui-ts/ts-components-canvas-path2d.md)、[Path](arkui-ts/ts-drawing-components-path.md)、[PatternLock](arkui-ts/ts-basic-components-patternlock.md)、[Polygon](arkui-ts/ts-drawing-components-polygon.md)、[Polyline](arkui-ts/ts-drawing-components-polyline.md)、[Progress](arkui-ts/ts-basic-components-progress.md)、[QRCode](arkui-ts/ts-basic-components-qrcode.md)、[Radio](arkui-ts/ts-basic-components-radio.md)、[Rating](arkui-ts/ts-basic-components-rating.md)、[Rect](arkui-ts/ts-drawing-components-rect.md)、[RelativeContainer](arkui-ts/ts-container-relativecontainer.md)、[RowSplit](arkui-ts/ts-container-rowsplit.md)、[Row](arkui-ts/ts-container-row.md)、[Shape](arkui-ts/ts-drawing-components-shape.md)、[Slider](arkui-ts/ts-basic-components-slider.md)、[Span](arkui-ts/ts-basic-components-span.md)、[Stack](arkui-ts/ts-container-stack.md)、[TextArea](arkui-ts/ts-basic-components-textarea.md)、[TextClock](arkui-ts/ts-basic-components-textclock.md)、[TextInput](arkui-ts/ts-basic-components-textinput.md)、[TextTimer](arkui-ts/ts-basic-components-texttimer.md)、[Text](arkui-ts/ts-basic-components-text.md)、[Toggle](arkui-ts/ts-basic-components-toggle.md)、[Video](arkui-ts/ts-media-components-video.md)(不含全屏播放能力)、[Web](../apis-arkweb/arkts-basic-components-web.md)、[XComponent](arkui-ts/ts-basic-components-xcomponent.md)。
61>
62> 从API version 12开始,新增以下组件支持纹理导出:[DatePicker](arkui-ts/ts-basic-components-datepicker.md)、[ForEach](arkui-ts/ts-rendering-control-foreach.md)、[Grid](arkui-ts/ts-container-grid.md)、[IfElse](../../ui/state-management/arkts-rendering-control-ifelse.md)、[LazyForEach](arkui-ts/ts-rendering-control-lazyforeach.md)、[List](arkui-ts/ts-container-list.md)、[Scroll](arkui-ts/ts-container-scroll.md)、[Swiper](arkui-ts/ts-container-swiper.md)、[TimePicker](arkui-ts/ts-basic-components-timepicker.md)、[@Component](../../ui/state-management/arkts-create-custom-components.md#component)修饰的自定义组件、[NodeContainer](arkui-ts/ts-basic-components-nodecontainer.md)以及[NodeContainer](arkui-ts/ts-basic-components-nodecontainer.md)下挂载的[FrameNode](./js-apis-arkui-frameNode.md)和[RenderNode](./js-apis-arkui-renderNode.md)。
63>
64> 使用方式可参考[同层渲染绘制](../../web/web-same-layer.md)。
65
66## RenderOptions
67
68创建BuilderNode时的可选参数。
69
70**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
71
72**系统能力:** SystemCapability.ArkUI.ArkUI.Full
73
74| 名称          | 类型                                   | 必填 | 说明                                                         |
75| ------------- | -------------------------------------- | ---- | ------------------------------------------------------------ |
76| selfIdealSize | [Size](js-apis-arkui-graphics.md#size) | 否   | 节点的理想大小。<br/>默认值:{ width: 0, height: 0 } |
77| type          | [NodeRenderType](#noderendertype)      | 否   | 节点的渲染类型。<br/>默认值:NodeRenderType.RENDER_TYPE_DISPLAY |
78| surfaceId     | string                                 | 否   | 纹理接收方的surfaceId。纹理接收方一般为[OH_NativeImage](../apis-arkgraphics2d/capi-oh-nativeimage-oh-nativeimage.md)。<br/>surfaceId仅当type为NodeRenderType.RENDER_TYPE_TEXTURE时生效。<br/>默认值:"" |
79
80## BuildOptions<sup>12+</sup>
81
82build的可选参数。
83
84**系统能力:** SystemCapability.ArkUI.ArkUI.Full
85
86| 名称          | 类型                                   | 只读 | 可选 | 说明                                                         |
87| ------------- | -------------------------------------- | ---- | ---- | ------------------------------------------------------------ |
88| nestingBuilderSupported | boolean | 否   | 是   | 是否支持Builder嵌套Builder进行使用。其中,true表示支持,false表示不支持。默认值:false <br/>**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 |
89| localStorage<sup>20+</sup> | [LocalStorage](../../ui/state-management/arkts-localstorage.md) | 否   | 是   | 给当前BuilderNode设置LocalStorage,挂载在此BuilderNode下的自定义组件共享该LocalStorage。如果自定义组件构造函数同时也传入LocalStorage,优先使用构造函数中传入的LocalStorage。默认值:null <br/>**原子化服务API:** 从API version 20开始,该接口支持在原子化服务中使用。 |
90| enableProvideConsumeCrossing<sup>20+</sup> | boolean | 否   | 是   | 定义BuilderNode内自定义组件的@Consume是否与BuilderNode外部的@Provide状态互通。true表示支持,false表示不支持。默认值:false <br/>**原子化服务API:** 从API version 20开始,该接口支持在原子化服务中使用。 |
91
92## InputEventType<sup>20+</sup>
93
94type InputEventType = TouchEvent | MouseEvent | AxisEvent
95
96postInputEvent的参数,定义要发送的输入事件类型。
97
98**原子化服务API:** 从API version 20开始,该接口支持在原子化服务中使用。
99
100**系统能力:** SystemCapability.ArkUI.ArkUI.Full
101
102| 类型 | 说明                                   |
103| ------------- | -------------------------------------- |
104| [TouchEvent](arkui-ts/ts-universal-events-touch.md#touchevent对象说明)  | 触摸事件。 |
105| [MouseEvent](arkui-ts/ts-universal-mouse-key.md#mouseevent对象说明)  | 鼠标事件。 |
106| [AxisEvent](arkui-ts/ts-universal-events-axis.md#axisevent)  | 轴事件。 |
107
108## BuilderNode
109
110class BuilderNode\<Args extends Object[]>
111
112BuilderNode支持通过无状态的UI方法[@Builder](../../ui/state-management/arkts-builder.md)生成组件树,并持有组件树的根节点。不支持定义为状态变量。BuilderNode中持有的FrameNode仅用于将该BuilderNode作为子节点挂载到其他FrameNode上。对BuilderNode持有的FrameNode进行属性设置与子节点操作可能会产生未定义行为,因此不建议通过BuilderNode的[getFrameNode](#getframenode)方法和[FrameNode](js-apis-arkui-frameNode.md)的[getRenderNode](js-apis-arkui-frameNode.md#getrendernode)方法获取RenderNode,并通过[RenderNode](js-apis-arkui-renderNode.md)的接口对其进行属性设置与子节点操作。
113
114**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
115
116**系统能力:** SystemCapability.ArkUI.ArkUI.Full
117
118### constructor
119
120constructor(uiContext: UIContext, options?: RenderOptions)
121
122当将BuilderNode生成的内容嵌入到其它RenderNode中显示时,需要显式指定RenderOptions中的selfIdealSize,否则Builder内的节点默认父组件布局约束为[0, 0]。该场景下,若不设置selfIdealSize则认为BuilderNode中子树的根节点大小为[0, 0]。
123
124**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
125
126**系统能力:** SystemCapability.ArkUI.ArkUI.Full
127
128**参数:**
129
130| 参数名    | 类型                                    | 必填 | 说明                                                              |
131| --------- | --------------------------------------- | ---- | ----------------------------------------------------------------- |
132| uiContext | [UIContext](arkts-apis-uicontext-uicontext.md) | 是   | UI上下文,获取方式可参考[UIContext获取方法](./js-apis-arkui-node.md#uicontext获取方法)。 |
133| options   | [RenderOptions](#renderoptions)         | 否   | BuilderNode的构造可选参数。<br/>默认值:undefined   |
134
135> **说明**
136> uiContext的入参需要为一个有效的值,即UI上下文正确,如果传入非法值或者未设置,会导致创建失败。
137
138### build
139
140build(builder: WrappedBuilder\<Args>, arg?: Object): void
141
142依照传入的对象创建组件树,并持有组件树的根节点。无状态的UI方法[@Builder](../../ui/state-management/arkts-builder.md)最多拥有一个根节点。
143支持自定义组件。
144
145> **说明**
146>
147> @Builder嵌套使用的时候需要保证内外的@Builder方法的入参对象一致。
148>
149> 最外层的@Builder只支持一个入参。
150>
151> build的参数是值传递,需要使用[update](#update)接口进行更新。
152>
153> 需要操作BuilderNode中的对象时,需要保证其引用不被回收。当BuilderNode对象被虚拟机回收之后,它的FrameNode、RenderNode对象也会与后端节点解引用。即从BuilderNode中获取的FrameNode对象不对应任何一个节点。
154>
155> BuilderNode对象会持有实体节点的引用。如果不需要使用BuilderNode前端对象管理后端节点,可以调用[dispose](#dispose12)接口,实现前后端对象的解绑。
156
157**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
158
159**系统能力:** SystemCapability.ArkUI.ArkUI.Full
160
161**参数:**
162
163| 参数名  | 类型                                                            | 必填 | 说明                                                                                   |
164| ------- | --------------------------------------------------------------- | ---- | -------------------------------------------------------------------------------------- |
165| builder | [WrappedBuilder\<Args>](../../ui/state-management/arkts-wrapBuilder.md) | 是   | 创建对应节点树的时候所需的无状态UI方法[@Builder](../../ui/state-management/arkts-builder.md)。 |
166| arg     | Object                                                          | 否   | builder的入参。当前仅支持一个入参,且入参对象类型与@Builder定义的入参类型保持一致。<br/>默认值:undefined |
167
168### build<sup>12+</sup>
169
170build(builder: WrappedBuilder\<Args>, arg: Object, options: [BuildOptions](#buildoptions12)): void
171
172依照传入的对象创建组件树,并持有组件树的根节点。无状态的UI方法[@Builder](../../ui/state-management/arkts-builder.md)最多拥有一个根节点。
173支持自定义组件。
174
175> **说明**
176>
177> @Builder进行创建和更新的规格参考[@Builder](../../ui/state-management/arkts-builder.md)。
178>
179> 最外层的@Builder只支持一个入参。
180
181**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
182
183**系统能力:** SystemCapability.ArkUI.ArkUI.Full
184
185**参数:**
186
187| 参数名  | 类型                                                            | 必填 | 说明                                                                                    |
188| ------- | --------------------------------------------------------------- | ---- | -------------------------------------------------------------------------------------- |
189| builder | [WrappedBuilder\<Args>](../../ui/state-management/arkts-wrapBuilder.md) | 是   | 创建对应节点树的时候所需的无状态UI方法[@Builder](../../ui/state-management/arkts-builder.md)。   |
190| arg     | Object                                                          | 是   | builder的入参。当前仅支持一个入参,且入参对象类型与@Builder定义的入参类型保持一致。                                                            |
191| options | [BuildOptions](#buildoptions12)                                           | 是   | build的配置参数,判断是否支持@Builder中嵌套@Builder的行为。                                         |
192
193**示例:**
194```ts
195import { BuilderNode, NodeContent } from "@kit.ArkUI";
196
197interface ParamsInterface {
198  text: string;
199  func: Function;
200}
201
202@Builder
203function buildTextWithFunc(fun: Function) {
204  Text(fun())
205    .fontSize(50)
206    .fontWeight(FontWeight.Bold)
207    .margin({ bottom: 36 })
208}
209
210@Builder
211function buildText(params: ParamsInterface) {
212  Column() {
213    Text(params.text)
214      .fontSize(50)
215      .fontWeight(FontWeight.Bold)
216      .margin({ bottom: 36 })
217    buildTextWithFunc(params.func)
218  }
219}
220
221
222@Entry
223@Component
224struct Index {
225  @State message: string = "HELLO";
226  private content: NodeContent = new NodeContent();
227
228  build() {
229    Row() {
230      Column() {
231        Button('addBuilderNode')
232          .onClick(() => {
233            let buildNode = new BuilderNode<[ParamsInterface]>(this.getUIContext());
234            buildNode.build(wrapBuilder<[ParamsInterface]>(buildText), {
235              text: this.message, func: () => {
236                return "FUNCTION";
237              }
238            }, { nestingBuilderSupported: true });
239            this.content.addFrameNode(buildNode.getFrameNode());
240            buildNode.dispose();
241          })
242        ContentSlot(this.content)
243      }
244      .id("column")
245      .width('100%')
246      .height('100%')
247    }
248    .height('100%')
249  }
250}
251```
252
253### getFrameNode
254
255getFrameNode(): FrameNode | null
256
257获取BuilderNode中的FrameNode。在BuilderNode执行build操作之后,才会生成FrameNode。
258
259**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
260
261**系统能力:** SystemCapability.ArkUI.ArkUI.Full
262
263**返回值:**
264
265| 类型                                                      | 说明                                                                  |
266| --------------------------------------------------------- | --------------------------------------------------------------------- |
267| [FrameNode](js-apis-arkui-frameNode.md) \| null | 一个FrameNode对象。若该BuilderNode不包含FrameNode,则返回空对象null。 |
268
269**示例1:**
270
271BuilderNode作为NodeContainer的根节点返回。
272
273```ts
274import { NodeController, BuilderNode, FrameNode, UIContext } from "@kit.ArkUI";
275
276class Params {
277  text: string = "";
278  constructor(text: string) {
279    this.text = text;
280  }
281}
282
283@Builder
284function buildText(params: Params) {
285  Column() {
286    Text(params.text)
287      .fontSize(50)
288      .fontWeight(FontWeight.Bold)
289      .margin({bottom: 36})
290  }
291}
292
293class TextNodeController extends NodeController {
294  private textNode: BuilderNode<[Params]> | null = null;
295  private message: string = "DEFAULT";
296
297  constructor(message: string) {
298    super();
299    this.message = message;
300  }
301
302  makeNode(context: UIContext): FrameNode | null {
303    this.textNode = new BuilderNode(context);
304    this.textNode.build(wrapBuilder<[Params]>(buildText), new Params(this.message));
305
306    return this.textNode.getFrameNode();
307  }
308}
309
310@Entry
311@Component
312struct Index {
313  @State message: string = "hello";
314
315  build() {
316    Row() {
317      Column() {
318        NodeContainer(new TextNodeController(this.message))
319          .width('100%')
320          .height(100)
321          .backgroundColor('#FFF0F0F0')
322      }
323      .width('100%')
324      .height('100%')
325    }
326    .height('100%')
327  }
328}
329```
330
331**示例2:**
332
333BuilderNode的FrameNode挂到其它FrameNode下。
334
335```ts
336import { NodeController, BuilderNode, FrameNode, UIContext } from "@kit.ArkUI";
337
338class Params {
339  text: string = "";
340
341  constructor(text: string) {
342    this.text = text;
343  }
344}
345
346@Builder
347function buildText(params: Params) {
348  Column() {
349    Text(params.text)
350      .fontSize(50)
351      .fontWeight(FontWeight.Bold)
352      .margin({ bottom: 36 })
353  }
354}
355
356class TextNodeController extends NodeController {
357  private rootNode: FrameNode | null = null;
358  private textNode: BuilderNode<[Params]> | null = null;
359  private message: string = "DEFAULT";
360
361  constructor(message: string) {
362    super();
363    this.message = message;
364  }
365
366  makeNode(context: UIContext): FrameNode | null {
367    this.rootNode = new FrameNode(context);
368    this.textNode = new BuilderNode(context, { selfIdealSize: { width: 150, height: 150 } });
369    this.textNode.build(wrapBuilder<[Params]>(buildText), new Params(this.message));
370    if (this.rootNode !== null) {
371      this.rootNode.appendChild(this.textNode?.getFrameNode());
372    }
373
374    return this.rootNode;
375  }
376}
377
378@Entry
379@Component
380struct Index {
381  @State message: string = "hello";
382
383  build() {
384    Row() {
385      Column() {
386        NodeContainer(new TextNodeController(this.message))
387          .width('100%')
388          .height(100)
389          .backgroundColor('#FFF0F0F0')
390      }
391      .width('100%')
392      .height('100%')
393    }
394    .height('100%')
395  }
396}
397```
398
399**示例3:**
400
401BuilderNode的RenderNode挂到其它RenderNode下。由于RenderNode不传递布局约束,不推荐通过该方式挂载节点。
402
403```ts
404import { NodeController, BuilderNode, FrameNode, UIContext, RenderNode } from "@kit.ArkUI";
405
406class Params {
407  text: string = "";
408
409  constructor(text: string) {
410    this.text = text;
411  }
412}
413
414@Builder
415function buildText(params: Params) {
416  Column() {
417    Text(params.text)
418      .fontSize(50)
419      .fontWeight(FontWeight.Bold)
420      .margin({ bottom: 36 })
421  }
422}
423
424class TextNodeController extends NodeController {
425  private rootNode: FrameNode | null = null;
426  private textNode: BuilderNode<[Params]> | null = null;
427  private message: string = "DEFAULT";
428
429  constructor(message: string) {
430    super();
431    this.message = message;
432  }
433
434  makeNode(context: UIContext): FrameNode | null {
435    this.rootNode = new FrameNode(context);
436    let renderNode = new RenderNode();
437    renderNode.clipToFrame = false;
438    this.textNode = new BuilderNode(context, { selfIdealSize: { width: 150, height: 150 } });
439    this.textNode.build(wrapBuilder<[Params]>(buildText), new Params(this.message));
440    const textRenderNode = this.textNode?.getFrameNode()?.getRenderNode();
441
442    const rootRenderNode = this.rootNode.getRenderNode();
443    if (rootRenderNode !== null) {
444      rootRenderNode.appendChild(renderNode);
445      renderNode.appendChild(textRenderNode);
446    }
447
448    return this.rootNode;
449  }
450}
451
452@Entry
453@Component
454struct Index {
455  @State message: string = "hello";
456
457  build() {
458    Row() {
459      Column() {
460        NodeContainer(new TextNodeController(this.message))
461          .width('100%')
462          .height(100)
463          .backgroundColor('#FFF0F0F0')
464      }
465      .width('100%')
466      .height('100%')
467    }
468    .height('100%')
469  }
470}
471```
472
473### update
474
475update(arg: Object): void
476
477根据提供的参数更新BuilderNode,该参数与[build](#build)方法调用时传入的参数类型相同。对自定义组件进行update的时候需要在自定义组件中将使用的变量定义为[@Prop](../../ui/state-management/arkts-prop.md)类型。
478
479**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
480
481**系统能力:** SystemCapability.ArkUI.ArkUI.Full
482
483**参数:**
484
485| 参数名 | 类型   | 必填 | 说明                                                                     |
486| ------ | ------ | ---- | ------------------------------------------------------------------------ |
487| arg    | Object | 是   | 用于更新BuilderNode的参数,和[build](#build)调用时传入的参数类型一致。 |
488
489**示例:**
490```ts
491import { NodeController, BuilderNode, FrameNode, UIContext } from "@kit.ArkUI";
492
493class Params {
494  text: string = "";
495  constructor(text: string) {
496    this.text = text;
497  }
498}
499
500// 自定义组件
501@Component
502struct TextBuilder {
503  @Prop message: string = "TextBuilder";
504
505  build() {
506    Row() {
507      Column() {
508        Text(this.message)
509          .fontSize(50)
510          .fontWeight(FontWeight.Bold)
511          .margin({bottom: 36})
512          .backgroundColor(Color.Gray)
513      }
514    }
515  }
516}
517
518@Builder
519function buildText(params: Params) {
520  Column() {
521    Text(params.text)
522      .fontSize(50)
523      .fontWeight(FontWeight.Bold)
524      .margin({ bottom: 36 })
525    TextBuilder({message: params.text}) // 自定义组件
526  }
527}
528
529class TextNodeController extends NodeController {
530  private rootNode: FrameNode | null = null;
531  private textNode: BuilderNode<[Params]> | null = null;
532  private message: string = "";
533
534  constructor(message: string) {
535    super();
536    this.message = message;
537  }
538
539  makeNode(context: UIContext): FrameNode | null {
540    this.textNode = new BuilderNode(context);
541    this.textNode.build(wrapBuilder<[Params]>(buildText), new Params(this.message));
542    return this.textNode.getFrameNode();
543  }
544
545  update(message: string) {
546    if (this.textNode !== null) {
547      this.textNode.update(new Params(message));
548    }
549  }
550}
551
552@Entry
553@Component
554struct Index {
555  @State message: string = "hello";
556  private textNodeController: TextNodeController = new TextNodeController(this.message);
557  private count = 0;
558
559  build() {
560    Row() {
561      Column() {
562        NodeContainer(this.textNodeController)
563          .width('100%')
564          .height(200)
565          .backgroundColor('#FFF0F0F0')
566        Button('Update')
567          .onClick(() => {
568            this.count += 1;
569            const message = "Update " + this.count.toString();
570            this.textNodeController.update(message);
571          })
572      }
573      .width('100%')
574      .height('100%')
575    }
576    .height('100%')
577  }
578}
579```
580
581### postTouchEvent
582
583postTouchEvent(event: TouchEvent): boolean
584
585将原始事件派发到某个BuilderNode创建出的FrameNode上。
586
587postTouchEvent是从组件树的中间节点往下分发,需要变换到父组件坐标系才能分发成功,参考下图。
588
589OffsetA为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的最终结果。
590
591![postTouchEvent](figures/postTouchEvent.PNG)
592
593> **说明:**
594>
595> 传入的坐标值需要转换为px,如果builderNode有仿射变换,则需要再叠加仿射变换。
596>
597> 在[webview](../apis-arkweb/arkts-apis-webview.md)中,内部已经处理过坐标系变换,可以将TouchEvent事件直接下发。
598>
599> 同一时间戳,postTouchEvent只能调用一次。<!--Del-->
600>
601> 不支持[UIExtensionComponent](arkui-ts/ts-container-ui-extension-component-sys.md)。
602<!--DelEnd-->
603
604**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
605
606**系统能力:** SystemCapability.ArkUI.ArkUI.Full
607
608**参数:**
609
610| 参数名 | 类型                                                                      | 必填 | 说明       |
611| ------ | ------------------------------------------------------------------------- | ---- | ---------- |
612| event  | [TouchEvent](arkui-ts/ts-universal-events-touch.md#touchevent对象说明) | 是   | 触摸事件。 |
613
614**返回值:**
615
616| 类型    | 说明               |
617| ------- | ------------------ |
618| boolean | 派发事件是否成功。true为已命中响应事件的组件,false为未命中任何可响应事件的组件。<br/>**说明:** <br/>如果未按照预期命中组件,需要确认以下几点:<br/>1.坐标系是否转换正确。<br/>2.组件是否可交互状态。<br/>3.是否绑定事件。 |
619
620**示例:**
621
622```ts
623import { NodeController, BuilderNode, FrameNode, UIContext } from '@kit.ArkUI';
624
625class Params {
626  text: string = "this is a text";
627}
628
629@Builder
630function ButtonBuilder(params: Params) {
631  Column() {
632    Button(`button ` + params.text)
633      .borderWidth(2)
634      .backgroundColor(Color.Orange)
635      .width("100%")
636      .height("100%")
637      .gesture(
638        TapGesture()
639          .onAction((event: GestureEvent) => {
640            console.info("TapGesture");
641          })
642      )
643  }
644  .width(500)
645  .height(300)
646  .backgroundColor(Color.Gray)
647}
648
649class MyNodeController extends NodeController {
650  private rootNode: BuilderNode<[Params]> | null = null;
651  private wrapBuilder: WrappedBuilder<[Params]> = wrapBuilder(ButtonBuilder);
652
653  makeNode(uiContext: UIContext): FrameNode | null {
654    this.rootNode = new BuilderNode(uiContext);
655    this.rootNode.build(this.wrapBuilder, { text: "this is a string" });
656    return this.rootNode.getFrameNode();
657  }
658
659  // 坐标转换示例
660  postTouchEvent(event: TouchEvent, uiContext: UIContext): boolean {
661    if (this.rootNode == null) {
662      return false;
663    }
664    let node: FrameNode | null = this.rootNode.getFrameNode();
665    let offsetX: number | null | undefined = node?.getPositionToParent().x;
666    let offsetY: number | null | undefined = node?.getPositionToParent().y;
667
668    let changedTouchLen = event.changedTouches.length;
669    for (let i = 0; i < changedTouchLen; i++) {
670      if (offsetX != null && offsetY != null && offsetX != undefined && offsetY != undefined) {
671        event.changedTouches[i].x = uiContext.vp2px(offsetX + event.changedTouches[i].x);
672        event.changedTouches[i].y = uiContext.vp2px(offsetY + event.changedTouches[i].y);
673      }
674    }
675    let result = this.rootNode.postTouchEvent(event);
676    console.info("result " + result);
677    return result;
678  }
679}
680
681@Entry
682@Component
683struct MyComponent {
684  private nodeController: MyNodeController = new MyNodeController();
685
686  build() {
687    Column() {
688      NodeContainer(this.nodeController)
689        .height(300)
690        .width(500)
691
692      Column()
693        .width(500)
694        .height(300)
695        .backgroundColor(Color.Pink)
696        .onTouch((event) => {
697          if (event != undefined) {
698            this.nodeController.postTouchEvent(event, this.getUIContext());
699          }
700        })
701    }
702  }
703}
704```
705
706### dispose<sup>12+</sup>
707
708dispose(): void
709
710立即释放当前BuilderNode对象对[实体节点](../../ui/arkts-user-defined-node.md#基本概念)的引用关系。关于BuilderNode的解绑场景请参见[节点解绑](../../ui/arkts-user-defined-arktsNode-builderNode.md#解除实体节点引用关系)。
711
712> **说明:**
713>
714> 当BuilderNode对象调用dispose之后,会与后端实体节点解除引用关系。若前端对象BuilderNode无法释放,容易导致内存泄漏。建议在不再需要对该BuilderNode对象进行操作时,开发者主动调用dispose释放后端节点,以减少引用关系的复杂性,降低内存泄漏的风险。
715
716**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
717
718**系统能力:** SystemCapability.ArkUI.ArkUI.Full
719
720```ts
721import { FrameNode, NodeController, BuilderNode } from '@kit.ArkUI';
722
723@Component
724struct TestComponent {
725  build() {
726    Column() {
727      Text('This is a BuilderNode.')
728        .fontSize(16)
729        .fontWeight(FontWeight.Bold)
730    }
731    .width('100%')
732    .backgroundColor(Color.Gray)
733  }
734
735  aboutToAppear() {
736    console.info('aboutToAppear');
737  }
738
739  aboutToDisappear() {
740    console.info('aboutToDisappear');
741  }
742}
743
744@Builder
745function buildComponent() {
746  TestComponent()
747}
748
749class MyNodeController extends NodeController {
750  private rootNode: FrameNode | null = null;
751  private builderNode: BuilderNode<[]> | null = null;
752
753  makeNode(uiContext: UIContext): FrameNode | null {
754    this.rootNode = new FrameNode(uiContext);
755    this.builderNode = new BuilderNode(uiContext, { selfIdealSize: { width: 200, height: 100 } });
756    this.builderNode.build(new WrappedBuilder(buildComponent));
757
758    const rootRenderNode = this.rootNode!.getRenderNode();
759    if (rootRenderNode !== null) {
760      rootRenderNode.size = { width: 200, height: 200 };
761      rootRenderNode.backgroundColor = 0xff00ff00;
762      rootRenderNode.appendChild(this.builderNode!.getFrameNode()!.getRenderNode());
763    }
764
765    return this.rootNode;
766  }
767
768  dispose() {
769    if (this.builderNode !== null) {
770      this.builderNode.dispose();
771    }
772  }
773
774  removeBuilderNode() {
775    const rootRenderNode = this.rootNode!.getRenderNode();
776    if (rootRenderNode !== null && this.builderNode !== null && this.builderNode.getFrameNode() !== null) {
777      rootRenderNode.removeChild(this.builderNode!.getFrameNode()!.getRenderNode());
778    }
779  }
780}
781
782@Entry
783@Component
784struct Index {
785  private myNodeController: MyNodeController = new MyNodeController();
786
787  build() {
788    Column({ space: 4 }) {
789      NodeContainer(this.myNodeController)
790      Button('BuilderNode dispose')
791        .onClick(() => {
792          this.myNodeController.removeBuilderNode();
793          this.myNodeController.dispose();
794        })
795        .width('100%')
796    }
797  }
798}
799```
800
801### reuse<sup>12+</sup>
802
803reuse(param?: Object): void
804
805触发BuilderNode中的自定义组件的复用。组件复用请参见[@Reusable装饰器:组件复用](../../ui/state-management/arkts-reusable.md)。关于BuilderNode的解绑场景请参见[节点解绑](../../ui/arkts-user-defined-arktsNode-builderNode.md#解除实体节点引用关系)。
806
807**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
808
809**系统能力:** SystemCapability.ArkUI.ArkUI.Full
810
811**参数:**
812
813| 参数名 | 类型   | 必填 | 说明                                                                     |
814| ------ | ------ | ---- | ------------------------------------------------------------------------ |
815| param  | Object | 否   | 用于复用BuilderNode的参数,和[build](#build)调用时传入的参数类型一致。 |
816
817### recycle<sup>12+</sup>
818
819recycle(): void
820
821- 触发BuilderNode中自定义组件的回收。自定义组件的回收是组件复用机制中的环节,具体信息请参见[@Reusable装饰器:组件复用](../../ui/state-management/arkts-reusable.md)。
822- BuilderNode通过reuse和recycle完成其内外自定义组件之间的复用事件传递,具体使用场景请参见[BuilderNode调用reuse和recycle接口实现节点复用能力](../../ui/arkts-user-defined-arktsNode-builderNode.md#buildernode调用reuse和recycle接口实现节点复用能力)。
823
824**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
825
826**系统能力:** SystemCapability.ArkUI.ArkUI.Full
827
828```ts
829import { FrameNode, NodeController, BuilderNode, UIContext } from "@kit.ArkUI";
830
831const TEST_TAG: string = "Reuse+Recycle";
832
833class MyDataSource {
834  private dataArray: string[] = [];
835  private listener: DataChangeListener | null = null;
836
837  public totalCount(): number {
838    return this.dataArray.length;
839  }
840
841  public getData(index: number) {
842    return this.dataArray[index];
843  }
844
845  public pushData(data: string) {
846    this.dataArray.push(data);
847  }
848
849  public reloadListener(): void {
850    this.listener?.onDataReloaded();
851  }
852
853  public registerDataChangeListener(listener: DataChangeListener): void {
854    this.listener = listener;
855  }
856
857  public unregisterDataChangeListener(): void {
858    this.listener = null;
859  }
860}
861
862class Params {
863  item: string = '';
864
865  constructor(item: string) {
866    this.item = item;
867  }
868}
869
870@Builder
871function buildNode(param: Params = new Params("hello")) {
872  Row() {
873    Text(`C${param.item} -- `)
874    ReusableChildComponent2({ item: param.item }) //该自定义组件在BuilderNode中无法被正确复用
875  }
876}
877
878class MyNodeController extends NodeController {
879  public builderNode: BuilderNode<[Params]> | null = null;
880  public item: string = "";
881
882  makeNode(uiContext: UIContext): FrameNode | null {
883    if (this.builderNode == null) {
884      this.builderNode = new BuilderNode(uiContext, { selfIdealSize: { width: 300, height: 200 } });
885      this.builderNode.build(wrapBuilder<[Params]>(buildNode), new Params(this.item));
886    }
887    return this.builderNode.getFrameNode();
888  }
889}
890
891// 被回收复用的自定义组件,其状态变量会更新,而子自定义组件ReusableChildComponent3中的状态变量也会更新,但BuilderNode会阻断这一传递过程
892@Reusable
893@Component
894struct ReusableChildComponent {
895  @Prop item: string = '';
896  @Prop switch: string = '';
897  private controller: MyNodeController = new MyNodeController();
898
899  aboutToAppear() {
900    this.controller.item = this.item;
901  }
902
903  aboutToRecycle(): void {
904    console.info(`${TEST_TAG} ReusableChildComponent aboutToRecycle ${this.item}`);
905
906    // 当开关为open,通过BuilderNode的reuse接口和recycle接口传递给其下的自定义组件,例如ReusableChildComponent2,完成复用
907    if (this.switch === 'open') {
908      this.controller?.builderNode?.recycle();
909    }
910  }
911
912  aboutToReuse(params: object): void {
913    console.info(`${TEST_TAG} ReusableChildComponent aboutToReuse ${JSON.stringify(params)}`);
914
915    // 当开关为open,通过BuilderNode的reuse接口和recycle接口传递给其下的自定义组件,例如ReusableChildComponent2,完成复用
916    if (this.switch === 'open') {
917      this.controller?.builderNode?.reuse(params);
918    }
919  }
920
921  build() {
922    Row() {
923      Text(`A${this.item}--`)
924      ReusableChildComponent3({ item: this.item })
925      NodeContainer(this.controller);
926    }
927  }
928}
929
930@Component
931struct ReusableChildComponent2 {
932  @Prop item: string = "false";
933
934  aboutToReuse(params: Record<string, object>) {
935    console.info(`${TEST_TAG} ReusableChildComponent2 aboutToReuse ${JSON.stringify(params)}`);
936  }
937
938  aboutToRecycle(): void {
939    console.info(`${TEST_TAG} ReusableChildComponent2 aboutToRecycle ${this.item}`);
940  }
941
942  build() {
943    Row() {
944      Text(`D${this.item}`)
945        .fontSize(20)
946        .backgroundColor(Color.Yellow)
947        .margin({ left: 10 })
948    }.margin({ left: 10, right: 10 })
949  }
950}
951
952@Component
953struct ReusableChildComponent3 {
954  @Prop item: string = "false";
955
956  aboutToReuse(params: Record<string, object>) {
957    console.info(`${TEST_TAG} ReusableChildComponent3 aboutToReuse ${JSON.stringify(params)}`);
958  }
959
960  aboutToRecycle(): void {
961    console.info(`${TEST_TAG} ReusableChildComponent3 aboutToRecycle ${this.item}`);
962  }
963
964  build() {
965    Row() {
966      Text(`B${this.item}`)
967        .fontSize(20)
968        .backgroundColor(Color.Yellow)
969        .margin({ left: 10 })
970    }.margin({ left: 10, right: 10 })
971  }
972}
973
974
975@Entry
976@Component
977struct Index {
978  @State data: MyDataSource = new MyDataSource();
979
980  aboutToAppear() {
981    for (let i = 0; i < 100; i++) {
982      this.data.pushData(i.toString());
983    }
984  }
985
986  build() {
987    Column() {
988      List({ space: 3 }) {
989        LazyForEach(this.data, (item: string) => {
990          ListItem() {
991            ReusableChildComponent({
992              item: item,
993              switch: 'open' // 将open改为close可观察到,BuilderNode不通过reuse和recycle接口传递复用时,BuilderNode内部的自定义组件的行为表现
994            })
995          }
996        }, (item: string) => item)
997      }
998      .width('100%')
999      .height('100%')
1000    }
1001  }
1002}
1003```
1004
1005### updateConfiguration<sup>12+</sup>
1006
1007updateConfiguration(): void
1008
1009传递[系统环境变化](../apis-ability-kit/js-apis-app-ability-configuration.md)事件,触发节点的全量更新。
1010
1011**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
1012
1013**系统能力:** SystemCapability.ArkUI.ArkUI.Full
1014
1015> **说明:**
1016>
1017> updateConfiguration接口用于通知对象更新,更新所使用的系统环境由应用当前的系统环境变化决定。
1018
1019**示例:**
1020```ts
1021import { NodeController, BuilderNode, FrameNode, UIContext, FrameCallback } from "@kit.ArkUI";
1022import { AbilityConstant, Configuration, ConfigurationConstant, EnvironmentCallback } from '@kit.AbilityKit';
1023
1024class Params {
1025  text: string = "";
1026
1027  constructor(text: string) {
1028    this.text = text;
1029  }
1030}
1031
1032// 自定义组件
1033@Component
1034struct TextBuilder {
1035  // 作为自定义组件中需要更新的属性,数据类型为基础属性,定义为@Prop
1036  @Prop message: string = "TextBuilder";
1037
1038  build() {
1039    Row() {
1040      Column() {
1041        Text(this.message)
1042          .fontSize(50)
1043          .fontWeight(FontWeight.Bold)
1044          .margin({ bottom: 36 })
1045      }
1046    }
1047  }
1048}
1049
1050@Builder
1051function buildText(params: Params) {
1052  Column() {
1053    Text(params.text)
1054      .fontSize(50)
1055      .fontWeight(FontWeight.Bold)
1056      .margin({ bottom: 36 })
1057    TextBuilder({ message: params.text }) // 自定义组件
1058  }.backgroundColor($r('sys.color.ohos_id_color_background'))
1059}
1060
1061class TextNodeController extends NodeController {
1062  private textNode: BuilderNode<[Params]> | null = null;
1063  private message: string = "";
1064
1065  constructor(message: string) {
1066    super();
1067    this.message = message;
1068  }
1069
1070  makeNode(context: UIContext): FrameNode | null {
1071    return this.textNode?.getFrameNode() ? this.textNode?.getFrameNode() : null;
1072  }
1073
1074  createNode(context: UIContext) {
1075    this.textNode = new BuilderNode(context);
1076    this.textNode.build(wrapBuilder<[Params]>(buildText), new Params(this.message));
1077    builderNodeMap.push(this.textNode);
1078  }
1079
1080  deleteNode() {
1081    let node = builderNodeMap.pop();
1082    node?.dispose();
1083  }
1084
1085  update(message: string) {
1086    if (this.textNode !== null) {
1087      // 调用update进行更新。
1088      this.textNode.update(new Params(message));
1089    }
1090  }
1091}
1092
1093// 记录创建的自定义节点对象
1094const builderNodeMap: Array<BuilderNode<[Params]>> = new Array();
1095
1096class MyFrameCallback extends FrameCallback {
1097  onFrame() {
1098    updateColorMode();
1099  }
1100}
1101
1102function updateColorMode() {
1103  builderNodeMap.forEach((value, index) => {
1104    // 通知BuilderNode环境变量改变,触发深浅色切换
1105    value.updateConfiguration();
1106  })
1107}
1108
1109@Entry
1110@Component
1111struct Index {
1112  @State message: string = "hello";
1113  private textNodeController: TextNodeController = new TextNodeController(this.message);
1114  private count = 0;
1115
1116  aboutToAppear(): void {
1117    let environmentCallback: EnvironmentCallback = {
1118      onMemoryLevel: (level: AbilityConstant.MemoryLevel): void => {
1119        console.info('onMemoryLevel');
1120      },
1121      onConfigurationUpdated: (config: Configuration): void => {
1122        console.info('onConfigurationUpdated ' + JSON.stringify(config));
1123        this.getUIContext()?.postFrameCallback(new MyFrameCallback());
1124      }
1125    };
1126    // 注册监听回调
1127    this.getUIContext().getHostContext()?.getApplicationContext().on('environment', environmentCallback);
1128    // 设置应用深浅色跟随系统
1129    this.getUIContext()
1130      .getHostContext()?.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET);
1131    //创建自定义节点并添加至map
1132    this.textNodeController.createNode(this.getUIContext());
1133  }
1134
1135  aboutToDisappear(): void {
1136    //移除map中的引用,并将自定义节点释放
1137    this.textNodeController.deleteNode();
1138  }
1139
1140  build() {
1141    Row() {
1142      Column() {
1143        NodeContainer(this.textNodeController)
1144          .width('100%')
1145          .height(200)
1146          .backgroundColor('#FFF0F0F0')
1147        Button('Update')
1148          .onClick(() => {
1149            this.count += 1;
1150            const message = "Update " + this.count.toString();
1151            this.textNodeController.update(message);
1152          })
1153        Button('切换深色')
1154          .onClick(() => {
1155            this.getUIContext()
1156              .getHostContext()?.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_DARK);
1157          })
1158        Button('设置浅色')
1159          .onClick(() => {
1160            this.getUIContext()
1161              .getHostContext()?.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_LIGHT);
1162          })
1163      }
1164      .width('100%')
1165      .height('100%')
1166    }
1167    .height('100%')
1168  }
1169}
1170```
1171
1172### isDisposed<sup>20+</sup>
1173
1174isDisposed(): boolean
1175
1176查询当前BuilderNode对象是否已解除与后端实体节点的引用关系。前端节点均绑定有相应的后端实体节点,当节点调用dispose接口解除绑定后,再次调用接口可能会出现crash、返回默认值的情况。由于业务需求,可能存在节点在dispose后仍被调用接口的情况。为此,提供此接口以供开发者在操作节点前检查其有效性,避免潜在风险。
1177
1178**原子化服务API:** 从API version 20开始,该接口支持在原子化服务中使用。
1179
1180**系统能力:** SystemCapability.ArkUI.ArkUI.Full
1181
1182**返回值:**
1183
1184| 类型    | 说明               |
1185| ------- | ------------------ |
1186| boolean | 后端实体节点是否解除引用。true为节点已与后端实体节点解除引用,false为节点未与后端实体节点解除引用。
1187
1188### postInputEvent<sup>20+</sup>
1189
1190postInputEvent(event: InputEventType): boolean
1191
1192将事件分发到目标节点。
1193
1194offsetA为builderNode相对于父组件的偏移,offsetB为命中位置相对于builderNode的偏移,offsetC为offsetA+offsetB,最终输入给postInputEvent中的window信息。
1195
1196![接口坐标换算示例图](figures/postInputEvent-point.png)
1197
1198> **说明:**
1199>
1200> 传入的坐标值需要转换为px,坐标转换示例可以参考下面示例代码。
1201>
1202> 鼠标左键点击事件将转换为触摸事件,转发时应注意不在外层同时绑定触摸事件与鼠标事件,否则可能导致坐标偏移。这是由于在事件转换过程中,SourceType不会发生变化,规格可查看[onTouch](arkui-ts/ts-universal-events-touch.md#ontouch)。
1203>
1204> 注入事件为[轴事件](arkui-ts/ts-universal-events-axis.md#axisevent)时,由于轴事件中缺少旋转轴信息与捏合轴信息,因此注入的事件无法触发[pinch捏合手势](arkui-ts/ts-basic-gestures-pinchgesture.md)与[rotate旋转手势](arkui-ts/ts-basic-gestures-rotationgesture.md)。
1205>
1206> 转发的事件会在被分发到的目标组件所在的子树里做touchtest,并触发对应手势,原始事件也会触发当前组件所在组件树中的手势。不保证两类手势的竞争结果。
1207>
1208> 如果是开发者构造的事件,必填字段必须赋值,比如触摸事件的touches字段,轴事件的scrollStep字段。要保证事件的完整,比如触摸事件的[TouchType](arkui-ts/ts-appendix-enums.md#touchtype)中DOWN和UP字段都要有,防止出现未定义行为。
1209>
1210> [webview](../apis-arkweb/arkts-apis-webview.md)已经处理过坐标系变换,可以将事件直接下发。
1211>
1212> postTouchEvent接口需要提供手势坐标相对于post事件对端内的局部坐标,postInputEvent接口需要提供手势坐标相对于post事件对端内的窗口坐标。
1213>
1214> 不建议同一个事件转发多次。<!--Del-->不支持[UIExtensionComponent](arkui-ts/ts-container-ui-extension-component-sys.md)。<!--DelEnd-->
1215
1216**原子化服务API:** 从API version 20开始,该接口支持在原子化服务中使用。
1217
1218**系统能力:** SystemCapability.ArkUI.ArkUI.Full
1219
1220**参数:**
1221
1222| 参数名 | 类型                                                                      | 必填 | 说明       |
1223| ------ | ------------------------------------------------------------------------- | ---- | ---------- |
1224| event  | [InputEventType](#inputeventtype20) | 是   | 用于透传的输入事件。 |
1225
1226**返回值:**
1227
1228| 类型    | 说明               |
1229| ------- | ------------------ |
1230| boolean | 事件是否被成功派发。如果事件派发成功,则返回true;否则,返回false。 |
1231
1232### inheritFreezeOptions<sup>20+</sup>
1233
1234inheritFreezeOptions(enabled: boolean): void
1235
1236查询当前BuilderNode对象是否设置为继承父组件中自定义组件的冻结策略。如果设置继承状态为false,则BuilderNode对象的冻结策略为false。在这种情况下,节点在不活跃状态下不会被冻结。
1237
1238**原子化服务API:** 从API version 20开始,该接口支持在原子化服务中使用。
1239
1240**系统能力:** SystemCapability.ArkUI.ArkUI.Full
1241
1242**参数:**
1243
1244| 参数名 | 类型   | 必填 | 说明                                                                     |
1245| ------ | ------ | ---- | ------------------------------------------------------------------------ |
1246| enabled  | boolean | 是  | BuilderNode对象是否设置为继承父组件中自定义组件的冻结策略。true为继承父组件中自定义组件的冻结策略,false为不继承父组件中自定义组件的冻结策略。 |
1247
1248## 示例
1249
1250### 示例1(鼠标事件)
1251
1252该示例演示了在自定义组件中截获鼠标事件并进行坐标转换的完整流程。组件通过onMouse回调读取本地x/y,再结合FrameNode.getPositionToParent()得到的偏移量,调用vp2px将相对坐标转换为像素坐标,更新MouseEvent的windowX/windowYdisplayX/displayY。最后通过rootNode.postInputEvent(event)将转换后的鼠标事件分发给子节点进行处理。
1253
1254```ts
1255import { NodeController, BuilderNode, FrameNode, UIContext, InputEventType } from '@kit.ArkUI';
1256
1257class Params {
1258  text: string = "this is a text"
1259  uiContext: UIContext | null = null
1260}
1261
1262@Builder
1263function ButtonBuilder(params: Params) {
1264  Column() {
1265    Button(params.text)
1266      .borderWidth(2)
1267      .align(Alignment.Center)
1268      .backgroundColor(Color.Orange)
1269      .fontSize(20)
1270      .width("45%")
1271      .height("30%")
1272      .offset({ x: 100, y: 100 })
1273      .onMouse(() => {
1274        console.info('onMouse')
1275      })
1276      .onTouch(() => {
1277        console.info('onTouch')
1278      })
1279  }
1280  .width(500)
1281  .height(300)
1282  .backgroundColor(Color.Gray)
1283}
1284
1285class MyNodeController extends NodeController {
1286  private rootNode: BuilderNode<[Params]> | null = null;
1287  private wrapBuilder: WrappedBuilder<[Params]> = wrapBuilder(ButtonBuilder);
1288
1289  makeNode(uiContext: UIContext): FrameNode | null {
1290    this.rootNode = new BuilderNode(uiContext);
1291    this.rootNode.build(this.wrapBuilder, { text: "This is a string", uiContext })
1292    return this.rootNode.getFrameNode();
1293  }
1294
1295  postMouseEvent(event: InputEventType, uiContext: UIContext): boolean {
1296    if (this.rootNode == null) {
1297      return false;
1298    }
1299    let node: FrameNode | null = this.rootNode.getFrameNode();
1300    let offsetX: number | null | undefined = node?.getPositionToParent().x;
1301    let offsetY: number | null | undefined = node?.getPositionToParent().y;
1302
1303    let mouseEvent = event as MouseEvent;
1304    if (offsetX != null && offsetY != null && offsetX != undefined && offsetY != undefined) {
1305      mouseEvent.windowX = uiContext.vp2px(offsetX + mouseEvent.x)
1306      mouseEvent.windowY = uiContext.vp2px(offsetY + mouseEvent.y)
1307      mouseEvent.displayX = uiContext.vp2px(offsetX + mouseEvent.x)
1308      mouseEvent.displayY = uiContext.vp2px(offsetY + mouseEvent.y)
1309      mouseEvent.x = uiContext.vp2px(mouseEvent.x)
1310      mouseEvent.y = uiContext.vp2px(mouseEvent.y)
1311    }
1312
1313    let result = this.rootNode.postInputEvent(event);
1314    return result;
1315  }
1316
1317  postTouchEvent(event: InputEventType, uiContext: UIContext): boolean {
1318    if (this.rootNode == null) {
1319      return false;
1320    }
1321    let node: FrameNode | null = this.rootNode.getFrameNode();
1322    let offsetX: number | null | undefined = node?.getPositionToParent().x;
1323    let offsetY: number | null | undefined = node?.getPositionToParent().y;
1324
1325    let touchEvent = event as TouchEvent;
1326    let changedTouchLen = touchEvent.changedTouches.length;
1327    for (let i = 0; i < changedTouchLen; i++) {
1328      if (offsetX != null && offsetY != null && offsetX != undefined && offsetY != undefined) {
1329        touchEvent.changedTouches[i].windowX = uiContext.vp2px(offsetX + touchEvent.changedTouches[i].x);
1330        touchEvent.changedTouches[i].windowY = uiContext.vp2px(offsetY + touchEvent.changedTouches[i].y);
1331        touchEvent.changedTouches[i].displayX = uiContext.vp2px(offsetX + touchEvent.changedTouches[i].x);
1332        touchEvent.changedTouches[i].displayY = uiContext.vp2px(offsetY + touchEvent.changedTouches[i].y);
1333      }
1334    }
1335    let touchesLen = touchEvent.touches.length;
1336    for (let i = 0; i < touchesLen; i++) {
1337      if (offsetX != null && offsetY != null && offsetX != undefined && offsetY != undefined) {
1338        touchEvent.touches[i].windowX = uiContext.vp2px(offsetX + touchEvent.touches[i].x);
1339        touchEvent.touches[i].windowY = uiContext.vp2px(offsetY + touchEvent.touches[i].y);
1340        touchEvent.touches[i].displayX = uiContext.vp2px(offsetX + touchEvent.touches[i].x);
1341        touchEvent.touches[i].displayY = uiContext.vp2px(offsetY + touchEvent.touches[i].y);
1342      }
1343    }
1344
1345    let result = this.rootNode.postInputEvent(event);
1346    return result;
1347  }
1348}
1349
1350@Entry
1351@Component
1352struct MyComponent {
1353  private nodeController: MyNodeController = new MyNodeController();
1354
1355  build() {
1356    Stack() {
1357      NodeContainer(this.nodeController)
1358        .height(300)
1359        .width(500)
1360      Column()
1361        .width(500)
1362        .height(300)
1363        .backgroundColor(Color.Transparent)
1364        .onMouse((event) => {
1365          if (event != undefined) {
1366            this.nodeController.postMouseEvent(event, this.getUIContext());
1367          }
1368        })
1369        .onTouch((event) => {
1370          if (event != undefined) {
1371            this.nodeController.postTouchEvent(event, this.getUIContext());
1372          }
1373        })
1374    }.offset({ top: 100 })
1375  }
1376}
1377```
1378
1379![OnMouse](figures/OnMouse.gif)
1380
1381### 示例2(触摸事件)
1382
1383该示例演示了在自定义组件中截获触摸事件并对触点坐标进行转换的完整流程。在onTouch回调中,遍历TouchEvent的changedTouches和touches数组,对每个触点的x/y加上组件偏移量并调用vp2px转换为像素,更新各自的windowX/windowYdisplayX/displayY。最后同样通过rootNode.postInputEvent(event)将转换后的触摸事件分发给子节点处理。
1384
1385```ts
1386import { NodeController, BuilderNode, FrameNode, UIContext, PromptAction, InputEventType  } from '@kit.ArkUI';
1387
1388class Params {
1389  text: string = "this is a text"
1390  uiContext: UIContext | null = null
1391}
1392@Builder
1393function ButtonBuilder(params: Params) {
1394  Column() {
1395    Button(params.text)
1396      .borderWidth(2)
1397      .align(Alignment.Center)
1398      .backgroundColor(Color.Orange)
1399      .fontSize(20)
1400      .width("45%")
1401      .height("30%")
1402      .offset({x: 100, y: 100})
1403      .onTouch((event) => {
1404        let promptAction: PromptAction = params.uiContext!.getPromptAction();
1405        promptAction.showToast({
1406          message: 'onTouch',
1407          duration: 3000
1408        });
1409        console.info('onTouch')
1410      })
1411  }
1412  .width(500)
1413  .height(300)
1414  .backgroundColor(Color.Gray)
1415}
1416class MyNodeController extends NodeController {
1417  private rootNode: BuilderNode<[Params]> | null = null;
1418  private wrapBuilder: WrappedBuilder<[Params]> = wrapBuilder(ButtonBuilder);
1419  makeNode(uiContext: UIContext): FrameNode | null {
1420    this.rootNode = new BuilderNode(uiContext);
1421    this.rootNode.build(this.wrapBuilder, { text: "This is a string", uiContext })
1422    return this.rootNode.getFrameNode();
1423  }
1424
1425  postInputEvent(event: InputEventType, uiContext: UIContext): boolean {
1426    if (this.rootNode == null) {
1427      return false;
1428    }
1429    let node: FrameNode | null = this.rootNode.getFrameNode();
1430    let offsetX: number | null | undefined = node?.getPositionToParent().x;
1431    let offsetY: number | null | undefined = node?.getPositionToParent().y;
1432
1433    // 只转发原始事件,不转发鼠标模拟的触摸事件
1434    if (event.source == SourceType.TouchScreen) {
1435      let touchEvent = event as TouchEvent;
1436      let changedTouchLen = touchEvent.changedTouches.length;
1437      for (let i = 0; i < changedTouchLen; i++) {
1438        if (offsetX != null && offsetY != null && offsetX != undefined && offsetY != undefined) {
1439          touchEvent.changedTouches[i].windowX = uiContext.vp2px(offsetX + touchEvent.changedTouches[i].x);
1440          touchEvent.changedTouches[i].windowY = uiContext.vp2px(offsetY + touchEvent.changedTouches[i].y);
1441          touchEvent.changedTouches[i].displayX = uiContext.vp2px(offsetX + touchEvent.changedTouches[i].x);
1442          touchEvent.changedTouches[i].displayY = uiContext.vp2px(offsetY + touchEvent.changedTouches[i].y);
1443        }
1444      }
1445      let touchesLen = touchEvent.touches.length;
1446      for (let i = 0; i < touchesLen; i++) {
1447        if (offsetX != null && offsetY != null && offsetX != undefined && offsetY != undefined) {
1448          touchEvent.touches[i].windowX = uiContext.vp2px(offsetX + touchEvent.touches[i].x);
1449          touchEvent.touches[i].windowY = uiContext.vp2px(offsetY + touchEvent.touches[i].y);
1450          touchEvent.touches[i].displayX = uiContext.vp2px(offsetX + touchEvent.touches[i].x);
1451          touchEvent.touches[i].displayY = uiContext.vp2px(offsetY + touchEvent.touches[i].y);
1452        }
1453      }
1454    }
1455
1456    let result = this.rootNode.postInputEvent(event);
1457    return result;
1458  }
1459}
1460@Entry
1461@Component
1462struct MyComponent {
1463  private nodeController: MyNodeController = new MyNodeController();
1464  build() {
1465    Stack() {
1466      NodeContainer(this.nodeController)
1467        .height(300)
1468        .width(500)
1469      Column()
1470        .width(500)
1471        .height(300)
1472        .backgroundColor(Color.Transparent)
1473        .onTouch((event) => {
1474          if (event != undefined) {
1475            this.nodeController.postInputEvent(event, this.getUIContext());
1476          }
1477        })
1478    }.offset({top: 100})
1479  }
1480}
1481```
1482
1483![OnTouch](figures/OnTouch.gif)
1484
1485### 示例3(轴事件)
1486
1487该示例演示了在自定义组件中截获滚轮或触控板轴事件并进行坐标转换的完整流程。在onAxisEvent回调中,先获取事件的相对x/y,再加上组件偏移量后调用vp2px转换为像素,更新AxisEvent的windowX/windowYdisplayX/displayY,最后通过rootNode.postInputEvent(event)将转换后的轴事件分发给子节点进行处理。
1488
1489```ts
1490import { NodeController, BuilderNode, FrameNode, UIContext, PromptAction, InputEventType } from '@kit.ArkUI';
1491
1492class Params {
1493  text: string = "this is a text"
1494  uiContext: UIContext | null = null
1495}
1496@Builder
1497function ButtonBuilder(params: Params) {
1498  Column() {
1499    Button(params.text)
1500      .borderWidth(2)
1501      .align(Alignment.Center)
1502      .backgroundColor(Color.Orange)
1503      .fontSize(20)
1504      .width("45%")
1505      .height("30%")
1506      .offset({x: 100, y: 100})
1507      .onAxisEvent((event) => {
1508        let promptAction: PromptAction = params.uiContext!.getPromptAction();
1509        promptAction.showToast({
1510          message: 'onAxisEvent',
1511          duration: 3000
1512        });
1513        console.info('onAxisEvent')
1514      })
1515  }
1516  .width(500)
1517  .height(300)
1518  .backgroundColor(Color.Gray)
1519}
1520class MyNodeController extends NodeController {
1521  private rootNode: BuilderNode<[Params]> | null = null;
1522  private wrapBuilder: WrappedBuilder<[Params]> = wrapBuilder(ButtonBuilder);
1523  makeNode(uiContext: UIContext): FrameNode | null {
1524    this.rootNode = new BuilderNode(uiContext);
1525    this.rootNode.build(this.wrapBuilder, { text: "This is a string", uiContext })
1526    return this.rootNode.getFrameNode();
1527  }
1528
1529  postInputEvent(event: InputEventType, uiContext: UIContext): boolean {
1530    if (this.rootNode == null) {
1531      return false;
1532    }
1533    let node: FrameNode | null = this.rootNode.getFrameNode();
1534    let offsetX: number | null | undefined = node?.getPositionToParent().x;
1535    let offsetY: number | null | undefined = node?.getPositionToParent().y;
1536
1537    let axisEvent = event as AxisEvent;
1538    if (offsetX != null && offsetY != null && offsetX != undefined && offsetY != undefined) {
1539      axisEvent.windowX = uiContext.vp2px(offsetX + axisEvent.x)
1540      axisEvent.windowY = uiContext.vp2px(offsetY + axisEvent.y)
1541      axisEvent.displayX = uiContext.vp2px(offsetX + axisEvent.x)
1542      axisEvent.displayY = uiContext.vp2px(offsetY + axisEvent.y)
1543      axisEvent.x = uiContext.vp2px(axisEvent.x)
1544      axisEvent.y = uiContext.vp2px(axisEvent.y)
1545    }
1546
1547    let result = this.rootNode.postInputEvent(event);
1548    return result;
1549  }
1550}
1551@Entry
1552@Component
1553struct MyComponent {
1554  private nodeController: MyNodeController = new MyNodeController();
1555  build() {
1556    Stack() {
1557      NodeContainer(this.nodeController)
1558        .height(300)
1559        .width(500)
1560      Column()
1561        .width(500)
1562        .height(300)
1563        .backgroundColor(Color.Transparent)
1564        .onAxisEvent((event) => {
1565          if (event != undefined) {
1566            this.nodeController.postInputEvent(event, this.getUIContext());
1567          }
1568        })
1569    }.offset({top: 100})
1570  }
1571}
1572```
1573
1574![onAxisEvent](figures/onAxisEvent.gif)
1575
1576### 示例4(BuilderNode共享localStorage)
1577该示例演示了如何在BuilderNode通过build方法传入外部localStorage,此时挂载在BuilderNode的所有自定义组件共享该localStorage。
1578```ts
1579import { NodeController, BuilderNode, FrameNode, UIContext } from '@kit.ArkUI';
1580
1581class Params {
1582  text: string = ""
1583  constructor(text: string) {
1584    this.text = text;
1585  }
1586}
1587
1588let globalBuilderNode: BuilderNode<[Params]> | null = null;
1589
1590@Builder
1591function buildText(params: Params) {
1592  Column() {
1593    Text('BuildNodeContentArea')
1594      .fontSize(25)
1595    CustomComp()
1596  }
1597}
1598
1599class TextNodeController extends NodeController {
1600  private rootNode: FrameNode | null = null;
1601  makeNode(context: UIContext): FrameNode | null {
1602    this.rootNode = new FrameNode(context);
1603    if (globalBuilderNode === null) {
1604      globalBuilderNode = new BuilderNode(context);
1605      globalBuilderNode.build(wrapBuilder<[Params]>(buildText), new Params('builder node text'), { localStorage: localStorage1 })
1606    }
1607    this.rootNode.appendChild(globalBuilderNode.getFrameNode());
1608    return this.rootNode;
1609  }
1610}
1611
1612let localStorage1: LocalStorage = new LocalStorage();
1613localStorage1.setOrCreate('PropA', 'PropA');
1614
1615@Entry(localStorage1)
1616@Component
1617struct Index {
1618  private controller: TextNodeController = new TextNodeController();
1619  @LocalStorageLink('PropA') PropA: string = 'Hello World';
1620  build() {
1621    Row() {
1622      Column() {
1623        Text(this.PropA)
1624        NodeContainer(this.controller)
1625        Button('changeLocalstorage').onClick(()=>{
1626          localStorage1.set('PropA','AfterChange')
1627        })
1628      }
1629    }
1630  }
1631}
1632@Component
1633struct CustomComp {
1634  @LocalStorageLink('PropA') PropA: string = 'Hello World';
1635  build() {
1636    Row() {
1637      Column() {
1638        Text(this.PropA)
1639      }
1640    }
1641  }
1642}
1643```
1644### 示例5(检验BuilderNode是否有效)
1645
1646该示例演示了BuilderNode释放节点前后分别使用isDisposed接口验证节点的状态,释放节点前节点调用isDisposed接口返回true,释放节点后节点调用isDisposed接口返回false。
1647
1648```ts
1649import { FrameNode, NodeController, BuilderNode } from '@kit.ArkUI';
1650
1651@Component
1652struct TestComponent {
1653  build() {
1654    Column() {
1655      Text('This is a BuilderNode.')
1656        .fontSize(25)
1657        .fontWeight(FontWeight.Bold)
1658    }
1659    .width('100%')
1660    .height(30)
1661    .backgroundColor(Color.Gray)
1662  }
1663
1664  aboutToAppear() {
1665    console.info('aboutToAppear');
1666  }
1667
1668  aboutToDisappear() {
1669    console.info('aboutToDisappear');
1670  }
1671}
1672
1673@Builder
1674function buildComponent() {
1675  TestComponent()
1676}
1677
1678class MyNodeController extends NodeController {
1679  private rootNode: FrameNode | null = null;
1680  private builderNode: BuilderNode<[]> | null = null;
1681
1682  makeNode(uiContext: UIContext): FrameNode | null {
1683    this.rootNode = new FrameNode(uiContext);
1684    this.builderNode = new BuilderNode(uiContext, { selfIdealSize: { width: 200, height: 100 } });
1685    this.builderNode.build(new WrappedBuilder(buildComponent));
1686
1687    const rootRenderNode = this.rootNode!.getRenderNode();
1688    if (rootRenderNode !== null) {
1689      rootRenderNode.size = { width: 300, height: 300 };
1690      rootRenderNode.backgroundColor = 0xffd5d5d5;
1691      rootRenderNode.appendChild(this.builderNode!.getFrameNode()!.getRenderNode());
1692    }
1693
1694    return this.rootNode;
1695  }
1696
1697  dispose() {
1698    if (this.builderNode !== null) {
1699      this.builderNode.dispose();
1700    }
1701  }
1702
1703  isDisposed() : string{
1704    if (this.builderNode !== null) {
1705      if (this.builderNode.isDisposed()) {
1706        return 'builderNode isDisposed is true';
1707      }
1708      else {
1709        return 'builderNode isDisposed is false';
1710      }
1711    }
1712    return 'builderNode is null';
1713  }
1714
1715  removeBuilderNode() {
1716    const rootRenderNode = this.rootNode!.getRenderNode();
1717    if (rootRenderNode !== null && this.builderNode !== null && this.builderNode.getFrameNode() !== null) {
1718      rootRenderNode.removeChild(this.builderNode!.getFrameNode()!.getRenderNode());
1719    }
1720  }
1721}
1722
1723@Entry
1724@Component
1725struct Index {
1726  @State text: string = ''
1727  private myNodeController: MyNodeController = new MyNodeController();
1728
1729  build() {
1730    Column({ space: 4 }) {
1731      NodeContainer(this.myNodeController)
1732      Button('BuilderNode dispose')
1733        .onClick(() => {
1734          this.myNodeController.removeBuilderNode();
1735          this.myNodeController.dispose();
1736          this.text = '';
1737        })
1738        .width(200)
1739        .height(50)
1740      Button('BuilderNode isDisposed')
1741        .onClick(() => {
1742          this.text = this.myNodeController.isDisposed();
1743        })
1744        .width(200)
1745        .height(50)
1746      Text(this.text)
1747        .fontSize(25)
1748    }
1749    .width('100%')
1750    .height('100%')
1751  }
1752}
1753```
1754
1755![isDisposed](figures/builderNode_isDisposed.gif)
1756
1757### 示例6(BuilderNode设置继承状态)
1758
1759该示例演示了BuilderNode设置继承状态为True,继承父自定义组件的冻结策略,在不活跃的时候进行冻结,切换为活跃状态解冻,更新缓存的数据。
1760
1761```ts
1762
1763import { BuilderNode, FrameNode, NodeController } from '@kit.ArkUI';
1764
1765class Params {
1766  count: number = 0;
1767
1768  constructor(count: number) {
1769    this.count = count;
1770  }
1771}
1772
1773@Builder // builder组件
1774function buildText(params: Params) {
1775
1776  Column() {
1777    TextBuilder({ message: params.count })
1778  }
1779}
1780
1781class TextNodeController extends NodeController {
1782  private rootNode: FrameNode | null = null;
1783  private textNode: BuilderNode<[Params]> | null = null;
1784  private count: number = 0;
1785
1786  makeNode(context: UIContext): FrameNode | null {
1787    this.rootNode = new FrameNode(context);
1788    this.textNode = new BuilderNode(context, { selfIdealSize: { width: 150, height: 150 } });
1789    this.textNode.build(wrapBuilder<[Params]>(buildText), new Params(this.count)); // 创建BuilderNode节点
1790    this.textNode.inheritFreezeOptions(true); // 设置BuilderNode的冻结继承状态为True
1791    if (this.rootNode !== null) {
1792      this.rootNode.appendChild(this.textNode.getFrameNode()); // 将BuilderNode上树
1793    }
1794    return this.rootNode;
1795  }
1796
1797  update(): void {
1798    if (this.textNode !== null) {
1799      this.count += 1;
1800      this.textNode.update(new Params(this.count)); // 更新BuilderNode中的数据,可以触发Log
1801    }
1802
1803  }
1804}
1805
1806const textNodeController: TextNodeController = new TextNodeController();
1807
1808@Entry
1809@Component
1810struct MyNavigationTestStack {
1811  @Provide('pageInfo') pageInfo: NavPathStack = new NavPathStack();
1812  @State message: number = 0;
1813  @State logNumber: number = 0;
1814
1815  @Builder
1816  PageMap(name: string) {
1817    if (name === 'pageOne') {
1818      pageOneStack({ message: this.message, logNumber: this.logNumber })
1819    } else if (name === 'pageTwo') {
1820      pageTwoStack({ message: this.message, logNumber: this.logNumber })
1821    }
1822  }
1823
1824  build() {
1825    Column() {
1826      Button('update builderNode') // 点击更新BuildrNode
1827        .onClick(() => {
1828          textNodeController.update();
1829        })
1830      Navigation(this.pageInfo) {
1831        Column() {
1832          Button('Next Page', { stateEffect: true, type: ButtonType.Capsule })
1833            .width('80%')
1834            .height(40)
1835            .margin(20)
1836            .onClick(() => {
1837              this.pageInfo.pushPath({ name: 'pageOne' }); // 将name指定的NavDestination页面信息入栈
1838            })
1839        }
1840      }.title('NavIndex')
1841      .navDestination(this.PageMap)
1842      .mode(NavigationMode.Stack)
1843    }
1844  }
1845}
1846
1847@Component
1848struct pageOneStack { // 页面一
1849  @Consume('pageInfo') pageInfo: NavPathStack;
1850  @State index: number = 1;
1851  @Link message: number;
1852  @Link logNumber: number;
1853
1854  build() {
1855    NavDestination() {
1856      Column() {
1857        NavigationContentMsgStack({ message: this.message, index: this.index, logNumber: this.logNumber })
1858        Button('Next Page', { stateEffect: true, type: ButtonType.Capsule }) // 切换至页面二
1859          .width('80%')
1860          .height(40)
1861          .margin(20)
1862          .onClick(() => {
1863            this.pageInfo.pushPathByName('pageTwo', null);
1864          })
1865        Button('Back Page', { stateEffect: true, type: ButtonType.Capsule }) // 返回主页面
1866          .width('80%')
1867          .height(40)
1868          .margin(20)
1869          .onClick(() => {
1870            this.pageInfo.pop();
1871          })
1872      }.width('100%').height('100%')
1873    }.title('pageOne')
1874    .onBackPressed(() => {
1875      this.pageInfo.pop();
1876      return true;
1877    })
1878  }
1879}
1880
1881@Component
1882struct pageTwoStack { // 页面二
1883  @Consume('pageInfo') pageInfo: NavPathStack;
1884  @State index: number = 2;
1885  @Link message: number;
1886  @Link logNumber: number;
1887
1888  build() {
1889    NavDestination() {
1890      Column() {
1891        NavigationContentMsgStack({ message: this.message, index: this.index, logNumber: this.logNumber })
1892        Text('BuilderNode处于冻结')
1893          .fontWeight(FontWeight.Bold)
1894          .margin({ top: 48, bottom: 48 })
1895        Button('Back Page', { stateEffect: true, type: ButtonType.Capsule }) // 返回至页面一
1896          .width('80%')
1897          .height(40)
1898          .margin(20)
1899          .onClick(() => {
1900            this.pageInfo.pop();
1901          })
1902      }.width('100%').height('100%')
1903    }.title('pageTwo')
1904    .onBackPressed(() => {
1905      this.pageInfo.pop();
1906      return true;
1907    })
1908  }
1909}
1910
1911@Component({ freezeWhenInactive: true }) // 设置冻结策略为不活跃冻结
1912struct NavigationContentMsgStack {
1913  @Link message: number;
1914  @Link index: number;
1915  @Link logNumber: number;
1916
1917  build() {
1918    Column() {
1919      if (this.index === 1) {
1920        NodeContainer(textNodeController)
1921      }
1922    }
1923  }
1924}
1925
1926@Component({ freezeWhenInactive: true }) // 设置冻结策略为不活跃冻结
1927struct TextBuilder {
1928  @Prop @Watch("info") message: number = 0;
1929  @State count : number = 0;
1930
1931  info() {
1932    this.count++;
1933    console.info(`freeze-test TextBuilder message callback change time ${this.count}`); // 根据message内容变化来打印日志来判断是否冻结
1934    console.info(`freeze-test TextBuilder message callback change massage ${this.message}`); // 根据message内容变化来打印日志来判断是否冻结
1935  }
1936
1937  build() {
1938    Row() {
1939      Column() {
1940        Text(`文本更新内容: ${this.message}`)
1941          .fontWeight(FontWeight.Bold)
1942          .margin({ top: 48, bottom: 48 })
1943        Text(`文本更新次数: ${this.count}`)
1944          .fontWeight(FontWeight.Bold)
1945          .margin({ top: 48, bottom: 48 })
1946      }
1947    }
1948  }
1949}
1950```
1951
1952![inheritFreezeOptions](figures/builderNode_inheritFreezeOptions.gif)
1953
1954### 示例7(BuilderNode支持内部@Consume接收外部的@Provide数据)
1955
1956设置BuilderNode的BuildOptions中enableProvideConsumeCrossing为true,以实现BuilderNode内部自定义组件的@Consume与所在自定义组件的@Provide数据互通。
1957
1958```ts
1959import { BuilderNode, NodeContent } from '@kit.ArkUI';
1960
1961@Component
1962struct ConsumeChild {
1963  @Consume @Watch("ChangeData") message: string = ""
1964
1965  ChangeData() {
1966    console.info(`ChangeData ${this.message}`);
1967  }
1968
1969  build() {
1970    Column() {
1971      Text(this.message)
1972        .fontWeight(FontWeight.Bold)
1973        .fontSize(20)
1974      Button("Click to change message to append C")
1975        .fontWeight(FontWeight.Bold)
1976        .onClick(() => {
1977          // 修改Consume的变量
1978          this.message = this.message + "C"
1979        })
1980    }
1981  }
1982}
1983
1984@Builder
1985function CreateText(textMessage: string) {
1986  Column() {
1987    Text(textMessage)
1988      .fontWeight(FontWeight.Bold)
1989      .fontSize(20)
1990    ConsumeChild()
1991  }
1992}
1993
1994@Entry
1995@Component
1996struct Index {
1997  @Provide message: string = 'Hello World';
1998  private content: NodeContent = new NodeContent();
1999  private builderNode: BuilderNode<[string]> = new BuilderNode<[string]>(this.getUIContext());
2000
2001  aboutToAppear(): void {
2002    // 设置enableProvideConsumeCrossing为true,支持BuilderNode内部自定义组件ConsumeChild的@Consume与其所在页面中的@Provide数据互通
2003    this.builderNode.build(wrapBuilder(CreateText), "Test Consume", { enableProvideConsumeCrossing: true })
2004    this.content.addFrameNode(this.builderNode.getFrameNode())
2005  }
2006
2007  build() {
2008    Column() {
2009      Text(this.message)
2010        .fontWeight(FontWeight.Bold)
2011        .fontSize(20)
2012      Button("Click to change message to append I")
2013        .fontWeight(FontWeight.Bold)
2014        .onClick(() => {
2015          this.message = this.message + "I";
2016        })
2017      Column() {
2018        ContentSlot(this.content)
2019      }
2020    }
2021    .height('100%')
2022    .width('100%')
2023  }
2024}
2025```
2026
2027![enableProvideConsumeCrossing](figures/BuilderNode_Consume.gif)
2028