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![text_list](figures/text_list.gif)
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.so286
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   ```