概述
对于具备自己前端定义的三方框架,需要将特定的dsl转换成为ArkUI的声明式描述。这个转换过程需依赖额外的数据驱动绑定至 Builder 中,转换比较复杂且性能较低。这一类框架一般依赖系统ArkUI框架的布局、事件能力,以及最基础的节点操作和自定义能力,大部分组件通过自定义完成,但是需要使用部分原生组件混合显示。 FrameNode 的设计就是为了解决上述的问题。
FrameNode表示组件树的实体节点,配合自定义占位容器组件 NodeContainer 等,在占位容器内挂载一颗自定义的节点树,并对这个节点树中的节点进行动态的增加、修改、删除等操作。基础的FrameNode可以设置通用属性、设置事件回调,并提供完整的自定义能力,包括自定义测量、布局以及绘制。
除此之外,ArkUI框架还提供获取和遍历获得原生组件对应的代理FrameNode对象的能力,下文简称代理节点。代理节点可以用于需要遍历整个UI的树形结构,并支持获取原生组件节点的具体信息或者额外注册组件的事件监听回调。
创建和删除节点
FrameNode提供了节点创建和删除的能力。可以通过FrameNode的构造函数创建自定义FrameNode节点,通过构造函数创建的节点对应一个实体的节点。同时,可以通过FrameNode中的 dispose 接口来实现与实体节点的绑定关系的解除。
说明
-
在创建FrameNode对象的时候需要传入必选参数UIContext,若未传入UIContext对象或者传入不合法,则节点创建抛出异常。
-
过自定义占位组件将节点进行显示的时候需要保证UI上下文一致,否则会出现显示异常。
-
若不持有FrameNode对象,则该对象会在GC的时候被回收。
判断节点是否可修改
isModifiable 用于查询当前节点类型是否为原生组件的代理节点。当FrameNode节点作为原生组件的代理节点的时候,该节点不可修改。即无法修改代理节点的自身属性以及其子节点的结构。
获取对应的RenderNode节点
FrameNode提供了 getRenderNode 接口,用于获取FrameNode中的RenderNode。可以通过对获取到的RenderNode对象进行操作,动态修改FrameNode上绘制相关的属性,具体可修改的属性参考RenderNode的接口。
说明
- 无法获取原生组件代理FrameNode的RenderNode对象。
- BuilderNode中调用 getFrameNode 获取得到的FrameNode节点对象中,可以通过getRenderNode获取对应的根节点的RenderNode对象。
操作节点树
FrameNode提供了节点的增、删、查、改的能力,能够修改非代理节点的子树结构;可以对所有FrameNode的节点的父子节点做出查询操作,并返回查询结果。
说明
对节点进行增、删、改操作的时候,会对非法操作抛出异常信息。
通过查询获得的原生组件的代理节点,仅具备查询节点信息的作用,不具备修改节点属性的功能;代理节点不持有组件的实体节点,即不影响对应的节点的生命周期。
查询节点仅查询获得UI相关的节点,不返回语法节点。
使用自定义组件的场景下,可能查询获得自定义组件的新增节点,节点类型为“Common”。
import { BuilderNode, FrameNode, NodeController, UIContext } from '@kit.ArkUI';
import { BusinessError } from '@kit.BasicServicesKit';
const TEST_TAG: string = "FrameNode"
class Params {
text: string = "this is a text"
}
@Builder
function buttonBuilder(params: Params) {
Column({ space: 10 }) {
Button(params.text)
.fontSize(12)
.borderRadius(8)
.borderWidth(2)
.backgroundColor(Color.Orange)
Button(params.text)
.fontSize(12)
.borderRadius(8)
.borderWidth(2)
.backgroundColor(Color.Pink)
}
}
class MyNodeController extends NodeController {
public buttonNode: BuilderNode<[Params]> | null = null;
public frameNode: FrameNode | null = null;
public childList: Array<FrameNode> = new Array<FrameNode>();
public rootNode: FrameNode | null = null;
private uiContext: UIContext | null = null;
private wrapBuilder: WrappedBuilder<[Params]> = wrapBuilder(buttonBuilder);
makeNode(uiContext: UIContext): FrameNode | null {
this.uiContext = uiContext;
if (this.rootNode == null) {
this.rootNode = new FrameNode(uiContext);
this.rootNode.commonAttribute
.width("50%")
.height(100)
.borderWidth(1)
.backgroundColor(Color.Gray)
}
if (this.frameNode == null) {
this.frameNode = new FrameNode(uiContext);
this.frameNode.commonAttribute
.width("100%")
.height(50)
.borderWidth(1)
.position({ x: 200, y: 0 })
.backgroundColor(Color.Pink);
this.rootNode.appendChild(this.frameNode);
}
if (this.buttonNode == null) {
this.buttonNode = new BuilderNode<[Params]>(uiContext);
this.buttonNode.build(this.wrapBuilder, { text: "This is a Button" })
this.rootNode.appendChild(this.buttonNode.getFrameNode())
}
return this.rootNode;
}
operationFrameNodeWithFrameNode(frameNode: FrameNode | undefined | null) {
if (frameNode) {
console.log(TEST_TAG + " get ArkTSNode success.")
console.log(TEST_TAG + " check rootNode whether is modifiable " + frameNode.isModifiable());
}
if (this.uiContext) {
let frameNode1 = new FrameNode(this.uiContext);
let frameNode2 = new FrameNode(this.uiContext);
frameNode1.commonAttribute.size({ width: 150, height: 150 })
.backgroundColor(Color.Black)
.position({ x: 50, y: 0 })
frameNode2.commonAttribute.size({ width: 150, height: 150 })
.backgroundColor(Co