BuilderNode

The BuilderNode module provides APIs for creating a BuilderNode – a custom node that can be used to mount native components. A BuilderNode can be used only as a leaf node. Whenever possible, avoid operating child nodes and their attributes under the root node through the root node's render node.

NOTE

The initial APIs of this module are supported since API version 11. Newly added APIs will be marked with a superscript to indicate their earliest API version.

BuilderNode is not available in DevEco Studio Previewer.

Modules to Import

import { BuilderNode, RenderOptions, NodeRenderType } from "@ohos.arkui.node";

NodeRenderType

Enumerates the node rendering types.

System capability: SystemCapability.ArkUI.ArkUI.Full

Name Value Description
RENDER_TYPE_DISPLAY 0 The node is displayed on the screen.
RENDER_TYPE_TEXTURE 1 The node is exported as a texture.

RenderOptions

Provides optional parameters for creating a BuilderNode.

System capability: SystemCapability.ArkUI.ArkUI.Full

Name Type Mandatory Description
selfIdealSize Size No Ideal size of the node.
type NodeRenderType No Rendering type of the node.
surfaceId string No Surface ID of the texture receiver. Generally, the texture receiver is an OH_NativeImage instance.

BuilderNode

class BuilderNode<Args extends Object[]>

Implements a BuilderNode, which can create a component tree through the stateless UI method @Builder and hold the root node of the component tree. A BuilderNode cannot be defined as a state variable. The FrameNode held in the BuilderNode is only used to mount the BuilderNode to other FrameNodes as a child node. Undefined behavior may occur if you set attributes or perform operations on subnodes of the FrameNode held by the BuilderNode. Therefore, after you have obtained a RenderNode through the getFrameNode method of the BuilderNode and the getRenderNode method of the FrameNode, avoid setting the attributes or operating the subnodes through APIs of the RenderNode.

System capability: SystemCapability.ArkUI.ArkUI.Full

constructor

constructor(uiContext: UIContext, options?: RenderOptions)

Constructor for creating a BuilderNode. When the content generated by the BuilderNode is embedded in another RenderNode for display, that is, the RenderNode corresponding to the BuilderNode is mounted to another RenderNode for display, selfIdealSize in RenderOptions must be explicitly specified. If selfIdealSize is not set, the node in the builder follows the default parent component layout constraint [0, 0], which means that the size of the root node of the subtree in BuilderNode is [0, 0].

System capability: SystemCapability.ArkUI.ArkUI.Full

