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