赞
踩
使用NDK接口构建UI界面时,需要在ArkTS页面创建用于挂载NDK接口创建组件的占位组件。占位组件类型为ContentSlot,ContentSlot能够绑定一个NodeContent对象,该对象可通过Node-API传递到Native侧挂载显示Native组件。
占位组件和其他ArkTS内置组件使用方法相同。
- import { NodeContent } from '@kit.ArkUI';
-
- @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%')
- }
- }
占位组件可以通过相关接口在Native侧转化为挂载对象。
- ArkUI_NodeContentHandle contentHandle;
- OH_ArkUI_GetNodeContentFromNapiValue(env, args[0], &contentHandle);
挂载对象提供了相关挂载和卸载组件接口。
- OH_ArkUI_NodeContent_AddNode(handle_, myNativeNode);
- OH_ArkUI_NodeContent_RemoveNode(handle_, myNativeNode);
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 '@kit.ArkUI';
-
- @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%')
- }
- }
使用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;
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); }
在NativeEntry.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
对应实现文件。
- // 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
使用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
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
- // 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
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
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
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
完善步骤3的CreateTextListExample函数,实现Native文本列表的创建和挂载显示。
- // NativeEntry.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)资料用来跟着学习是非常有必要的。
点击→【纯血版鸿蒙全套最新学习资料】希望这一份鸿蒙学习资料能够给大家带来帮助!~
鸿蒙(HarmonyOS NEXT)最新学习路线
该路线图包含基础技能、就业必备技能、多媒体技术、六大电商APP、进阶高级技能、实战就业级设备开发,不仅补充了华为官网未涉及的解决方案
路线图适合人群:
IT开发人员:想要拓展职业边界
零基础小白:鸿蒙爱好者,希望从0到1学习,增加一项技能。
技术提升/进阶跳槽:发展瓶颈期,提升职场竞争力,快速掌握鸿蒙技术
2.视频学习资料+学习PDF文档
这份鸿蒙(HarmonyOS NEXT)资料包含了鸿蒙开发必掌握的核心知识要点,内容包含了(ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、音频、视频、WebGL、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、(南向驱动、嵌入式等)鸿蒙项目实战等等)鸿蒙(HarmonyOS NEXT)技术知识点。
HarmonyOS Next 最新全套视频教程
大厂面试必问面试题
鸿蒙南向开发技术
鸿蒙APP开发必备
《鸿蒙 (OpenHarmony)开发基础到实战手册》
OpenHarmony北向、南向开发环境搭建
《鸿蒙开发基础》
《鸿蒙开发进阶》
《鸿蒙进阶实战》
总结
总的来说,华为鸿蒙不再兼容安卓,对程序员来说是一个挑战,也是一个机会。只有积极应对变化,不断学习和提升自己,才能在这个变革的时代中立于不败之地。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。