鸿蒙中 XComponent 组件
本文同步发表于我的微信公众号,微信搜索 程语新视界 即可关注,每个工作日都有文章更新
一、介绍
XComponent 是鸿蒙中一个非常强大的组件,主要为图形绘制和媒体数据写入提供了 Surface 能力。允许将一个 Surface(可以理解为一块可供绘制的画布)嵌入到应用视图中:
-
高性能绘制:它可以直接与底层图形系统交互,非常适合用于 EGL/OpenGL ES 渲染、视频播放、相机预览等需要高性能图形处理的场景。
-
灵活的类型:XComponent 支持多种类型,以适应不同的需求:
类型 描述 SURFACE绘制的内容单独送显,直接合成到屏幕,性能最佳。 COMPONENTXComponent 作为一个容器组件,可以在其中执行非UI逻辑或动态加载显示内容。 TEXTURE绘制内容会与 XComponent 组件的内容合成后展示到屏幕上。
二、如何使用
在 ArkTS 中,可以通过几种不同的接口来创建 XComponent 组件。
| 接口版本/特性 | 适用场景 | 关键配置 |
|---|---|---|
XComponent(options: XComponentOptions) (推荐) |
API 12+,功能最全面,支持 AI 分析。 | 通过 XComponentOptions 配置类型、控制器及 AI 选项。 |
XComponent(params: NativeXComponentParameters) |
API 19+,需将组件节点传递至 Native (C++) 侧进行高级操作时使用。 | 通过 NativeXComponentParameters 配置。 |
| 传统接口 (已废弃或不推荐) | 早期版本,不推荐在新项目中使用。 | 通过 id, type, libraryname 等参数配置。 |
1、基本创建示例
// 引入XComponentController
import { XComponentController } from '@kit.ArkUI';
@Entry
@Component
struct GraphicsCanvas {
// 实例化组件控制器
private xComponentCtrl: XComponentController = new XComponentController();
// 用于存储Surface ID
private surfaceId: string = '';
build() {
Column() {
// 使用XComponent组件
XComponent({
id: 'my_gl_surface',
type: XComponentType.SURFACE, // 指定为SURFACE类型
controller: this.xComponentCtrl // 绑定控制器
})
.onLoad(() => {
// 组件加载完成时,获取Surface ID并设置尺寸
this.surfaceId = this.xComponentCtrl.getXComponentSurfaceId();
console.info(`XComponent Surface ID: ${this.surfaceId}`);
// 在需要时设置Surface大小
// this.xComponentCtrl.setXComponentSurfaceSize({surfaceWidth: 1920, surfaceHeight: 1080});
})
.onDestroy(() => {
// 组件销毁时,可进行资源清理
console.info('XComponent is being destroyed.');
})
.width('100%')
.height('300px')
.backgroundColor(Color.Black) // 设置背景色
}
.width('100%')
.height('100%')
.padding(10)
}
}
-
XComponentController:这个控制器是关键,它提供了获取 Surface ID 等方法,这个 ID 对于后续与相机、Native 层渲染等模块对接至关重要。 -
onLoad回调:当 XComponent 及其底层的 Surface 准备就绪时,会触发此回调,是进行初始化操作(如获取 Surface ID)的安全时机。 -
onDestroy回调:当组件卸载时触发,用于释放资源。
2、动态创建
除了在 build 方法中声明,还可以通过 NodeController 动态创建 XComponent 节点,需要更精细控制节点生命周期时非常有用。
import { NodeController, FrameNode, XComponentNode, XComponentType, NodeRenderType } from "@ohos.arkui.node";
import { UIContext } from '@ohos.arkui.UIContext';
class CustomXComponentNodeController extends NodeController {
private xCompNode: CustomXComponentNode | null = null;
// 假设使用名为 "native_render" 的 Native 动态库
private nativeLib: string = "native_render";
constructor() {
super();
}
// 创建并返回XComponentNode节点
makeNode(uiContext: UIContext): FrameNode | null {
this.xCompNode = new CustomXComponentNode(uiContext, {
// 必须显式指定理想大小,否则节点可能不显示
selfIdealSize: { width: 300, height: 300 }
}, "xcomponent_node_id", XComponentType.SURFACE, this.nativeLib);
return this.xCompNode;
}
// 示例:动态改变渲染类型
switchRenderType(newType: NodeRenderType): void {
if (this.xCompNode) {
this.xCompNode.changeRenderType(newType);
}
}
}
// 自定义XComponentNode,可以覆写生命周期回调
class CustomXComponentNode extends XComponentNode {
onCreate(event: Object) {
// 节点加载完成回调,event中包含Native上下文
console.info("CustomXComponentNode has been created.");
}
onDestroy() {
// 节点销毁回调
console.info("CustomXComponentNode is being destroyed.");
}
}
@Entry
@Component
struct DynamicGraphicsScene {
// 持有自定义NodeController的实例
private nodeCtrl: CustomXComponentNodeController = new CustomXComponentNodeController();
build() {
Column() {
// 使用NodeContainer来承载动态创建的XComponentNode
NodeContainer(this.nodeCtrl)
.width(300)
.height(300)
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
}
}
-
这种方式通过
NodeController和XComponentNode提供了更强的程序化控制能力,例如动态改变渲染类型 (changeRenderType)。
getXComponentSurfaceId(): string:获取SurfaceId的关键方法。
getXComponentContext(): Object:获取与Native层交互的上下文。
-
关键点:在
RenderOptions中必须通过selfIdealSize明确指定 XComponentNode 的尺寸,否则其内容区域可能为零而导致不显示。
三、常见应用场景
1、相机预览示例
这是 XComponent 最典型的应用之一。其核心流程是:获取 Surface 的ID -> 将该ID传递给相机系统 -> 相机将图像数据填充到对应的 Surface。
// 引入XComponentController和相机模块
import { XComponentController, XComponentType } from '@kit.ArkUI';
import camera from '@ohos.multimedia.camera';
@Entry
@Component
struct CameraPreview {
// 创建控制器实例
private xComponentCtrl: XComponentController = new XComponentController();
// 用于存储Surface ID
private surfaceId: string = '';
build() {
Column() {
// 使用XComponent组件
XComponent({
id: 'camera_xcomponent',
type: XComponentType.SURFACE, // 指定为SURFACE类型
controller: this.xComponentCtrl // 绑定控制器
})
.onLoad(() => {
// 组件加载完成,获取Surface ID
this.surfaceId = this.xComponentCtrl.getXComponentSurfaceId();
console.info(`Camera Surface ID: ${this.surfaceId}`);
// 将surfaceId传递给相机API,创建预览输出
camera.createPreviewOutput(this.surfaceId).then((previewOutput) => {
console.info('PreviewOutput created successfully');
// ... 后续可配置相机会话并启动预览
}).catch((error) => {
console.error(`Failed to create PreviewOutput: ${error.code}`);
});
})
.onDestroy(() => {
// 组件销毁时,停止预览并释放相机资源
console.info('CameraPreview XComponent is being destroyed.');
})
.width('100%')
.height('300px')
.backgroundColor(Color.Black) // 可设置默认背景色
}
.width('100%')
.height('100%')
}
}
2. 作为容器组件示例
当 type 设置为 COMPONENT 时,XComponent 不再是一块画布,而变成一个可以容纳其他子组件的容器。它本身不支持直接的事件响应,但其内部的子组件可以。
import { XComponentType } from '@kit.ArkUI';
@Entry
@Component
struct CustomContainer {
build() {
Column() {
// 作为容器的XComponent
XComponent({
id: 'container_component',
type: XComponentType.COMPONENT
}) {
// 在容器内可以正常使用其他子组件
Column() {
Text('这是一个容器内部')
.fontSize(20)
.fontColor(Color.White)
Button('点击我', { type: ButtonType.Normal })
.backgroundColor(Color.Pink)
.onClick(() => {
console.info('容器内的按钮被点击了!');
})
}
.justifyContent(FlexAlign.Center)
.width('100%')
.height(200)
.backgroundColor(Color.Grey)
}
.width('90%')
.margin({ top: 20 })
}
.width('100%')
.height('100%')
.padding(10)
}
}
3. 自定义图形绘制示例 (OpenGL ES)
ArkTS侧 (xxx.ets) 主要负责UI布局和Native桥接:
import { XComponentController, XComponentType } from '@kit.ArkUI';
@Entry
@Component
struct OpenGLScene {
private xComponentCtrl: XComponentController = new XComponentController();
// 假设你的Native动态库名为 'libgl_render.so'
private nativeLibrary: string = "gl_render";
build() {
Column() {
// 关联到Native动态库的XComponent
XComponent({
id: 'gl_xcomponent',
type: XComponentType.SURFACE,
controller: this.xComponentCtrl,
libraryname: this.nativeLibrary // 指定Native动态库名称
})
.onLoad(() => {
// 通常,Surface准备就绪的信号和后续的渲染指令会通过NAPI传递给Native层
console.info('OpenGL XComponent is loaded.');
const surfaceId = this.xComponentCtrl.getXComponentSurfaceId();
// 你可以通过NAPI调用,将surfaceId等参数传递给Native渲染引擎
})
.onDestroy(() => {
// 通知Native层释放OpenGL资源
console.info('OpenGL XComponent is being destroyed.');
})
.width('100%')
.height('300px')
}
.width('100%')
.height('100%')
}
}
C++侧 (Native层) 则负责核心的图形API调用和渲染工作:
-
初始化:在
OnSurfaceCreated回调中初始化EGL环境、创建OpenGL上下文。 -
渲染:在
OnSurfaceChanged(处理尺寸变化) 和渲染循环中,使用OpenGL ES命令进行绘制。 -
提交:通过
OH_NativeWindow_NativeWindowRequestBuffer获取OHNativeWindowBuffer,将渲染好的图像数据写入,最后通过OH_NativeWindow_NativeWindowFlushBuffer提交到屏幕。
对于需要自主控制每一帧画面的场景(如游戏、3D建模、数据可视化),XComponent 可以与 Native (C++) 层的 OpenGL ES 结合,实现高性能绘制。
更多推荐


所有评论(0)