【HarmonyOS开发小实践】ArkUI 底层自定义与 NDK:FrameNode、RenderNode 与 C++ 构建 UI
ArkUI 底层自定义与 NDK:FrameNode、RenderNode 与 C++ 构建 UI
什么时候需要底层自定义
前面讲的 @Component、@Builder、Modifier 都是声明式层面的复用。但有些场景声明式够不着:
- 需要精确控制单个组件节点的增删改查
- 需要和底层渲染管线交互,做特殊绘制
- 已有 C/C++ 图形库,想在鸿蒙里直接用
- 对性能有极高要求,需要绕开声明式框架的开销
这些场景要往下走:自定义节点(FrameNode/RenderNode/BuilderNode)、自定义渲染,以及 NDK 构建 UI。
自定义节点三兄弟
ArkUI 提供了三个节点 API,从不同角度操作底层:
| API | 定位 | 用途 |
|---|---|---|
FrameNode | 声明式组件的底层节点 | 在声明式外获取、操作组件节点,实现动态增删 |
RenderNode | 渲染节点 | 自定义绘制,绕过组件直接操作渲染属性 |
BuilderNode | 构建节点 | 把 Builder 内容挂到任意位置,动态插入 UI |
FrameNode:动态操作组件节点
FrameNode 让你在声明式结构之外,获取并操控组件节点:
import { FrameNode, NodeController } from '@kit.ArkUI';
// 一个控制节点增删的控制器
class MyNodeController extends NodeController {
makeNode(uiContext: UIContext): FrameNode | null {
// 创建 FrameNode,挂载一个 Text 组件
let node = new FrameNode(uiContext);
let textNode = new FrameNode(uiContext);
textNode.initialize('Text'); // 初始化为 Text 组件
// 设置组件属性
textNode.attribute.text('动态创建的文本');
node.appendChild(textNode);
return node;
}
}
@Entry
@Component
struct FrameNodeDemo {
private controller: MyNodeController = new MyNodeController();
build() {
// 用 NodeContainer 承载动态节点
NodeContainer(this.controller)
.width(200)
.height(60)
}
}
FrameNode 的核心价值是命令式地创建和操作组件,在声明式框架之外做更精细的控制。典型场景:动态创建组件树、精确控制某个节点的层级和属性。
RenderNode:自定义绘制
RenderNode 绕开组件,直接操作渲染属性,做自定义绘制:
import { RenderNode } from '@kit.ArkUI';
@Entry
@Component
struct RenderNodeDemo {
private rootNode: RenderNode = new RenderNode();
aboutToAppear() {
let renderNode = this.rootNode;
renderNode.frame = { x: 0, y: 0, width: 200, height: 200 };
renderNode.backgroundColor = 0xFFFF6B35;
// 渲染命令:绘制一个圆形
renderNode.drawCircle({
x: 100, y: 100, radius: 80, fill: 0xFF4A90E2
});
}
build() {
// RenderNode 挂载到组件上显示
Stack() {}
.width('100%')
.height('100%')
.onAreaChange(() => {})
}
}
RenderNode 直接写渲染属性(frame、backgroundColor)和绘制命令(drawCircle 等),绕过了组件层。适合需要自定义绘制、性能敏感的场景,但门槛也更高——你需要理解渲染管线。
BuilderNode:动态插入 UI
BuilderNode 把一段 Builder 构建的 UI 动态挂到任意位置:
import { BuilderNode } from '@kit.ArkUI';
@Entry
@Component
struct BuilderNodeDemo {
build() {
Column() {
Text('BuilderNode 会在下方插入内容')
}
}
}
// 动态创建 BuilderNode 并插入某种 UI
function insertDynamicContent(uiContext: UIContext) {
let builderNode = new BuilderNode(uiContext);
builderNode.build((ui: UIContext) => {
// 构建要插入的 UI
Row() {
Text('动态插入的内容')
}
});
}
BuilderNode 结合了 Builder 的声明式写法和节点的动态挂载能力,比 FrameNode 更贴近声明式习惯。
XComponent:画布与自定义渲染
XComponent 提供自定义渲染的画布,可以对接 C/C++ 或 OpenGL:
@Entry
@Component
struct XComponentDemo {
build() {
XComponent({ id: 'canvas', type: XComponentType.SURFACE })
.onLoad((xcomponent) => {
// 拿到 XComponent 的 surface,对接渲染
let surfaceId = xcomponent.surfaceId;
// 后续通过 native 接口在 surfaceId 上绘制
})
.width('100%')
.height(300)
}
}
XComponent 是自定义渲染的入口,拿到 surface 后可以用 native 代码(C/C++)绘制,适合游戏、图形应用、视频渲染等自绘场景。
NDK 构建 UI
NDK 构建 UI 是更彻底的底层方案——用 C/C++ 代码直接创建组件、操作 UI 树:
#include "native_capi.h"
// C++ 侧创建并挂载 UI 组件
void BuildNativeUI(OH_NativeXComponent* component) {
// 创建 ArkUI 节点树
ArkUI_NodeHandle root = OH_ArkUI_Node_Create(...);
// 创建文本组件节点
ArkUI_NodeHandle textNode = OH_ArkUI_Node_Create(...);
OH_ArkUI_Node_SetAttribute(textNode, ARKUI_NODE_ATTRIBUTE_CONTENT, "Native 文本");
OH_ArkUI_Node_SetAttribute(textNode, ARKUI_NODE_ATTRIBUTE_FONT_SIZE, 24);
// 挂载到根节点
OH_ArkUI_Node_AppendChild(root, textNode);
// 注册到组件
OH_ArkUI_Node_RegisterToComponent(component, root);
}
NDK 构建 UI 的适用场景:
- 业务已有 C/C++ 代码库,想复用
- 需要精细化控制组件创建和挂载
- 对性能有极致要求
设备上的能力差异
底层自定义能力在模拟器和真机上有差异,涉及这些的能力模拟器可能不支持:
- Image 的
enableAnalyzer(图像 AI 分析) @Preview装饰器- EmbeddedComponent 嵌入组件
toolbar属性restoreId属性
做底层自定义时,要在真机上验证,别在模拟器上白调试。
底层自定义的选型小建议哦
需求 → 选型
────────────────────────────────────
复用结构 + 状态 → @Component
复用布局片段 → @Builder
复用样式属性 → Modifier
动态创建/操作组件节点 → FrameNode
自定义绘制、绕开组件 → RenderNode
动态插入声明式 UI → BuilderNode
自绘图形、游戏、视频 → XComponent
已有 C/C++ 库、极致性能 → NDK 构建 UI
从 @Component 到 NDK,抽象层级越来越低、控制力越来越强、门槛也越来越高。大部分业务停在 @Component + Modifier 就够了。
总结一波
-
别为了"高级"而底层。底层自定义门槛高、调试难、维护成本大,只有声明式确实解决不了时才往下走。能声明式解决的别碰 FrameNode/RenderNode。
-
FrameNode 用于"声明式不够灵活"的场景。需要运行时动态增删组件、精确控制层级时,FrameNode 比声明式更直接。
-
RenderNode 用于自绘和性能敏感场景。理解渲染管线再动 RenderNode,否则容易画出问题。
-
NDK 用于复用 C/C++ 库。如果单纯为了性能,先优化声明式代码,NDK 是最后的手段。
-
底层能力真机验证。涉及底层节点、渲染、嵌入组件的功能,模拟器可能不支持,务必真机测试。
更多推荐
所有评论(0)