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 UI
3.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 NativeModule
3.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層的性能優勢。