Name Type Mandatory Description
uiContext UIContext Yes UI context. For details about how to obtain it, see [Obtaining UI Context.
options RenderOptions No Parameters for creating a BuilderNode.

build

build(builder: WrappedBuilder<Args>, arg?: Object): void

Creates a component tree based on the passed object and holds the root node of the component tree. The stateless UI method @Builder has at most one root node. Custom components are allowed. Yet, the custom components cannot use decorators, such as @Reusable, @Link, @Prop, @Provide, and @Consume, for state synchronization with the owning page.

System capability: SystemCapability.ArkUI.ArkUI.Full

Parameters

Name Type Mandatory Description
builder WrappedBuilder<Args> Yes Stateless UI method @Builder required for creating a component tree.
arg Object No Object, which is used as the input parameter of the builder.

getFrameNode

getFrameNode(): FrameNode | null

Obtains the FrameNode in the BuilderNode. The FrameNode is generated only after the BuilderNode executes the build operation.

System capability: SystemCapability.ArkUI.ArkUI.Full

Return value

Type Description
FrameNode | null FrameNode object. If no such object is held by the BuilderNode instance, null is returned.

Example 1

In this example, the BuilderNode is returned as the root node of the <NodeContainer>.

import { NodeController, BuilderNode, FrameNode } from "@ohos.arkui.node"
import { UIContext } from '@ohos.arkui.UIContext';

class Params {
  text: string = ""
  constructor(text: string) {
    this.text = text;
  }
}

@Builder
function buildText(params: Params) {
  Column() {
    Text(params.text)
      .fontSize(50)
      .fontWeight(FontWeight.Bold)
      .margin({bottom: 36})
  }
}

class TextNodeController extends NodeController {
  private textNode: BuilderNode<[Params]> | null = null;
  private message: string = "DEFAULT";

  constructor(message: string) {
    super();
    this.message = message;
  }

  makeNode(context: UIContext): FrameNode | null {
    this.textNode = new BuilderNode(context);
    this.textNode.build(wrapBuilder<[Params]>(buildText), new Params(this.message))

    return this.textNode.getFrameNode();
  }
}

@Entry
@Component
struct Index {
  @State message: string = "hello"

  build() {
    Row() {
      Column() {
        NodeContainer(new TextNodeController(this.message))
          .width('100%')
          .height(100)
          .backgroundColor('#FFF0F0F0')
      }
      .width('100%')
      .height('100%')
    }
    .height('100%')
  }
}

Example 2

This example mounts the RenderNode of the BuilderNode to another RenderNode.

import { NodeController, BuilderNode, FrameNode } from "@ohos.arkui.node"
import { UIContext } from '@ohos.arkui.UIContext';

class Params {
  text: string = ""

  constructor(text: string) {
    this.text = text;
  }
}

@Builder
function buildText(params: Params) {
  Column() {
    Text(params.text)
      .fontSize(50)
      .fontWeight(FontWeight.Bold)
      .margin({ bottom: 36 })
  }
}

class TextNodeController extends NodeController {
  private rootNode: FrameNode | null = null;
  private textNode: BuilderNode<[Params]> | null = null;
  private message: string = "DEFAULT";

  constructor(message: string) {
    super();
    this.message = message;
  }

  makeNode(context: UIContext): FrameNode | null {
    this.rootNode = new FrameNode(context);

    this.textNode = new BuilderNode(context, { selfIdealSize: { width: 150, height: 150 } });
    this.textNode.build(wrapBuilder<[Params]>(buildText), new Params(this.message));
    const textRenderNode = this.textNode?.getFrameNode()?.getRenderNode();

    const rootRenderNode = this.rootNode.getRenderNode();
    if (rootRenderNode !== null) {
      rootRenderNode.appendChild(textRenderNode);
    }

    return this.rootNode;
  }
}

@Entry
@Component
struct Index {
  @State message: string = "hello"

  build() {
    Row() {
      Column() {
        NodeContainer(new TextNodeController(this.message))
          .width('100%')
          .height(100)
          .backgroundColor('#FFF0F0F0')
      }
      .width('100%')
      .height('100%')
    }
    .height('100%')
  }
}

update

update(arg: Object): void

Updates this BuilderNode based on the provided parameter, which is of the same type as the input parameter passed to the build API. To call this API on a custom component, the variable used in the component must be defined as the @Prop type.

System capability: SystemCapability.ArkUI.ArkUI.Full

Parameters

Name Type Mandatory Description
arg Object Yes Parameter used to update the BuilderNode. It is of the same type as the parameter passed to the build API.

Example

import { UIContext } from '@ohos.arkui.UIContext';
import { NodeController, BuilderNode, FrameNode } from "@ohos.arkui.node"

class Params {
  text: string = ""
  constructor(text: string) {
    this.text = text;
  }
}

// Custom component
@Component
struct TextBuilder {
  @Prop message: string = "TextBuilder";

  build() {
    Row() {
      Column() {
        Text(this.message)
          .fontSize(50)
          .fontWeight(FontWeight.Bold)
          .margin({bottom: 36})
          .backgroundColor(Color.Gray)
      }
    }
  }
}

@Builder
function buildText(params: Params) {
  Column() {
    Text(params.text)
      .fontSize(50)
      .fontWeight(FontWeight.Bold)
      .margin({ bottom: 36 })
    TextBuilder({message: params.text}) //Custom component
  }
}

class TextNodeController extends NodeController {
  private rootNode: FrameNode | null = null;
  private textNode: BuilderNode<[Params]> | null = null;
  private message: string = "";

  constructor(message: string) {
    super()
    this.message = message
  }

  makeNode(context: UIContext): FrameNode | null {
    this.textNode = new BuilderNode(context);
    this.textNode.build(wrapBuilder<[Params]>(buildText), new Params(this.message))
    return this.textNode.getFrameNode();
  }

  update(message: string) {
    if (this.textNode !== null) {
      this.textNode.update(new Params(message));
    }
  }
}

@Entry
@Component
struct Index {
  @State message: string = "hello"
  private textNodeController: TextNodeController = new TextNodeController(this.message);
  private count = 0;

  build() {
    Row() {
      Column() {
        NodeContainer(this.textNodeController)
          .width('100%')
          .height(100)
          .backgroundColor('#FFF0F0F0')
        Button('Update')
          .onClick(() => {
            this.count += 1;
            const message = "Update " + this.count.toString();
            this.textNodeController.update(message);
          })
      }
      .width('100%')
      .height('100%')
    }
    .height('100%')
  }
}

postTouchEvent

postTouchEvent(event: TouchEvent): boolean

Dispatches an event to the FrameNode created by this BuilderNode.

System capability: SystemCapability.ArkUI.ArkUI.Full

Parameters

Name Type Mandatory Description
event TouchEvent Yes Touch event.

Return value

Type Description
boolean Whether the event is successfully dispatched.

Example

import { UIContext } from '@ohos.arkui.UIContext';
import { NodeController, BuilderNode, FrameNode } from '@ohos.arkui.node';

class Params {
  text: string = "this is a text"
}

@Builder
function ButtonBuilder(params: Params) {
  Column() {
    Button(`button ` + params.text)
      .borderWidth(2)
      .backgroundColor(Color.Orange)
      .width("100%")
      .height("100%")
      .gesture(
        TapGesture()
          .onAction((event: GestureEvent) => {
            console.log("TapGesture");
          })
      )
  }
  .width(500)
  .height(300)
  .backgroundColor(Color.Gray)
}

class MyNodeController extends NodeController {
  private rootNode: BuilderNode<[Params]> | null = null;
  private wrapBuilder: WrappedBuilder<[Params]> = wrapBuilder(ButtonBuilder);

  makeNode(uiContext: UIContext): FrameNode | null {
    this.rootNode = new BuilderNode(uiContext);
    this.rootNode.build(this.wrapBuilder, { text: "this is a string" })
    return this.rootNode.getFrameNode();
  }

  postTouchEvent(touchEvent: TouchEvent): void {
    if(this.rootNode == null){
      return;
    }
    let result = this.rootNode.postTouchEvent(touchEvent);
    console.log("result " + result);
  }
}

@Entry
@Component
struct MyComponent {
  private nodeController: MyNodeController = new MyNodeController();

  build() {
    Column() {
      NodeContainer(this.nodeController)
        .height(300)
        .width(500)

      Column()
        .width(500)
        .height(300)
        .backgroundColor(Color.Pink)
        .onTouch((event) => {
          if(event != undefined){
            this.nodeController.postTouchEvent(event);
          }
        })
    }
  }
}