HarmonyOS 6.0 UI开发新姿势:基于ArkUI NDK UI开发第一个页面
在HarmonyOS 6.0中,ArkUI推出了NDK UI开发能力,允许开发者通过C/C++语言直接构建Native层UI组件,并与ArkTS页面无缝集成。这种开发方式不仅能充分利用Native层的性能优势,还能满足部分复杂UI场景的定制化需求。本文将从零开始,带大家掌握ArkUI NDK UI开发的核心流程,最终实现一个可挂载到ArkTS页面的Native文本列表。
一、核心前置知识:ArkTS与Native UI的桥梁搭建
要实现Native UI在ArkTS页面的展示,核心是搭建两者之间的通信与挂载桥梁,关键涉及占位组件和NDK基础配置。
1.1 占位组件:ContentSlot & NodeContent
使用ArkUI NDK构建UI时,必须在ArkTS页面中创建占位组件,用于承载Native侧创建的UI组件。这里的核心组件是ContentSlot,它的核心作用是提供Native UI的挂载容器,而NodeContent则是连接ArkTS侧与Native侧的桥梁对象,可通过Node-API传递到Native侧,用于挂载显示Native组件。
ContentSlot的使用方式与普通ArkTS系统组件一致,核心是完成与NodeContent的绑定,以及通过状态控制Native UI的显示与销毁。
1.2 NDK配置文件:oh-package.json5
在Native模块中,需要通过oh-package.json5配置文件声明动态库信息,实现ArkTS侧对Native库的引用。该文件位于entry/src/main/cpp/types/libentry/目录下,核心配置如下:
{
"name": "libentry.so",
"types": "./index.d.ts",
"version": "",
"description": "Please describe the basic information."
}name:指定Native动态库名称(ArkTS侧通过该名称引用库)types:指定桥接接口声明文件(.d.ts格式,定义Native与ArkTS的交互方法)
1.3 ArkTS侧核心代码实现
在ArkTS页面中,我们需要完成NodeContent初始化、ContentSlot绑定,以及通过按钮控制Native UI的显示与隐藏,核心代码如下:
import { NodeContent } from '@kit.ArkUI';
import nativeNode from 'libentry.so'; // 引用Native动态库
@Entry
@Component
struct Index {
// 初始化NodeContent对象,作为跨端桥梁
private rootSlot = new NodeContent();
// 状态变量,控制Native UI显示/隐藏,绑定监听函数
@State @Watch('changeNativeFlag') showNative: boolean = false;
// 监听状态变化,创建/销毁Native UI
changeNativeFlag(): void {
if (this.showNative) {
// 传递NodeContent对象,让Native侧挂载UI组件
nativeNode.createNativeRoot(this.rootSlot)
} else {
// 销毁Native侧UI组件,释放资源
nativeNode.destroyNativeRoot()
}
}
build() {
Column() {
// 切换按钮:控制Native UI的显示与隐藏
Button(this.showNative ? "隐藏NativeUI" : "显示NativeUI")
.fontSize($r('app.float.page_text_font_size'))
.fontWeight(FontWeight.Bold)
.onClick(() => {
this.showNative = !this.showNative
})
Row() {
// 占位组件:绑定NodeContent,承载Native UI
ContentSlot(this.rootSlot)
}.layoutWeight(1)
}
.width('100%')
.height('100%')
}
}ContentSlot、NodeContent、ArkTS与C++代码关系可以概括为:ContentSlot在ArkTS中用来占位UI,构建ContentSlot需要NodeContent对象实例,同时把NodeContent实例对象传到C++层,在C++层实现控件的挂载等,NodeContent实例对象是ArkTS和C++代码的桥接。
二、NDK UI组件核心操作:基于ArkUI_NativeNodeAPI_1
ArkUI NDK提供的UI能力(组件创建、树操作、属性设置等),均通过函数指针结构体(如ArkUI_NativeNodeAPI_1)暴露。开发者需先获取该结构体实例,再通过其内部函数完成各类UI操作。
2.1 模块初始化:获取函数指针结构体
模块查询接口OH_ArkUI_GetModuleInterface不仅能获取ArkUI_NativeNodeAPI_1实例,还包含NDK全局初始化逻辑,建议优先调用:
// 全局初始化,获取UI操作函数指针结构体
ArkUI_NativeNodeAPI_1* arkUINativeNodeApi = nullptr;
OH_ArkUI_GetModuleInterface(ARKUI_NATIVE_NODE, ArkUI_NativeNodeAPI_1, arkUINativeNodeApi);2.2 核心UI操作:组件创建到事件注册
获取ArkUI_NativeNodeAPI_1实例后,即可完成各类Native UI操作,核心功能如下:
(1)组件创建与销毁
通过createNode创建指定类型的组件(组件类型参考ArkUI_NodeType枚举),通过disposeNode销毁组件释放资源:
// 创建列表组件(ARKUI_NODE_LIST为枚举值,对应List组件)
auto listNode = arkUINativeNodeApi->createNode(ARKUI_NODE_LIST);
// 销毁列表组件,释放内存
arkUINativeNodeApi->disposeNode(listNode);createNode用来创建组件节点、disposeNode用来销毁组件节点。支持C++创建的组件枚举如下:
typedef enum {
/** Custom node. */
ARKUI_NODE_CUSTOM = 0,
/** Text. */
ARKUI_NODE_TEXT = 1,
/** Text span. */
ARKUI_NODE_SPAN = 2,
/** Image span. */
ARKUI_NODE_IMAGE_SPAN = 3,
/** Image. */
ARKUI_NODE_IMAGE = 4,
/** Toggle. */
ARKUI_NODE_TOGGLE = 5,
/** Loading icon. */
ARKUI_NODE_LOADING_PROGRESS = 6,
/** Single-line text input. */
ARKUI_NODE_TEXT_INPUT = 7,
/** Multi-line text input. */
ARKUI_NODE_TEXT_AREA = 8,
/** Button. */
ARKUI_NODE_BUTTON = 9,
/** Progress indicator. */
ARKUI_NODE_PROGRESS = 10,
/** Check box. */
ARKUI_NODE_CHECKBOX = 11,
/** XComponent. */
ARKUI_NODE_XCOMPONENT = 12,
/** Date picker. */
ARKUI_NODE_DATE_PICKER = 13,
/** Time picker. */
ARKUI_NODE_TIME_PICKER = 14,
/** Text picker. */
ARKUI_NODE_TEXT_PICKER = 15,
/** Calendar picker. */
ARKUI_NODE_CALENDAR_PICKER = 16,
/** Slider. */
ARKUI_NODE_SLIDER = 17,
/** Radio */
ARKUI_NODE_RADIO = 18,
/** Image animator. */
ARKUI_NODE_IMAGE_ANIMATOR = 19,
/** XComponent of type TEXTURE.
* @since 18 */ ARKUI_NODE_XCOMPONENT_TEXTURE,
/** Check box group.
* @since 15 */ ARKUI_NODE_CHECKBOX_GROUP = 21,
/** Stack container. */
ARKUI_NODE_STACK = MAX_NODE_SCOPE_NUM,
/** Swiper. */
ARKUI_NODE_SWIPER,
/** Scrolling container. */
ARKUI_NODE_SCROLL,
/** List. */
ARKUI_NODE_LIST,
/** List item. */
ARKUI_NODE_LIST_ITEM,
/** List item group. */
ARKUI_NODE_LIST_ITEM_GROUP,
/** Column container. */
ARKUI_NODE_COLUMN,
/** Row container. */
ARKUI_NODE_ROW,
/** Flex container. */
ARKUI_NODE_FLEX,
/** Refresh component. */
ARKUI_NODE_REFRESH,
/** Water flow container. */
ARKUI_NODE_WATER_FLOW,
/** Water flow item. */
ARKUI_NODE_FLOW_ITEM,
/** Relative layout component. */
ARKUI_NODE_RELATIVE_CONTAINER,
/** Grid. */
ARKUI_NODE_GRID,
/** Grid item. */
ARKUI_NODE_GRID_ITEM,
/** Custom span. */
ARKUI_NODE_CUSTOM_SPAN,
/**
* EmbeddedComponent. * @since 20 */ ARKUI_NODE_EMBEDDED_COMPONENT,
/**
* Undefined. * @since 20 */ ARKUI_NODE_UNDEFINED,
} ArkUI_NodeType;在createNode传入对应枚举值创建对应组件。
(2)组件树操作
支持父组件添加/移除子组件,构建复杂UI层级结构:
// 创建父容器(Stack)和子容器(Stack)
auto parent = arkUINativeNodeApi->createNode(ARKUI_NODE_STACK);
auto child = arkUINativeNodeApi->createNode(ARKUI_NODE_STACK);
// 添加子组件到父组件
arkUINativeNodeApi->addChild(parent, child);
// 从父组件中移除子组件
arkUINativeNodeApi->removeChild(parent, child);(3)组件属性设置
通过setAttribute设置组件属性(属性类型参考ArkUI_NodeAttributeType枚举),支持宽高、背景色、字体大小等各类属性:
// 创建Stack组件
auto stack = arkUINativeNodeApi->createNode(ARKUI_NODE_STACK);
// 设置组件宽度为100px
ArkUI_NumberValue value[] = {{.f32 = 100}};
ArkUI_AttributeItem item = {value, 1};
arkUINativeNodeApi->setAttribute(stack, NODE_WIDTH, &item);
// 设置组件背景色为#112233
ArkUI_NumberValue value_color[] = {{.u32 = 0xff112233}};
ArkUI_AttributeItem item_color = {value_color, 1};
arkUINativeNodeApi->setAttribute(stack, NODE_BACKGROUND_COLOR, &item_color);(4)组件事件注册
通过addNodeEventReceiver设置事件回调,通过registerNodeEvent注册指定事件(事件类型参考ArkUI_NodeEventType枚举):
// 创建Stack组件
auto stack = arkUINativeNodeApi->createNode(ARKUI_NODE_STACK);
// 设置事件回调函数
arkUINativeNodeApi->addNodeEventReceiver(stack, [](ArkUI_NodeEvent* event){
// 事件处理逻辑(如点击事件响应)
});
// 注册点击事件(NODE_ON_CLICK为枚举值,对应点击事件)
arkUINativeNodeApi->registerNodeEvent(stack, NODE_ON_CLICK, 0, nullptr);(5)Native侧获取NodeContent与挂载组件
ArkTS侧传递的NodeContent对象,在Native侧需通过OH_ArkUI_GetNodeContentFromNapiValue转换为挂载句柄,再通过OH_ArkUI_NodeContent_AddNode/OH_ArkUI_NodeContent_RemoveNode完成组件挂载与卸载:
// 从ArkTS传递的参数中获取NodeContent句柄
ArkUI_NodeContentHandle contentHandle;
OH_ArkUI_GetNodeContentFromNapiValue(env, args[0], &contentHandle);
// 挂载Native组件到NodeContent(显示UI)
OH_ArkUI_NodeContent_AddNode(handle_, myNativeNode);
// 从NodeContent卸载Native组件(隐藏并释放UI)
OH_ArkUI_NodeContent_RemoveNode(handle_, myNativeNode);OH_ArkUI_GetNodeContentFromNapiValue将ArkTS中传入的NodeContent实例对象转换为ArkUI_NodeContentHandle类型。有了ArkUI_NodeContentHandle对象后可以通过 OH_ArkUI_NodeContent_AddNode给ArkTS中的NodeContent挂载具体组件,通过OH_ArkUI_NodeContent_RemoveNode移除对应组件。
三、实操示例:构建Native文本列表
下面通过一个完整示例,展示如何实现一个可挂载到ArkTS页面的Native文本列表,包含目录结构、桥接层实现、组件封装与功能落地。
3.1 创建工程
首先创建Native C++工程:
3.2 步骤1:Native侧桥接接口声明(index.d.ts)
定义ArkTS侧可调用的Native方法,实现跨端交互:
// entry/src/main/cpp/types/libentry/index.d.ts
export const createNativeRoot: (content: Object) => void; // 创建Native UI
export const destroyNativeRoot: () => void; // 销毁Native UI3.3 步骤2:Native侧桥接方法绑定(napi_init.cpp)
将index.d.ts声明的方法与Native侧实现绑定,完成Node-API桥接:
// entry/src/main/cpp/napi_init.cpp
#include "napi/native_api.h"
#include "NativeEntry.h"
EXTERN_C_START
// 初始化函数:绑定桥接方法
static napi_value Init(napi_env env, napi_value exports) {
// 绑定createNativeRoot和destroyNativeRoot方法
napi_property_descriptor desc[] = {
{"createNativeRoot", nullptr, NativeModule::CreateNativeRoot, nullptr, nullptr, nullptr, napi_default, nullptr},
{"destroyNativeRoot", nullptr, NativeModule::DestroyNativeRoot, nullptr, nullptr, nullptr, napi_default, nullptr}};
napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
return exports;
}
EXTERN_C_END
// 定义Native模块
static napi_module demoModule = {
.nm_version = 1,
.nm_flags = 0,
.nm_filename = nullptr,
.nm_register_func = Init,
.nm_modname = "entry",
.nm_priv = ((void *)0),
.reserved = {0},
};
// 注册Native模块
extern "C" __attribute__((constructor)) void RegisterEntryModule(void) {
napi_module_register(&demoModule);
}3.4 步骤3:Native侧核心逻辑实现(NativeEntry.h/cpp)
实现createNativeRoot和destroyNativeRoot方法,完成NodeContent获取、Native UI创建与销毁,以及生命周期管理:
(1)头文件声明(NativeEntry.h)
// entry/src/main/cpp/NativeEntry.h
#ifndef MYAPPLICATION_NATIVEENTRY_H
#define MYAPPLICATION_NATIVEENTRY_H
#include <ArkUIBaseNode.h>
#include <arkui/native_type.h>
#include <js_native_api_types.h>
namespace NativeModule {
napi_value CreateNativeRoot(napi_env env, napi_callback_info info);
napi_value DestroyNativeRoot(napi_env env, napi_callback_info info);
// 单例类:管理Native UI组件生命周期和内存
class NativeEntry {
public:
static NativeEntry *GetInstance() {
static NativeEntry nativeEntry;
return &nativeEntry;
}
void SetContentHandle(ArkUI_NodeContentHandle handle) {
handle_ = handle;
}
void SetRootNode(const std::shared_ptr<ArkUIBaseNode> &baseNode) {
root_ = baseNode;
// 挂载Native组件到NodeContent,实现UI显示
OH_ArkUI_NodeContent_AddNode(handle_, root_->GetHandle());
}
void DisposeRootNode() {
// 从NodeContent卸载组件,并销毁Native UI
OH_ArkUI_NodeContent_RemoveNode(handle_, root_->GetHandle());
root_.reset();
}
private:
std::shared_ptr<ArkUIBaseNode> root_; // 根组件句柄
ArkUI_NodeContentHandle handle_; // NodeContent句柄
};
} // namespace NativeModule
#endif // MYAPPLICATION_NATIVEENTRY_H(2)实现文件(NativeEntry.cpp)
// entry/src/main/cpp/NativeEntry.cpp
#include <arkui/native_node_napi.h>
#include <hilog/log.h>
#include <js_native_api.h>
#include "NativeEntry.h"
#include "NormalTextListExample.h"
namespace NativeModule {
napi_value CreateNativeRoot(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1] = {nullptr};
// 获取ArkTS传递的参数(NodeContent对象)
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
// 转换为Native侧NodeContent句柄
ArkUI_NodeContentHandle contentHandle;
OH_ArkUI_GetNodeContentFromNapiValue(env, args[0], &contentHandle);
NativeEntry::GetInstance()->SetContentHandle(contentHandle);
// 创建文本列表组件
auto list = CreateTextListExample();
// 挂载组件,维护生命周期
NativeEntry::GetInstance()->SetRootNode(list);
return nullptr;
}
napi_value DestroyNativeRoot(napi_env env, napi_callback_info info) {
// 销毁Native UI组件,释放资源
NativeEntry::GetInstance()->DisposeRootNode();
return nullptr;
}
} // namespace NativeModule3.5 步骤4:CMakeLists.txt配置
配置C/C++编译参数,链接ArkUI NDK库,并添加需要编译的cpp文件:
# entry/src/main/cpp/CMakeLists.txt
add_library(entry SHARED napi_init.cpp NativeEntry.cpp)
# 链接ArkUI NDK库和Node-API库
target_link_libraries(entry PUBLIC libace_napi.z.so libace_ndk.z.so)3.6 步骤5:Native侧UI组件封装
为简化开发,采用C++面向对象方式封装UI组件,实现通用属性、生命周期管理,核心封装如下:
(1)全局API封装(NativeModule.h)
单例类封装ArkUI_NativeNodeAPI_1,提供全局访问入口:
#ifndef MYAPPLICATION_NATIVEMODULE_H
#define MYAPPLICATION_NATIVEMODULE_H
#include "napi/native_api.h"
#include <arkui/native_node.h>
#include <cassert>
#include <arkui/native_interface.h>
namespace NativeModule {
class NativeModuleInstance {
public:
static NativeModuleInstance *GetInstance() {
static NativeModuleInstance instance;
return &instance;
}
NativeModuleInstance() {
// 初始化并获取ArkUI Native API
OH_ArkUI_GetModuleInterface(ARKUI_NATIVE_NODE, ArkUI_NativeNodeAPI_1, arkUINativeNodeApi_);
assert(arkUINativeNodeApi_);
}
ArkUI_NativeNodeAPI_1 *GetNativeNodeAPI() { return arkUINativeNodeApi_; }
private:
ArkUI_NativeNodeAPI_1 *arkUINativeNodeApi_ = nullptr;
};
} // namespace NativeModule
#endif // MYAPPLICATION_NATIVEMODULE_H(2)基类封装(ArkUIBaseNode.h/ArkUINode.h)
ArkUIBaseNode:封装组件树操作(添加/移除子组件)和生命周期管理(自动销毁子组件)ArkUINode:继承ArkUIBaseNode,封装通用属性(宽高、背景色等)
(3)业务组件封装(列表/列表项/文本)
分别封装ArkUIListNode(列表组件)、ArkUIListItemNode(列表项组件)、ArkUITextNode(文本组件),暴露专属属性设置方法(如字体大小、滚动条状态等)。
3.7 步骤6:文本列表功能落地(NormalTextListExample.h)
创建30条文本数据的列表,完成组件嵌套与属性设置,最终返回列表根组件:
#ifndef MYAPPLICATION_NORMALTEXTLISTEXAMPLE_H
#define MYAPPLICATION_NORMALTEXTLISTEXAMPLE_H
#include "ArkUIBaseNode.h"
#include "ArkUIListItemNode.h"
#include "ArkUIListNode.h"
#include "ArkUITextNode.h"
#include <hilog/log.h>
namespace NativeModule {
std::shared_ptr<ArkUIBaseNode> CreateTextListExample() {
// 1. 创建列表组件,设置宽高占比100%,显示滚动条
auto list = std::make_shared<ArkUIListNode>();
list->SetPercentWidth(1);
list->SetPercentHeight(1);
list->SetScrollBarState(true);
// 2. 循环创建30个列表项,每个列表项包含一个文本组件
for (int32_t i = 0; i < 30; ++i) {
auto listItem = std::make_shared<ArkUIListItemNode>();
auto textNode = std::make_shared<ArkUITextNode>();
// 设置文本属性:内容、字体大小、颜色、背景色等
textNode->SetTextContent("条目:" + std::to_string(i));
textNode->SetFontSize(16);
textNode->SetFontColor(0xFFEBEBEB);
textNode->SetPercentWidth(1);
textNode->SetWidth(300);
textNode->SetHeight(100);
textNode->SetBackgroundColor(0xFFFAA533);
textNode->SetTextAlign(ARKUI_TEXT_ALIGNMENT_CENTER);
// 文本组件添加到列表项,列表项添加到列表
listItem->InsertChild(textNode, i);
list->AddChild(listItem);
}
return list;
}
} // namespace NativeModule
#endif // MYAPPLICATION_NORMALTEXTLISTEXAMPLE_H注意:上述代码中设置颜色的地方SetTextContent和SetBackgroundColor,设置的颜色必须是ARGB样式,不能省略A,否则会渲染失败。
3.8 项目目录结构说明和运行效果展示
示例代码的目录结构清晰划分了ArkTS侧与Native侧文件,便于工程管理:
.
|——cpp // Native侧核心代码目录
| |——types
| | |——libentry
| | | |——index.d.ts // 桥接接口声明文件
| |——napi_init.cpp // Native与ArkTS桥接方法绑定
| |——NativeEntry.cpp // 桥接方法具体实现
| |——NativeEntry.h // 桥接方法头文件声明
| |——CMakeLists.txt // C/C++编译配置文件
| |——ArkUIBaseNode.h // UI组件基类(封装通用生命周期)
| |——ArkUINode.h // UI组件通用属性封装
| |——ArkUIListNode.h // 列表组件封装
| |——ArkUIListItemNode.h // 列表项组件封装
| |——ArkUITextNode.h // 文本组件封装
| |——NormalTextListExample.h // 文本列表功能实现
|
|——ets // ArkTS侧代码目录
| |——pages
| |——entry.ets // 应用启动页(承载Native UI)项目目录结构截图如下:
运行效果:
点击按钮后展示文本列表:
四、总结
本文详细讲解了HarmonyOS 6.0 ArkUI NDK UI开发的核心流程,从ArkTS侧占位组件搭建、Native侧桥接层实现,到UI组件封装与文本列表落地,核心要点如下:
ContentSlot+NodeContent是ArkTS与Native UI的核心桥梁,实现Native UI的挂载与显示ArkUI_NativeNodeAPI_1是Native UI操作的入口,需通过OH_ArkUI_GetModuleInterface初始化- 采用C++面向对象封装Native UI组件,可简化开发并提升工程可维护性
- 桥接层(.d.ts + napi_init.cpp)是ArkTS与Native的交互关键,实现方法绑定与参数传递
通过本文的步骤,开发者可快速搭建第一个ArkUI NDK UI页面,后续可基于该框架拓展更复杂的Native UI场景(如图形绘制、高性能列表等),充分发挥HarmonyOS Native层的性能优势。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用。你还可以使用@来通知其他用户。