1# 接入ArkTS页面 2 3 4## 占位组件 5 6使用NDK接口构建UI界面时,需要在ArkTS页面创建用于挂载NDK接口创建组件的占位组件。占位组件类型为[ContentSlot](../reference/apis-arkui/arkui-ts/ts-components-contentSlot.md),ContentSlot能够绑定一个[NodeContent](../reference/apis-arkui/js-apis-arkui-NodeContent.md)对象,该对象可通过Node-API传递到Native侧挂载显示Native组件。 7 8- 占位组件和其他ArkTS内置组件使用方法相同。 9 ```ts 10 import { NodeContent } from '@kit.ArkUI'; 11 12 @Entry 13 @Component 14 struct Index { 15 // 初始化NodeContent对象。 16 private rootSlot = new NodeContent(); 17 @State @Watch('changeNativeFlag') showNative: boolean = false; 18 19 changeNativeFlag(): void { 20 if (this.showNative) { 21 // 传递NodeContent对象用于Native创建组件的挂载显示 22 nativeNode.createNativeRoot(this.rootSlot) 23 } else { 24 // 销毁NativeModule组件 25 nativeNode.destroyNativeRoot() 26 } 27 } 28 29 build() { 30 Column() { 31 Button(this.showNative ? "HideNativeUI" : "ShowNativeUI").onClick(() => { 32 this.showNative = !this.showNative 33 }) 34 Row() { 35 // 将NodeContent和ContentSlot占位组件绑定。 36 ContentSlot(this.rootSlot) 37 }.layoutWeight(1) 38 } 39 .width('100%') 40 .height('100%') 41 } 42 } 43 ``` 44 45- 占位组件可以通过相关接口在Native侧转化为挂载对象。 46 ``` 47 ArkUI_NodeContentHandle contentHandle; 48 OH_ArkUI_GetNodeContentFromNapiValue(env, args[0], &contentHandle); 49 ``` 50 51- 挂载对象提供了相关挂载和卸载组件接口。 52 ``` 53 OH_ArkUI_NodeContent_AddNode(handle_, myNativeNode); 54 OH_ArkUI_NodeContent_RemoveNode(handle_, myNativeNode); 55 ``` 56 57 58## NDK组件模块 59 60NDK提供的UI组件能力如组件创建、树操作、属性设置、事件注册等是通过函数指针结构体(如[ArkUI_NativeNodeAPI_1](../reference/apis-arkui/_ark_u_i___native_node_a_p_i__1.md))进行暴露,该函数指针结构体可以通过[模块查询接口](../reference/apis-arkui/_ark_u_i___native_module.md#oh_arkui_getmoduleinterface)获取。 61 62``` 63ArkUI_NativeNodeAPI_1* arkUINativeNodeApi = nullptr; 64OH_ArkUI_GetModuleInterface(ARKUI_NATIVE_NODE, ArkUI_NativeNodeAPI_1, arkUINativeNodeApi); 65``` 66 67 68在获取到函数指针结构体后,可以使用该结构体内的函数实现相关UI组件操作。 69 70 71- 组件创建和销毁。 72 ``` 73 auto listNode = arkUINativeNodeApi->createNode(ARKUI_NODE_LIST); 74 arkUINativeNodeApi->disposeNode(listNode); 75 ``` 76 77 获取NDK接口支持的组件范围可以通过查询[ArkUI_NodeType](../reference/apis-arkui/_ark_u_i___native_module.md#arkui_nodetype)枚举值。 78 79- 组件树操作。 80 ``` 81 auto parent = arkUINativeNodeApi->createNode(ARKUI_NODE_STACK); 82 auto child = arkUINativeNodeApi->createNode(ARKUI_NODE_STACK); 83 arkUINativeNodeApi->addChild(parent, child); 84 arkUINativeNodeApi->removeChild(parent, child); 85 ``` 86 87- 属性设置。 88 ``` 89 auto stack = arkUINativeNodeApi->createNode(ARKUI_NODE_STACK); 90 ArkUI_NumberValue value[] = {{.f32 = 100}}; 91 ArkUI_AttributeItem item = {value, 1}; 92 arkUINativeNodeApi->setAttribute(stack, NODE_WIDTH, &item); 93 ArkUI_NumberValue value[] = {{.u32 = 0xff112233}}; 94 ArkUI_AttributeItem item = {value, 1}; 95 arkUINativeNodeApi->setAttribute(stack, NODE_BACKGROUND_COLOR, &item); 96 ``` 97 98 获取NDK接口支持的属性范围可以通过查询[ArkUI_NodeAttributeType](../reference/apis-arkui/_ark_u_i___native_module.md#arkui_nodeattributetype)枚举值。 99 100- 事件注册。 101 ``` 102 auto stack = arkUINativeNodeApi->createNode(ARKUI_NODE_STACK); 103 arkUINativeNodeApi->addNodeEventReceiver(stack, [](ArkUI_NodeEvent* event){ 104 // process event 105 }); 106 arkUINativeNodeApi->registerNodeEvent(stack, NODE_ON_CLICK, 0, nullptr); 107 ``` 108 109 获取NDK接口支持的事件范围可以通过查询[ArkUI_NodeEventType](../reference/apis-arkui/_ark_u_i___native_module.md#arkui_nodeeventtype)枚举值。 110 111 112## 示例 113 114下面的示例展示了如何使用ContentSlot挂载Native侧的文本列表。 115 116**图1** Native文本列表 117 118 119 1201. 在ArkTS页面上声明用于Native页面挂载的占位组件,并在页面创建时通知Native侧创建文本列表。 121 ```ts 122 import nativeNode from 'libentry.so'; 123 import { NodeContent } from '@kit.ArkUI'; 124 125 @Entry 126 @Component 127 struct Index { 128 // 初始化NodeContent对象。 129 private rootSlot = new NodeContent(); 130 @State @Watch('changeNativeFlag') showNative: boolean = false; 131 132 changeNativeFlag(): void { 133 if (this.showNative) { 134 // 传递NodeContent对象用于Native创建组件的挂载显示 135 nativeNode.createNativeRoot(this.rootSlot) 136 } else { 137 // 销毁NativeModule组件 138 nativeNode.destroyNativeRoot() 139 } 140 } 141 142 build() { 143 Column() { 144 Button(this.showNative ? "HideNativeUI" : "ShowNativeUI").onClick(() => { 145 this.showNative = !this.showNative 146 }) 147 Row() { 148 // 将NodeContent和ContentSlot占位组件绑定。 149 ContentSlot(this.rootSlot) 150 }.layoutWeight(1) 151 } 152 .width('100%') 153 .height('100%') 154 } 155 } 156 ``` 157 1582. 使用Native模板创建工程,并在Native侧提供Node-API的桥接方法,实现ArkTS侧的NativeNode模块接口。 159 接口声明。 160 ```ts 161 // entry/src/main/cpp/types/libentry/Index.d.ts 162 163 export const createNativeRoot: (content: Object) => void; 164 export const destroyNativeRoot: () => void; 165 ``` 166 167 Native实现。 168 ```cpp 169 // entry/src/main/cpp/napi_init.cpp 170 #include "napi/native_api.h" 171 #include "NativeEntry.h" 172 173 EXTERN_C_START 174 static napi_value Init(napi_env env, napi_value exports) { 175 // 绑定Native侧的创建组件和销毁组件。 176 napi_property_descriptor desc[] = { 177 {"createNativeRoot", nullptr, NativeModule::CreateNativeRoot, nullptr, nullptr, nullptr, napi_default, nullptr}, 178 {"destroyNativeRoot", nullptr, NativeModule::DestroyNativeRoot, nullptr, nullptr, nullptr, napi_default, nullptr}}; 179 napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc); 180 return exports; 181 } 182 EXTERN_C_END 183 184 static napi_module demoModule = { 185 .nm_version = 1, 186 .nm_flags = 0, 187 .nm_filename = nullptr, 188 .nm_register_func = Init, 189 .nm_modname = "entry", 190 .nm_priv = ((void *)0), 191 .reserved = {0}, 192 }; 193 194 extern "C" __attribute__((constructor)) void RegisterEntryModule(void) { napi_module_register(&demoModule); } 195 ``` 196 1973. 在NativeEntry.h文件中创建Native界面。 198 ```c 199 // NativeEntry.h 200 201 #ifndef MYAPPLICATION_NATIVEENTRY_H 202 #define MYAPPLICATION_NATIVEENTRY_H 203 204 #include <js_native_api_types.h> 205 206 namespace NativeModule { 207 208 napi_value CreateNativeRoot(napi_env env, napi_callback_info info); 209 210 napi_value DestroyNativeRoot(napi_env env, napi_callback_info info); 211 212 // 管理Native组件的生命周期和内存。 213 class NativeEntry { 214 public: 215 static NativeEntry *GetInstance() { 216 static NativeEntry nativeEntry; 217 return &nativeEntry; 218 } 219 220 void SetContentHandle(ArkUI_NodeContentHandle handle) { 221 handle_ = handle; 222 } 223 224 void SetRootNode(const std::shared_ptr<ArkUIBaseNode> &baseNode) { 225 root_ = baseNode; 226 // 添加Native组件到NodeContent上用于挂载显示。 227 OH_ArkUI_NodeContent_AddNode(handle_, root_->GetHandle()); 228 } 229 void DisposeRootNode() { 230 // 从NodeContent上卸载组件并销毁Native组件。 231 OH_ArkUI_NodeContent_RemoveNode(handle_, root_->GetHandle()); 232 root_.reset(); 233 } 234 235 private: 236 std::shared_ptr<ArkUIBaseNode> root_; 237 ArkUI_NodeContentHandle handle_; 238 }; 239 240 } // namespace NativeModule 241 242 #endif // MYAPPLICATION_NATIVEENTRY_H 243 ``` 244 245 对应实现文件。 246 ```cpp 247 // NativeEntry.cpp 248 #include "NativeEntry.h" 249 250 #include <arkui/native_node_napi.h> 251 #include <hilog/log.h> 252 #include <js_native_api.h> 253 254 namespace NativeModule { 255 256 napi_value CreateNativeRoot(napi_env env, napi_callback_info info) { 257 size_t argc = 1; 258 napi_value args[1] = {nullptr}; 259 260 napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); 261 262 // 获取NodeContent 263 ArkUI_NodeContentHandle contentHandle; 264 OH_ArkUI_GetNodeContentFromNapiValue(env, args[0], &contentHandle); 265 NativeEntry::GetInstance()->SetContentHandle(contentHandle); 266 267 // 创建文本列表 268 auto list = CreateTextListExample(); 269 270 // 保持Native侧对象到管理类中,维护生命周期。 271 NativeEntry::GetInstance()->SetRootNode(list); 272 return nullptr; 273 } 274 275 napi_value DestroyNativeRoot(napi_env env, napi_callback_info info) { 276 // 从管理类中释放Native侧对象。 277 NativeEntry::GetInstance()->DisposeRootNode(); 278 return nullptr; 279 } 280 281 } // namespace NativeModule 282 ``` 283 284 使用NDK 提供的C接口需要在CMakeLists.txt 中增加libace_ndk.z.so 的引用,如下所示,其中entry为工程导出的动态库名称,如当前示例使用的是默认的名称 libentry.so。 285 ``` 286 target_link_libraries(entry PUBLIC libace_napi.z.so libace_ndk.z.so) 287 ``` 288 2894. 由于NDK接口提供的是C接口,为了使用面向对象的方式简化编程和工程管理,这里建议使用C++进行二次封装,下面示例代码展示了示例界面中所需的列表,文本组件封装类。 290 1)获取ArkUI在NDK接口的入口模块[ArkUI_NativeNodeAPI_1](../reference/apis-arkui/_ark_u_i___native_node_a_p_i__1.md),该结构体模块提供了一系列组件创建、树构建、属性设置和事件注册等函数指针。 291 ```c 292 // NativeModule.h 293 // 提供获取ArkUI在Native侧模块的封装接口 294 295 #ifndef MYAPPLICATION_NATIVEMODULE_H 296 #define MYAPPLICATION_NATIVEMODULE_H 297 298 #include <arkui/native_node.h> 299 #include <functional> 300 #include <cassert> 301 302 #include <arkui/native_interface.h> 303 304 namespace NativeModule { 305 306 class NativeModuleInstance { 307 public: 308 static NativeModuleInstance *GetInstance() { 309 static NativeModuleInstance instance; 310 return &instance; 311 } 312 313 NativeModuleInstance() { 314 // 获取NDK接口的函数指针结构体对象,用于后续操作。 315 OH_ArkUI_GetModuleInterface(ARKUI_NATIVE_NODE, ArkUI_NativeNodeAPI_1, arkUINativeNodeApi_); 316 assert(arkUINativeNodeApi_); 317 } 318 // 暴露给其他模块使用。 319 ArkUI_NativeNodeAPI_1 *GetNativeNodeAPI() { return arkUINativeNodeApi_; } 320 321 private: 322 ArkUI_NativeNodeAPI_1 *arkUINativeNodeApi_ = nullptr; 323 }; 324 325 } // namespace NativeModule 326 327 #endif // MYAPPLICATION_NATIVEMODULE_H 328 ``` 329 330 2)提供列表,文本组件的基类对象,用于封装通用属性和事件。 331 332 ```c 333 // ArkUIBaseNode.h 334 // 提供组件树操作的基类。 335 336 #ifndef MYAPPLICATION_ARKUIBASENODE_H 337 #define MYAPPLICATION_ARKUIBASENODE_H 338 339 #include <arkui/native_type.h> 340 #include <list> 341 #include <memory> 342 343 #include "NativeModule.h" 344 345 namespace NativeModule { 346 347 class ArkUIBaseNode { 348 public: 349 explicit ArkUIBaseNode(ArkUI_NodeHandle handle) 350 : handle_(handle), nativeModule_(NativeModuleInstance::GetInstance()->GetNativeNodeAPI()) {} 351 352 virtual ~ArkUIBaseNode() { 353 // 封装析构函数,实现子节点移除功能。 354 if (!children_.empty()) { 355 for (const auto& child : children_) { 356 nativeModule_->removeChild(handle_, child->GetHandle()); 357 } 358 children_.clear(); 359 } 360 // 封装析构函数,统一回收节点资源。 361 nativeModule_->disposeNode(handle_); 362 } 363 364 void AddChild(const std::shared_ptr<ArkUIBaseNode> &child) { 365 children_.emplace_back(child); 366 OnAddChild(child); 367 } 368 369 void RemoveChild(const std::shared_ptr<ArkUIBaseNode> &child) { 370 children_.remove(child); 371 OnRemoveChild(child); 372 } 373 374 void InsertChild(const std::shared_ptr<ArkUIBaseNode> &child, int32_t index) { 375 if (index >= children_.size()) { 376 AddChild(child); 377 } else { 378 auto iter = children_.begin(); 379 std::advance(iter, index); 380 children_.insert(iter, child); 381 OnInsertChild(child, index); 382 } 383 } 384 385 ArkUI_NodeHandle GetHandle() const { return handle_; } 386 387 protected: 388 // 针对父容器子类需要重载下面的函数,实现组件挂载和卸载。 389 virtual void OnAddChild(const std::shared_ptr<ArkUIBaseNode> &child) {} 390 virtual void OnRemoveChild(const std::shared_ptr<ArkUIBaseNode> &child) {} 391 virtual void OnInsertChild(const std::shared_ptr<ArkUIBaseNode> &child, int32_t index) {} 392 393 ArkUI_NodeHandle handle_; 394 ArkUI_NativeNodeAPI_1 *nativeModule_ = nullptr; 395 396 private: 397 std::list<std::shared_ptr<ArkUIBaseNode>> children_; 398 }; 399 } // namespace NativeModule 400 401 #endif // MYAPPLICATION_ARKUIBASENODE_H 402 ``` 403 404 ```c 405 // ArkUINode.h 406 // 提供通用属性和事件的封装。 407 408 #ifndef MYAPPLICATION_ARKUINODE_H 409 #define MYAPPLICATION_ARKUINODE_H 410 411 #include "ArkUIBaseNode.h" 412 #include "NativeModule.h" 413 #include <arkui/native_node.h> 414 #include <arkui/native_type.h> 415 416 namespace NativeModule { 417 418 class ArkUINode : public ArkUIBaseNode { 419 public: 420 explicit ArkUINode(ArkUI_NodeHandle handle) : ArkUIBaseNode(handle) {} 421 422 ~ArkUINode() override {} 423 424 // NDK相关通用属性调用封装 425 void SetWidth(float width) { 426 assert(handle_); 427 ArkUI_NumberValue value[] = {{.f32 = width}}; 428 ArkUI_AttributeItem item = {value, 1}; 429 nativeModule_->setAttribute(handle_, NODE_WIDTH, &item); 430 } 431 void SetPercentWidth(float percent) { 432 assert(handle_); 433 ArkUI_NumberValue value[] = {{.f32 = percent}}; 434 ArkUI_AttributeItem item = {value, 1}; 435 nativeModule_->setAttribute(handle_, NODE_WIDTH_PERCENT, &item); 436 } 437 void SetHeight(float height) { 438 assert(handle_); 439 ArkUI_NumberValue value[] = {{.f32 = height}}; 440 ArkUI_AttributeItem item = {value, 1}; 441 nativeModule_->setAttribute(handle_, NODE_HEIGHT, &item); 442 } 443 void SetPercentHeight(float percent) { 444 assert(handle_); 445 ArkUI_NumberValue value[] = {{.f32 = percent}}; 446 ArkUI_AttributeItem item = {value, 1}; 447 nativeModule_->setAttribute(handle_, NODE_HEIGHT_PERCENT, &item); 448 } 449 void SetBackgroundColor(uint32_t color) { 450 assert(handle_); 451 ArkUI_NumberValue value[] = {{.u32 = color}}; 452 ArkUI_AttributeItem item = {value, 1}; 453 nativeModule_->setAttribute(handle_, NODE_BACKGROUND_COLOR, &item); 454 } 455 456 protected: 457 // 组件树操作的实现类对接。 458 void OnAddChild(const std::shared_ptr<ArkUIBaseNode> &child) override { 459 nativeModule_->addChild(handle_, child->GetHandle()); 460 } 461 void OnRemoveChild(const std::shared_ptr<ArkUIBaseNode> &child) override { 462 nativeModule_->removeChild(handle_, child->GetHandle()); 463 } 464 void OnInsertChild(const std::shared_ptr<ArkUIBaseNode> &child, int32_t index) override { 465 nativeModule_->insertChildAt(handle_, child->GetHandle(), index); 466 } 467 }; 468 } // namespace NativeModule 469 470 #endif // MYAPPLICATION_ARKUINODE_H 471 ``` 472 473 3)实现列表组件。 474 475 ```c 476 // ArkUIListNode.h 477 // 提供列表组件的封装。 478 479 #ifndef MYAPPLICATION_ARKUILISTNODE_H 480 #define MYAPPLICATION_ARKUILISTNODE_H 481 482 #include "ArkUINode.h" 483 484 namespace NativeModule { 485 class ArkUIListNode : public ArkUINode { 486 public: 487 ArkUIListNode() 488 : ArkUINode((NativeModuleInstance::GetInstance()->GetNativeNodeAPI())->createNode(ARKUI_NODE_LIST)) {} // 创建ArkUI的列表组件。 489 490 ~ArkUIListNode() override {} 491 // List组件的属性NDK接口封装。 492 void SetScrollBarState(bool isShow) { 493 assert(handle_); 494 ArkUI_ScrollBarDisplayMode displayMode = 495 isShow ? ARKUI_SCROLL_BAR_DISPLAY_MODE_ON : ARKUI_SCROLL_BAR_DISPLAY_MODE_OFF; 496 ArkUI_NumberValue value[] = {{.i32 = displayMode}}; 497 ArkUI_AttributeItem item = {value, 1}; 498 nativeModule_->setAttribute(handle_, NODE_SCROLL_BAR_DISPLAY_MODE, &item); 499 } 500 }; 501 } // namespace NativeModule 502 503 #endif // MYAPPLICATION_ARKUILISTNODE_H 504 ``` 505 506 4)实现列表项组件。 507 508 ```c 509 // ArkUIListItemNode.h 510 // 提供列表项的封装类。 511 512 #ifndef MYAPPLICATION_ARKUISTACKNODE_H 513 #define MYAPPLICATION_ARKUISTACKNODE_H 514 515 #include "ArkUINode.h" 516 517 namespace NativeModule { 518 class ArkUIListItemNode : public ArkUINode { 519 public: 520 ArkUIListItemNode() 521 : ArkUINode((NativeModuleInstance::GetInstance()->GetNativeNodeAPI())->createNode(ARKUI_NODE_LIST_ITEM)) {} 522 }; 523 } // namespace NativeModule 524 525 #endif // MYAPPLICATION_ARKUISTACKNODE_H 526 ``` 527 528 5)实现文本组件。 529 530 ```c 531 // ArkUITextNode.h 532 // 实现文本组件的封装类。 533 534 #ifndef MYAPPLICATION_ARKUITEXTNODE_H 535 #define MYAPPLICATION_ARKUITEXTNODE_H 536 537 #include "ArkUINode.h" 538 539 #include <string> 540 541 namespace NativeModule { 542 class ArkUITextNode : public ArkUINode { 543 public: 544 ArkUITextNode() 545 : ArkUINode((NativeModuleInstance::GetInstance()->GetNativeNodeAPI())->createNode(ARKUI_NODE_TEXT)) {} 546 // 文本属性NDK接口封装。 547 void SetFontSize(float fontSize) { 548 assert(handle_); 549 ArkUI_NumberValue value[] = {{.f32 = fontSize}}; 550 ArkUI_AttributeItem item = {value, 1}; 551 nativeModule_->setAttribute(handle_, NODE_FONT_SIZE, &item); 552 } 553 void SetFontColor(uint32_t color) { 554 assert(handle_); 555 ArkUI_NumberValue value[] = {{.u32 = color}}; 556 ArkUI_AttributeItem item = {value, 1}; 557 nativeModule_->setAttribute(handle_, NODE_FONT_COLOR, &item); 558 } 559 void SetTextContent(const std::string &content) { 560 assert(handle_); 561 ArkUI_AttributeItem item = {nullptr, 0, content.c_str()}; 562 nativeModule_->setAttribute(handle_, NODE_TEXT_CONTENT, &item); 563 } 564 void SetTextAlign(ArkUI_TextAlignment align) { 565 assert(handle_); 566 ArkUI_NumberValue value[] = {{.i32 = align}}; 567 ArkUI_AttributeItem item = {value, 1}; 568 nativeModule_->setAttribute(handle_, NODE_TEXT_ALIGN, &item); 569 } 570 }; 571 } // namespace NativeModule 572 573 #endif // MYAPPLICATION_ARKUITEXTNODE_H 574 ``` 575 5765. 完善步骤3的CreateTextListExample函数,实现Native文本列表的创建和挂载显示。 577 ```c 578 // NormalTextListExample.h 579 // 自定义NDK接口入口函数。 580 581 #ifndef MYAPPLICATION_NORMALTEXTLISTEXAMPLE_H 582 #define MYAPPLICATION_NORMALTEXTLISTEXAMPLE_H 583 584 #include "ArkUIBaseNode.h" 585 #include "ArkUIListItemNode.h" 586 #include "ArkUIListNode.h" 587 #include "ArkUITextNode.h" 588 #include <hilog/log.h> 589 590 namespace NativeModule { 591 592 std::shared_ptr<ArkUIBaseNode> CreateTextListExample() { 593 // 创建组件并挂载 594 // 1:使用智能指针创建List组件。 595 auto list = std::make_shared<ArkUIListNode>(); 596 list->SetPercentWidth(1); 597 list->SetPercentHeight(1); 598 // 2:创建ListItem子组件并挂载到List上。 599 for (int32_t i = 0; i < 30; ++i) { 600 auto listItem = std::make_shared<ArkUIListItemNode>(); 601 auto textNode = std::make_shared<ArkUITextNode>(); 602 textNode->SetTextContent(std::to_string(i)); 603 textNode->SetFontSize(16); 604 textNode->SetPercentWidth(1); 605 textNode->SetHeight(100); 606 textNode->SetBackgroundColor(0xFFfffacd); 607 textNode->SetTextAlign(ARKUI_TEXT_ALIGNMENT_CENTER); 608 listItem->AddChild(textNode); 609 list->AddChild(listItem); 610 } 611 return list; 612 } 613 } // namespace NativeModule 614 615 #endif // MYAPPLICATION_NORMALTEXTLISTEXAMPLE_H 616 ``` 617