鸿蒙NEXT开发实战往期必看文章:
HarmonyOS NEXT应用开发案例实践总结合(持续更新......)
HarmonyOS NEXT应用开发性能优化实践总结(持续更新......)
一分钟了解”纯血版!鸿蒙HarmonyOS Next应用开发!
“非常详细的” 鸿蒙HarmonyOS Next应用开发学习路线!(从零基础入门到精通)
占位组件
使用NDK接口构建UI界面时,需要在ArkTS页面创建用于挂载NDK接口创建组件的占位组件。占位组件类型为ContentSlot,ContentSlot能够绑定一个NodeContent对象,该对象可通过Node-API传递到Native侧挂载显示Native组件。
-
占位组件和其他ArkTS内置组件使用方法相同。
import { NodeContent } from '@ohos.arkui.node'; @Entry @Component struct Index { // 初始化NodeContent对象。 private rootSlot = new NodeContent(); @State @Watch('changeNativeFlag') showNative: boolean = false; changeNativeFlag(): void { if (this.showNative) { // 传递NodeContent对象用于Native创建组件的挂载显示 nativeNode.createNativeRoot(this.rootSlot) } else { // 销毁NativeModule组件 nativeNode.destroyNativeRoot() } } build() { Column() { Button(this.showNative ? "HideNativeUI" : "ShowNativeUI").onClick(() => { this.showNative = !this.showNative }) Row() { // 将NodeContent和ContentSlot占位组件绑定。 ContentSlot(this.rootSlot) }.layoutWeight(1) } .width('100%') .height('100%') } } ts
-
占位组件可以通过相关接口在Native侧转化为挂载对象。
ArkUI_NodeContentHandle contentHandle; OH_ArkUI_GetNodeContentFromNapiValue(env, args[0], &contentHandle);
-
挂载对象提供了相关挂载和卸载组件接口。
OH_ArkUI_NodeContent_AddNode(handle_, myNativeNode); OH_ArkUI_NodeContent_RemoveNode(handle_, myNativeNode);
NDK组件模块
NDK提供的UI组件能力如组件创建、树操作、属性设置、事件注册等是通过函数指针结构体(如ArkUI_NativeNodeAPI_1)进行暴露,该函数指针结构体可以通过模块查询接口获取。
ArkUI_NativeNodeAPI_1* arkUINativeNodeApi = nullptr;
OH_ArkUI_GetModuleInterface(ARKUI_NATIVE_NODE, ArkUI_NativeNodeAPI_1, arkUINativeNodeApi);
在获取到函数指针结构体后,可以使用该结构体内的函数实现相关UI组件操作。
-
组件创建和销毁。
auto listNode = arkUINativeNodeApi->createNode(ARKUI_NODE_LIST); arkUINativeNodeApi->disposeNode(listNode);
获取NDK接口支持的组件范围可以通过查询ArkUI_NodeType枚举值。
-
组件树操作。
auto parent = arkUINativeNodeApi->createNode(ARKUI_NODE_STACK); auto child = arkUINativeNodeApi->createNode(ARKUI_NODE_STACK); arkUINativeNodeApi->addChild(parent, child); arkUINativeNodeApi->removeChild(parent, child);
-
属性设置。
auto stack = arkUINativeNodeApi->createNode(ARKUI_NODE_STACK); ArkUI_NumberValue value[] = {{.f32 = 100}}; ArkUI_AttributeItem item = {value, 1}; arkUINativeNodeApi->setAttribute(stack, NODE_WIDTH, &item); ArkUI_NumberValue value[] = {{.u32 = 0xff112233}}; ArkUI_AttributeItem item = {value, 1}; arkUINativeNodeApi->setAttribute(stack, NODE_BACKGROUND_COLOR, &item);
获取NDK接口支持的属性范围可以通过查询ArkUI_NodeAttributeType枚举值。
-
事件注册。
auto stack = arkUINativeNodeApi->createNode(ARKUI_NODE_STACK); arkUINativeNodeApi->addNodeEventReceiver(stack, [](ArkUI_NodeEvent* event){ // process event }); arkUINativeNodeApi->registerNodeEvent(stack, NODE_ON_CLICK, 0, nullptr);
获取NDK接口支持的事件范围可以通过查询ArkUI_NodeEventType枚举值。
示例
下面的示例展示了如何使用ContentSlot挂载Native侧的文本列表。
图1 Native文本列表
-
在ArkTS页面上声明用于Native页面挂载的占位组件,并在页面创建时通知Native侧创建文本列表。
import nativeNode from 'libentry.so'; import { NodeContent } from '@ohos.arkui.node'; @Entry @Component struct Index { // 初始化NodeContent对象。 private rootSlot = new NodeContent(); @State @Watch('changeNativeFlag') showNative: boolean = false; changeNativeFlag(): void { if (this.showNative) { // 传递NodeContent对象用于Native创建组件的挂载显示 nativeNode.createNativeRoot(this.rootSlot) } else { // 销毁NativeModule组件 nativeNode.destroyNativeRoot() } } build() { Column() { Button(this.showNative ? "HideNativeUI" : "ShowNativeUI").onClick(() => { this.showNative = !this.showNative }) Row() { // 将NodeContent和ContentSlot占位组件绑定。 ContentSlot(this.rootSlot) }.layoutWeight(1) } .width('100%') .height('100%') } } ts
-
使用Native模板创建工程,并在Native侧提供Node-API的桥接方法,实现ArkTS侧的NativeNode模块接口。 接口声明。
// entry/src/main/cpp/types/libentry/Index.d.ts export const createNativeRoot: (content: Object) => void; export const destroyNativeRoot: () => void; ts
Native实现。
// entry/src/main/cpp/napi_init.cpp #include "NativeEntry.h" #include "napi/native_api.h" EXTERN_C_START static napi_value Init(napi_env env, napi_value exports) { // 绑定Native侧的创建组件和销毁组件。 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 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}, }; extern "C" __attribute__((constructor)) void RegisterEntryModule(void) { napi_module_register(&demoModule); } cpp
-
在NativeEnrty.h文件中创建Native界面。
// NativeEntry.h #ifndef MYAPPLICATION_NATIVEENTRY_H #define MYAPPLICATION_NATIVEENTRY_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组件的生命周期和内存。 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上用于挂载显示。 OH_ArkUI_NodeContent_AddNode(handle_, root_->GetHandle()); } void DisposeRootNode() { // 从NodeContent上卸载组件并销毁Native组件。 OH_ArkUI_NodeContent_RemoveNode(handle_, root_->GetHandle()); root_.reset(); } private: std::shared_ptr<ArkUIBaseNode> root_; ArkUI_NodeContentHandle handle_; }; } // namespace NativeModule #endif // MYAPPLICATION_NATIVEENTRY_H c
对应实现文件。
// NativeEntry.cpp #include "NativeEntry.h" #include <arkui/native_node_napi.h> #include <hilog/log.h> #include <js_native_api.h> namespace NativeModule { napi_value CreateNativeRoot(napi_env env, napi_callback_info info) { size_t argc = 1; napi_value args[1] = {nullptr}; napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); // 获取NodeContent ArkUI_NodeContentHandle contentHandle; OH_ArkUI_GetNodeContentFromNapiValue(env, args[0], &contentHandle); NativeEntry::GetInstance()->SetContentHandle(contentHandle); // 创建文本列表 auto list = CreateTextListExample(); // 保持Native侧对象到管理类中,维护生命周期。 NativeEntry::GetInstance()->SetRootNode(list); return nullptr; } napi_value DestroyNativeRoot(napi_env env, napi_callback_info info) { // 从管理类中释放Native侧对象。 NativeEntry::GetInstance()->DisposeRootNode(); return nullptr; } } // namespace NativeModule cpp
使用NDK 提供的C接口需要在CMakeLists.txt 中增加libace_ndk.z.so 的引用,如下所示,其中entry为工程导出的动态库名称,如当前示例使用的是默认的名称 libentry.so。
target_link_libraries(entry PUBLIC libace_napi.z.so libace_ndk.z.so)
-
由于NDK接口提供的是C接口,为了使用面向对象的方式简化编程和工程管理,这里建议使用C++进行二次封装,下面示例代码展示了示例界面中所需的列表,文本组件封装类。 1)获取ArkUI在NDK接口的入口模块ArkUI_NativeNodeAPI_1,该结构体模块提供了一系列组件创建、树构建、属性设置和事件注册等函数指针。
// NativeModule.h // 提供获取ArkUI在Native侧模块的封装接口 #ifndef MYAPPLICATION_NATIVEMODULE_H #define MYAPPLICATION_NATIVEMODULE_H #include <arkui/native_node.h> #include <functional> #include <cassert> #include <arkui/native_interface.h> namespace NativeModule { class NativeModuleInstance { public: static NativeModuleInstance *GetInstance() { static NativeModuleInstance instance; return &instance; } NativeModuleInstance() { // 获取NDK接口的函数指针结构体对象,用于后续操作。 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 c
2)提供列表,文本组件的基类对象,用于封装通用属性和事件。
// ArkUIBaseNode.h // 提供组件树操作的基类。 #ifndef MYAPPLICATION_ARKUIBASENODE_H #define MYAPPLICATION_ARKUIBASENODE_H #include <arkui/native_type.h> #include <list> #include <memory> #include "NativeModule.h" namespace NativeModule { class ArkUIBaseNode { public: explicit ArkUIBaseNode(ArkUI_NodeHandle handle) : handle_(handle), nativeModule_(NativeModuleInstance::GetInstance()->GetNativeNodeAPI()) {} virtual ~ArkUIBaseNode() { // 封装析构函数,实现子节点移除功能。 if (!children_.empty()) { for (const auto& child : children_) { nativeModule_->removeChild(handle_, child->GetHandle()); } children_.clear(); } // 封装析构函数,统一回收节点资源。 nativeModule_->disposeNode(handle_); } void AddChild(const std::shared_ptr<ArkUIBaseNode> &child) { children_.emplace_back(child); OnAddChild(child); } void RemoveChild(const std::shared_ptr<ArkUIBaseNode> &child) { children_.remove(child); OnRemoveChild(child); } void InsertChild(const std::shared_ptr<ArkUIBaseNode> &child, int32_t index) { if (index >= children_.size()) { AddChild(child); } else { auto iter = children_.begin(); std::advance(iter, index); children_.insert(iter, child); OnInsertChild(child, index); } } ArkUI_NodeHandle GetHandle() const { return handle_; } protected: // 针对父容器子类需要重载下面的函数,实现组件挂载和卸载。 virtual void OnAddChild(const std::shared_ptr<ArkUIBaseNode> &child) {} virtual void OnRemoveChild(const std::shared_ptr<ArkUIBaseNode> &child) {} virtual void OnInsertChild(const std::shared_ptr<ArkUIBaseNode> &child, int32_t index) {} ArkUI_NodeHandle handle_; ArkUI_NativeNodeAPI_1 *nativeModule_ = nullptr; private: std::list<std::shared_ptr<ArkUIBaseNode>> children_; }; } // namespace NativeModule #endif // MYAPPLICATION_ARKUIBASENODE_H c
// ArkUINode.h // 提供通用属性和事件的封装。 #ifndef MYAPPLICATION_ARKUINODE_H #define MYAPPLICATION_ARKUINODE_H #include "ArkUIBaseNode.h" #include "NativeModule.h" #include <arkui/native_node.h> #include <arkui/native_type.h> namespace NativeModule { class ArkUINode : public ArkUIBaseNode { public: explicit ArkUINode(ArkUI_NodeHandle handle) : ArkUIBaseNode(handle) {} ~ArkUINode() override {} // NDK相关通用属性调用封装 void SetWidth(float width) { assert(handle_); ArkUI_NumberValue value[] = {{.f32 = width}}; ArkUI_AttributeItem item = {value, 1}; nativeModule_->setAttribute(handle_, NODE_WIDTH, &item); } void SetPercentWidth(float percent) { assert(handle_); ArkUI_NumberValue value[] = {{.f32 = percent}}; ArkUI_AttributeItem item = {value, 1}; nativeModule_->setAttribute(handle_, NODE_WIDTH_PERCENT, &item); } void SetHeight(float height) { assert(handle_); ArkUI_NumberValue value[] = {{.f32 = height}}; ArkUI_AttributeItem item = {value, 1}; nativeModule_->setAttribute(handle_, NODE_HEIGHT, &item); } void SetPercentHeight(float percent) { assert(handle_); ArkUI_NumberValue value[] = {{.f32 = percent}}; ArkUI_AttributeItem item = {value, 1}; nativeModule_->setAttribute(handle_, NODE_HEIGHT_PERCENT, &item); } void SetBackgroundColor(uint32_t color) { assert(handle_); ArkUI_NumberValue value[] = {{.u32 = color}}; ArkUI_AttributeItem item = {value, 1}; nativeModule_->setAttribute(handle_, NODE_BACKGROUND_COLOR, &item); } protected: // 组件树操作的实现类对接。 void OnAddChild(const std::shared_ptr<ArkUIBaseNode> &child) override { nativeModule_->addChild(handle_, child->GetHandle()); } void OnRemoveChild(const std::shared_ptr<ArkUIBaseNode> &child) override { nativeModule_->removeChild(handle_, child->GetHandle()); } void OnInsertChild(const std::shared_ptr<ArkUIBaseNode> &child, int32_t index) override { nativeModule_->insertChildAt(handle_, child->GetHandle(), index); } }; } // namespace NativeModule #endif // MYAPPLICATION_ARKUINODE_H c
3)实现列表组件。
// ArkUIListNode.h // 提供列表组件的封装。 #ifndef MYAPPLICATION_ARKUILISTNODE_H #define MYAPPLICATION_ARKUILISTNODE_H #include "ArkUINode.h" namespace NativeModule { class ArkUIListNode : public ArkUINode { public: ArkUIListNode() : ArkUINode((NativeModuleInstance::GetInstance()->GetNativeNodeAPI())->createNode(ARKUI_NODE_LIST)) {} // 创建ArkUI的列表组件。 ~ArkUIListNode() override {} // List组件的属性NDK接口封装。 void SetScrollBarState(bool isShow) { assert(handle_); ArkUI_ScrollBarDisplayMode displayMode = isShow ? ARKUI_SCROLL_BAR_DISPLAY_MODE_ON : ARKUI_SCROLL_BAR_DISPLAY_MODE_OFF; ArkUI_NumberValue value[] = {{.i32 = displayMode}}; ArkUI_AttributeItem item = {value, 1}; nativeModule_->setAttribute(handle_, NODE_SCROLL_BAR_DISPLAY_MODE, &item); } }; } // namespace NativeModule #endif // MYAPPLICATION_ARKUILISTNODE_H c
4)实现列表项组件。
// ArkUIListItemNode.h // 提供列表项的封装类。 #ifndef MYAPPLICATION_ARKUISTACKNODE_H #define MYAPPLICATION_ARKUISTACKNODE_H #include "ArkUINode.h" namespace NativeModule { class ArkUIListItemNode : public ArkUINode { public: ArkUIListItemNode() : ArkUINode((NativeModuleInstance::GetInstance()->GetNativeNodeAPI())->createNode(ARKUI_NODE_LIST_ITEM)) {} }; } // namespace NativeModule #endif // MYAPPLICATION_ARKUISTACKNODE_H c
5)实现文本组件。
// ArkUITextNode.h // 实现文本组件的封装类。 #ifndef MYAPPLICATION_ARKUITEXTNODE_H #define MYAPPLICATION_ARKUITEXTNODE_H #include "ArkUINode.h" #include <string> namespace NativeModule { class ArkUITextNode : public ArkUINode { public: ArkUITextNode() : ArkUINode((NativeModuleInstance::GetInstance()->GetNativeNodeAPI())->createNode(ARKUI_NODE_TEXT)) {} // 文本属性NDK接口封装。 void SetFontSize(float fontSize) { assert(handle_); ArkUI_NumberValue value[] = {{.f32 = fontSize}}; ArkUI_AttributeItem item = {value, 1}; nativeModule_->setAttribute(handle_, NODE_FONT_SIZE, &item); } void SetFontColor(uint32_t color) { assert(handle_); ArkUI_NumberValue value[] = {{.u32 = color}}; ArkUI_AttributeItem item = {value, 1}; nativeModule_->setAttribute(handle_, NODE_FONT_COLOR, &item); } void SetTextContent(const std::string &content) { assert(handle_); ArkUI_AttributeItem item = {nullptr, 0, content.c_str()}; nativeModule_->setAttribute(handle_, NODE_TEXT_CONTENT, &item); } void SetTextAlign(ArkUI_TextAlignment align) { assert(handle_); ArkUI_NumberValue value[] = {{.i32 = align}}; ArkUI_AttributeItem item = {value, 1}; nativeModule_->setAttribute(handle_, NODE_TEXT_ALIGN, &item); } }; } // namespace NativeModule #endif // MYAPPLICATION_ARKUITEXTNODE_H c
-
完善步骤3的CreateTextListExample函数,实现Native文本列表的创建和挂载显示。
// NativeEnrty.h // 自定义NDK接口入口函数。 #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:使用智能指针创建List组件。 auto list = std::make_shared<ArkUIListNode>(); list->SetPercentWidth(1); list->SetPercentHeight(1); // 2:创建ListItem子组件并挂载到List上。 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->SetPercentWidth(1); textNode->SetHeight(100); textNode->SetBackgroundColor(0xFFfffacd); textNode->SetTextAlign(ARKUI_TEXT_ALIGNMENT_CENTER); listItem->AddChild(textNode); list->AddChild(listItem); } return list; } } // namespace NativeModule #endif // MYAPPLICATION_NORMALTEXTLISTEXAMPLE_H
最后
小编在之前的鸿蒙系统扫盲中,有很多朋友给我留言,不同的角度的问了一些问题,我明显感觉到一点,那就是许多人参与鸿蒙开发,但是又不知道从哪里下手,因为资料太多,太杂,无从选择。有很多小伙伴不知道学习哪些鸿蒙开发技术?不知道需要重点掌握哪些鸿蒙应用开发知识点?而且学习时频繁踩坑,最终浪费大量时间。所以有一份实用的鸿蒙(HarmonyOS NEXT)文档用来跟着学习是非常有必要的。
如果你是一名有经验的资深Android移动开发、Java开发、前端开发、对鸿蒙感兴趣以及转行人员
→ 鸿蒙HarmonyOS Next 全栈开发学习笔记 希望这一份鸿蒙学习文档能够给大家带来帮助~
这份鸿蒙(HarmonyOS NEXT)包含了鸿蒙开发必掌握的核心知识要点,内容包含了(ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、音频、视频、WebGL、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、(南向驱动、嵌入式等)鸿蒙项目实战等等)鸿蒙(HarmonyOS NEXT)技术知识点。