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 249 #include <arkui/native_node_napi.h> 250 #include <hilog/log.h> 251 #include <js_native_api.h> 252 #include "NativeEntry.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 285 使用NDK 提供的C接口需要在CMakeLists.txt 中增加libace_ndk.z.so 的引用,如下所示,其中entry为工程导出的动态库名称,如当前示例使用的是默认的名称 libentry.so。 286 287 ``` 288 target_link_libraries(entry PUBLIC libace_napi.z.so libace_ndk.z.so) 289 ``` 290 2914. 由于NDK接口提供的是C接口,为了使用面向对象的方式简化编程和工程管理,这里建议使用C++进行二次封装,下面示例代码展示了示例界面中所需的列表,文本组件封装类。 292 293 1)获取ArkUI在NDK接口的入口模块[ArkUI_NativeNodeAPI_1](../reference/apis-arkui/_ark_u_i___native_node_a_p_i__1.md),该结构体模块提供了一系列组件创建、树构建、属性设置和事件注册等函数指针。 294 295 ```c 296 // NativeModule.h 297 // 提供获取ArkUI在Native侧模块的封装接口 298 299 #ifndef MYAPPLICATION_NATIVEMODULE_H 300 #define MYAPPLICATION_NATIVEMODULE_H 301 302 #include <arkui/native_node.h> 303 #include <functional> 304 #include <cassert> 305 306 #include <arkui/native_interface.h> 307 308 namespace NativeModule { 309 310 class NativeModuleInstance { 311 public: 312 static NativeModuleInstance *GetInstance() { 313 static NativeModuleInstance instance; 314 return &instance; 315 } 316 317 NativeModuleInstance() { 318 // 获取NDK接口的函数指针结构体对象,用于后续操作。 319 OH_ArkUI_GetModuleInterface(ARKUI_NATIVE_NODE, ArkUI_NativeNodeAPI_1, arkUINativeNodeApi_); 320 assert(arkUINativeNodeApi_); 321 } 322 // 暴露给其他模块使用。 323 ArkUI_NativeNodeAPI_1 *GetNativeNodeAPI() { return arkUINativeNodeApi_; } 324 325 private: 326 ArkUI_NativeNodeAPI_1 *arkUINativeNodeApi_ = nullptr; 327 }; 328 329 } // namespace NativeModule 330 331 #endif // MYAPPLICATION_NATIVEMODULE_H 332 ``` 333 334 2)提供列表,文本组件的基类对象,用于封装通用属性和事件。 335 336 ```c 337 // ArkUIBaseNode.h 338 // 提供组件树操作的基类。 339 340 #ifndef MYAPPLICATION_ARKUIBASENODE_H 341 #define MYAPPLICATION_ARKUIBASENODE_H 342 343 #include <arkui/native_type.h> 344 #include <list> 345 #include <memory> 346 347 #include "NativeModule.h" 348 349 namespace NativeModule { 350 351 class ArkUIBaseNode { 352 public: 353 explicit ArkUIBaseNode(ArkUI_NodeHandle handle) 354 : handle_(handle), nativeModule_(NativeModuleInstance::GetInstance()->GetNativeNodeAPI()) {} 355 356 virtual ~ArkUIBaseNode() { 357 // 封装析构函数,实现子节点移除功能。 358 if (!children_.empty()) { 359 for (const auto& child : children_) { 360 nativeModule_->removeChild(handle_, child->GetHandle()); 361 } 362 children_.clear(); 363 } 364 // 封装析构函数,统一回收节点资源。 365 nativeModule_->disposeNode(handle_); 366 } 367 368 void AddChild(const std::shared_ptr<ArkUIBaseNode> &child) { 369 children_.emplace_back(child); 370 OnAddChild(child); 371 } 372 373 void RemoveChild(const std::shared_ptr<ArkUIBaseNode> &child) { 374 children_.remove(child); 375 OnRemoveChild(child); 376 } 377 378 void InsertChild(const std::shared_ptr<ArkUIBaseNode> &child, int32_t index) { 379 if (index >= children_.size()) { 380 AddChild(child); 381 } else { 382 auto iter = children_.begin(); 383 std::advance(iter, index); 384 children_.insert(iter, child); 385 OnInsertChild(child, index); 386 } 387 } 388 389 ArkUI_NodeHandle GetHandle() const { return handle_; } 390 391 protected: 392 // 针对父容器子类需要重载下面的函数,实现组件挂载和卸载。 393 virtual void OnAddChild(const std::shared_ptr<ArkUIBaseNode> &child) {} 394 virtual void OnRemoveChild(const std::shared_ptr<ArkUIBaseNode> &child) {} 395 virtual void OnInsertChild(const std::shared_ptr<ArkUIBaseNode> &child, int32_t index) {} 396 397 ArkUI_NodeHandle handle_; 398 ArkUI_NativeNodeAPI_1 *nativeModule_ = nullptr; 399 400 private: 401 std::list<std::shared_ptr<ArkUIBaseNode>> children_; 402 }; 403 } // namespace NativeModule 404 405 #endif // MYAPPLICATION_ARKUIBASENODE_H 406 ``` 407 408 ```c 409 // ArkUINode.h 410 // 提供通用属性和事件的封装。 411 412 #ifndef MYAPPLICATION_ARKUINODE_H 413 #define MYAPPLICATION_ARKUINODE_H 414 415 #include "ArkUIBaseNode.h" 416 #include "NativeModule.h" 417 #include <arkui/native_node.h> 418 #include <arkui/native_type.h> 419 420 namespace NativeModule { 421 422 class ArkUINode : public ArkUIBaseNode { 423 public: 424 explicit ArkUINode(ArkUI_NodeHandle handle) : ArkUIBaseNode(handle) {} 425 426 ~ArkUINode() override {} 427 428 // NDK相关通用属性调用封装 429 void SetWidth(float width) { 430 assert(handle_); 431 ArkUI_NumberValue value[] = {{.f32 = width}}; 432 ArkUI_AttributeItem item = {value, 1}; 433 nativeModule_->setAttribute(handle_, NODE_WIDTH, &item); 434 } 435 void SetPercentWidth(float percent) { 436 assert(handle_); 437 ArkUI_NumberValue value[] = {{.f32 = percent}}; 438 ArkUI_AttributeItem item = {value, 1}; 439 nativeModule_->setAttribute(handle_, NODE_WIDTH_PERCENT, &item); 440 } 441 void SetHeight(float height) { 442 assert(handle_); 443 ArkUI_NumberValue value[] = {{.f32 = height}}; 444 ArkUI_AttributeItem item = {value, 1}; 445 nativeModule_->setAttribute(handle_, NODE_HEIGHT, &item); 446 } 447 void SetPercentHeight(float percent) { 448 assert(handle_); 449 ArkUI_NumberValue value[] = {{.f32 = percent}}; 450 ArkUI_AttributeItem item = {value, 1}; 451 nativeModule_->setAttribute(handle_, NODE_HEIGHT_PERCENT, &item); 452 } 453 void SetBackgroundColor(uint32_t color) { 454 assert(handle_); 455 ArkUI_NumberValue value[] = {{.u32 = color}}; 456 ArkUI_AttributeItem item = {value, 1}; 457 nativeModule_->setAttribute(handle_, NODE_BACKGROUND_COLOR, &item); 458 } 459 460 protected: 461 // 组件树操作的实现类对接。 462 void OnAddChild(const std::shared_ptr<ArkUIBaseNode> &child) override { 463 nativeModule_->addChild(handle_, child->GetHandle()); 464 } 465 void OnRemoveChild(const std::shared_ptr<ArkUIBaseNode> &child) override { 466 nativeModule_->removeChild(handle_, child->GetHandle()); 467 } 468 void OnInsertChild(const std::shared_ptr<ArkUIBaseNode> &child, int32_t index) override { 469 nativeModule_->insertChildAt(handle_, child->GetHandle(), index); 470 } 471 }; 472 } // namespace NativeModule 473 474 #endif // MYAPPLICATION_ARKUINODE_H 475 ``` 476 477 3)实现列表组件。 478 479 ```c 480 // ArkUIListNode.h 481 // 提供列表组件的封装。 482 483 #ifndef MYAPPLICATION_ARKUILISTNODE_H 484 #define MYAPPLICATION_ARKUILISTNODE_H 485 486 #include "ArkUINode.h" 487 488 namespace NativeModule { 489 class ArkUIListNode : public ArkUINode { 490 public: 491 ArkUIListNode() 492 : ArkUINode((NativeModuleInstance::GetInstance()->GetNativeNodeAPI())->createNode(ARKUI_NODE_LIST)) {} // 创建ArkUI的列表组件。 493 494 ~ArkUIListNode() override {} 495 // List组件的属性NDK接口封装。 496 void SetScrollBarState(bool isShow) { 497 assert(handle_); 498 ArkUI_ScrollBarDisplayMode displayMode = 499 isShow ? ARKUI_SCROLL_BAR_DISPLAY_MODE_ON : ARKUI_SCROLL_BAR_DISPLAY_MODE_OFF; 500 ArkUI_NumberValue value[] = {{.i32 = displayMode}}; 501 ArkUI_AttributeItem item = {value, 1}; 502 nativeModule_->setAttribute(handle_, NODE_SCROLL_BAR_DISPLAY_MODE, &item); 503 } 504 }; 505 } // namespace NativeModule 506 507 #endif // MYAPPLICATION_ARKUILISTNODE_H 508 ``` 509 510 4)实现列表项组件。 511 512 ```c 513 // ArkUIListItemNode.h 514 // 提供列表项的封装类。 515 516 #ifndef MYAPPLICATION_ARKUISTACKNODE_H 517 #define MYAPPLICATION_ARKUISTACKNODE_H 518 519 #include "ArkUINode.h" 520 521 namespace NativeModule { 522 class ArkUIListItemNode : public ArkUINode { 523 public: 524 ArkUIListItemNode() 525 : ArkUINode((NativeModuleInstance::GetInstance()->GetNativeNodeAPI())->createNode(ARKUI_NODE_LIST_ITEM)) {} 526 }; 527 } // namespace NativeModule 528 529 #endif // MYAPPLICATION_ARKUISTACKNODE_H 530 ``` 531 532 5)实现文本组件。 533 534 ```c 535 // ArkUITextNode.h 536 // 实现文本组件的封装类。 537 538 #ifndef MYAPPLICATION_ARKUITEXTNODE_H 539 #define MYAPPLICATION_ARKUITEXTNODE_H 540 541 #include "ArkUINode.h" 542 543 #include <string> 544 545 namespace NativeModule { 546 class ArkUITextNode : public ArkUINode { 547 public: 548 ArkUITextNode() 549 : ArkUINode((NativeModuleInstance::GetInstance()->GetNativeNodeAPI())->createNode(ARKUI_NODE_TEXT)) {} 550 // 文本属性NDK接口封装。 551 void SetFontSize(float fontSize) { 552 assert(handle_); 553 ArkUI_NumberValue value[] = {{.f32 = fontSize}}; 554 ArkUI_AttributeItem item = {value, 1}; 555 nativeModule_->setAttribute(handle_, NODE_FONT_SIZE, &item); 556 } 557 void SetFontColor(uint32_t color) { 558 assert(handle_); 559 ArkUI_NumberValue value[] = {{.u32 = color}}; 560 ArkUI_AttributeItem item = {value, 1}; 561 nativeModule_->setAttribute(handle_, NODE_FONT_COLOR, &item); 562 } 563 void SetTextContent(const std::string &content) { 564 assert(handle_); 565 ArkUI_AttributeItem item = {nullptr, 0, content.c_str()}; 566 nativeModule_->setAttribute(handle_, NODE_TEXT_CONTENT, &item); 567 } 568 void SetTextAlign(ArkUI_TextAlignment align) { 569 assert(handle_); 570 ArkUI_NumberValue value[] = {{.i32 = align}}; 571 ArkUI_AttributeItem item = {value, 1}; 572 nativeModule_->setAttribute(handle_, NODE_TEXT_ALIGN, &item); 573 } 574 }; 575 } // namespace NativeModule 576 577 #endif // MYAPPLICATION_ARKUITEXTNODE_H 578 ``` 579 5805. 完善步骤3的CreateTextListExample函数,实现Native文本列表的创建和挂载显示。 581 ```c 582 // NormalTextListExample.h 583 // 自定义NDK接口入口函数。 584 585 #ifndef MYAPPLICATION_NORMALTEXTLISTEXAMPLE_H 586 #define MYAPPLICATION_NORMALTEXTLISTEXAMPLE_H 587 588 #include "ArkUIBaseNode.h" 589 #include "ArkUIListItemNode.h" 590 #include "ArkUIListNode.h" 591 #include "ArkUITextNode.h" 592 #include <hilog/log.h> 593 594 namespace NativeModule { 595 596 std::shared_ptr<ArkUIBaseNode> CreateTextListExample() { 597 // 创建组件并挂载 598 // 1:使用智能指针创建List组件。 599 auto list = std::make_shared<ArkUIListNode>(); 600 list->SetPercentWidth(1); 601 list->SetPercentHeight(1); 602 // 2:创建ListItem子组件并挂载到List上。 603 for (int32_t i = 0; i < 30; ++i) { 604 auto listItem = std::make_shared<ArkUIListItemNode>(); 605 auto textNode = std::make_shared<ArkUITextNode>(); 606 textNode->SetTextContent(std::to_string(i)); 607 textNode->SetFontSize(16); 608 textNode->SetPercentWidth(1); 609 textNode->SetHeight(100); 610 textNode->SetBackgroundColor(0xFFfffacd); 611 textNode->SetTextAlign(ARKUI_TEXT_ALIGNMENT_CENTER); 612 listItem->AddChild(textNode); 613 list->AddChild(listItem); 614 } 615 return list; 616 } 617 } // namespace NativeModule 618 619 #endif // MYAPPLICATION_NORMALTEXTLISTEXAMPLE_H 620 ```