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