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 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 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/windowY、displayX/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 1380 1381### 示例2(触摸事件) 1382 1383该示例演示了在自定义组件中截获触摸事件并对触点坐标进行转换的完整流程。在onTouch回调中,遍历TouchEvent的changedTouches和touches数组,对每个触点的x/y加上组件偏移量并调用vp2px转换为像素,更新各自的windowX/windowY、displayX/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 1484 1485### 示例3(轴事件) 1486 1487该示例演示了在自定义组件中截获滚轮或触控板轴事件并进行坐标转换的完整流程。在onAxisEvent回调中,先获取事件的相对x/y,再加上组件偏移量后调用vp2px转换为像素,更新AxisEvent的windowX/windowY、displayX/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 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 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 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 2028