• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 自定义占位节点
2
3ArkUI提供了ArkTS原生组件作为自定义节点的占位节点。该占位节点具备组件的通用属性。
4
5## NodeContainer和NodeController
6
7[NodeContainer](../reference/apis-arkui/arkui-ts/ts-basic-components-nodecontainer.md)作为原生组件,仅具备组件的通用属性,其节点规格参考默认左上角对齐的[Stack](../reference/apis-arkui/arkui-ts/ts-container-stack.md)组件。[NodeContainer](../reference/apis-arkui/arkui-ts/ts-basic-components-nodecontainer.md)作为一个占位容器组件,主要是用于自定义节点以及自定义节点树的显示和复用。
8
9[NodeController](../reference/apis-arkui/js-apis-arkui-nodeController.md)提供了一系列生命周期回调,通过[makeNode](../reference/apis-arkui/js-apis-arkui-nodeController.md#makenode)回调返回一个 [FrameNode](../reference/apis-arkui/js-apis-arkui-frameNode.md#framenode) 节点树的根节点。将[FrameNode](../reference/apis-arkui/js-apis-arkui-frameNode.md)节点树挂载到对应的[NodeContainer](../reference/apis-arkui/arkui-ts/ts-basic-components-nodecontainer.md)下。同时提供了[aboutToAppear](../reference/apis-arkui/arkui-ts/ts-custom-component-lifecycle.md#abouttoappear)、[aboutToDisappear](../reference/apis-arkui/arkui-ts/ts-custom-component-lifecycle.md#abouttodisappear)、[aboutToResize](../reference/apis-arkui/js-apis-arkui-nodeController.md#abouttoresize)、[onTouchEvent](../reference/apis-arkui/js-apis-arkui-nodeController.md#ontouchevent)、[rebuild](../reference/apis-arkui/js-apis-arkui-nodeController.md#rebuild)五个回调方法用于监听对应的[NodeContainer](../reference/apis-arkui/arkui-ts/ts-basic-components-nodecontainer.md)的状态。
10
11每个生命周期的回调的具体含义参考[NodeController](../reference/apis-arkui/js-apis-arkui-nodeController.md)的接口文档说明。
12
13> **说明:**
14>
15> - NodeContainer下仅支持挂载自定义的FrameNode节点以及BuilderNode创建的组件树的根节点。
16>
17> - 从API Version 12开始支持的接口,可以通过FrameNode的查询接口返回原生组件的代理节点,代理节点可以作为makeNode的返回值进行返回,但代理节点无法成功挂载在组件树上,最终的显示结果为代理节点挂载失败。
18>
19> - 需要保证一个节点只能作为一个父节点的子节点去使用,否则可能存在显示异常或者功能异常,尤其是页面路由场景或者动效场景。例如,如果通过NodeController将同一个节点挂载在多个NodeContainer上,仅一个占位容器下会显示节点,且多个NodeContainer的可见性、透明度等影响子组件状态的属性更新均会影响被挂载的子节点。
20
21```ts
22// common.ets
23import { BuilderNode, UIContext } from '@kit.ArkUI'
24
25class Params {
26  text: string = "this is a text"
27}
28
29let buttonNode: BuilderNode<[Params]> | null = null;
30
31@Builder
32function buttonBuilder(params: Params) {
33  Column() {
34    Button(params.text)
35      .fontSize(12)
36      .borderRadius(8)
37      .borderWidth(2)
38      .backgroundColor(Color.Orange)
39  }
40}
41
42export function createNode(uiContext: UIContext) {
43  buttonNode = new BuilderNode<[Params]>(uiContext);
44  buttonNode.build(wrapBuilder(buttonBuilder), { text: "This is a Button" });
45  return buttonNode;
46}
47
48export function getOrCreateNode(uiContext: UIContext): BuilderNode<[Params]> | null {
49  if (buttonNode?.getFrameNode() && buttonNode?.getFrameNode()?.getUniqueId() != -1) {
50    return buttonNode;
51  } else {
52    return createNode(uiContext);
53  }
54}
55```
56```ts
57// Index.ets
58import { FrameNode, NodeController, Size, UIContext } from '@kit.ArkUI'
59import { getOrCreateNode } from "./common"
60
61class MyNodeController extends NodeController {
62  private isShow: boolean = false;
63
64  constructor(isShow: boolean) {
65    super();
66    this.isShow = isShow;
67  }
68
69  makeNode(uiContext: UIContext): FrameNode | null {
70    if (!this.isShow) {
71      return null;
72    }
73    let frameNode = getOrCreateNode(uiContext)?.getFrameNode();
74    return frameNode ? frameNode : null;
75  }
76
77  aboutToResize(size: Size) {
78    console.log("aboutToResize width : " + size.width + " height : " + size.height)
79  }
80
81  aboutToAppear() {
82    console.log("aboutToAppear")
83  }
84
85  aboutToDisappear() {
86    console.log("aboutToDisappear");
87  }
88
89  onTouchEvent(event: TouchEvent) {
90    console.log("onTouchEvent");
91  }
92
93  toShow() {
94    this.isShow = true;
95    this.rebuild();
96  }
97
98  toHide() {
99    this.isShow = false;
100    this.rebuild();
101  }
102}
103
104@Entry
105@Component
106struct Index {
107  private myNodeController1: MyNodeController = new MyNodeController(true);
108  private myNodeController2: MyNodeController = new MyNodeController(false);
109
110  build() {
111    Column() {
112      NodeContainer(this.myNodeController1)
113        .width("100%")
114        .height("40%")
115        .backgroundColor(Color.Brown)
116      NodeContainer(this.myNodeController2)
117        .width("100%")
118        .height("40%")
119        .backgroundColor(Color.Gray)
120      Button("Change the place of button")
121        .onClick(() => {
122          // 先在原始占位节点中下树
123          // 后在新的占位节点中上树
124          // 保证自定义节点仅作为一个节点的子节点存在
125          this.myNodeController1.toHide();
126          this.myNodeController2.toShow();
127        })
128    }
129    .padding({ left: 35, right: 35, top: 35 })
130    .width("100%")
131    .height("100%")
132  }
133}
134```
135