• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# ContentSlot:混合开发
2<!--Kit: ArkUI-->
3<!--Subsystem: ArkUI-->
4<!--Owner: @sd-wu-->
5<!--Designer: @sunbees-->
6<!--Tester: @liuli0427-->
7<!--Adviser: @zhang_yixin13-->
8
9用于渲染并管理Native层使用C-API创建的组件。
10
11支持混合模式开发。当容器为ArkTS组件,且子组件在Native侧创建时,推荐使用ContentSlot占位组件。
12
13## 接口
14
15### ArkTS侧接口
16
17| 接口名  | 描述 |
18| ------- | -------- |
19| ContentSlot(content: Content) | Content作为ContentSlot的管理器,通过Native侧提供的接口,可以注册并触发ContentSlot的上下树事件回调以及管理ContentSlot的子组件。 |
20
21```ts
22abstract class Content {
23}
24```
25
26### Native侧接口
27
28| 接口名 | 描述 |
29| -------- | -------- |
30|OH_ArkUI_NodeContent_RegisterCallback(ArkUI_NodeContentHandle content, ArkUI_NodeContentCallback callback)|向管理器Content上注册事件。|
31|OH_ArkUI_NodeContentEvent_GetEventType(ArkUI_NodeContentEvent* event)|获取Content上触发的事件类型。|
32|OH_ArkUI_NodeContent_AddNode(ArkUI_NodeContentHandle content, ArkUI_NodeHandle node)|在Content上添加子组件。|
33|OH_ArkUI_NodeContent_InsertNode(ArkUI_NodeContentHandle content, ArkUI_NodeHandle node, int32_t position)|在Content上插入子组件。|
34|OH_ArkUI_NodeContent_RemoveNode(ArkUI_NodeContentHandle content, ArkUI_NodeHandle node)|在Content上移除子组件。|
35|OH_ArkUI_GetNodeContentFromNapiValue(napi_env env, napi_value value, ArkUI_NodeContentHandle* content)|在Native侧获取ArkTS侧Content指针。|
36|OH_ArkUI_NodeContentEvent_GetNodeContentHandle(ArkUI_NodeContentEvent* event)|获取触发上下树事件的Content对象。|
37|OH_ArkUI_NodeContent_SetUserData(ArkUI_NodeContentHandle content, void* userData)|在Content上设置用户自定义属性。|
38|OH_ArkUI_NodeContent_GetUserData(ArkUI_NodeContentHandle content)|在Content上获取用户自定义属性。|
39|typedef enum {<br>   NOTE_CONTENT_EVENT_ON_ATTACH_TO_WINDOW = 0,<br>   NOTE_CONTENT_EVENT_ON_DETACH_FROM_WINDOW = 1,<br>} ArkUI_NodeContentEventType|Content上会触发的上树和下树事件类型。|
40
41## 开发实现
42
43### ArkTS侧代码实现
44
45```ts
46import { nativeNode } from 'libNativeNode.so'; // 开发者自己实现的so
47import { NodeContent } from '@kit.ArkUI';
48
49@Entry
50@Component
51struct Parent {
52  private nodeContent: Content = new NodeContent();
53
54  aboutToAppear() {
55    // 通过C-API创建节点,并添加到管理器nodeContent上
56    nativeNode.createNativeNode(this.nodeContent);
57  }
58
59  build() {
60    Column() {
61      // 显示nodeContent管理器里存放的Native侧的组件
62      ContentSlot(this.nodeContent)
63    }
64  }
65}
66```
67
68### Native侧代码实现
69Napi的基础开发知识请查看以下文档:[开发导读](../../napi/ndk-development-overview.md)。
70
71本章节描述实现ContentSlot相关逻辑代码。创建C侧组件的具体步骤,请参阅[使用NDK接口构建UI](../ndk-build-ui-overview.md)。
72
73```c++
74#include "napi/native_api.h"
75#include "arkui/native_type.h"
76#include "arkui/native_node.h"
77#include "arkui/native_node_napi.h"
78#include "arkui/native_interface.h"
79#include "hilog/log.h"
80
81ArkUI_NodeContentHandle nodeContentHandle_ = nullptr;
82ArkUI_NativeNodeAPI_1 *nodeAPI;
83const unsigned int LOG_PRINT_DOMAIN = 0xFF00;
84
85// 在Native侧创建一个宽高为480vp*480vp、背景色为0xFFFF0000(红色)的Column组件。对于更详细的节点树创建方法,请参考ArkUI API文档的C API章节。
86ArkUI_NodeHandle CreateNodeHandle() {
87    ArkUI_NodeHandle column = nodeAPI->createNode(ARKUI_NODE_COLUMN);
88    ArkUI_NumberValue value[] = {480};
89    ArkUI_AttributeItem item{value, 1};
90    nodeAPI->setAttribute(column, NODE_WIDTH, &item);
91    nodeAPI->setAttribute(column, NODE_HEIGHT, &item);
92    value[0].u32 = 0xFFFF0000;
93    nodeAPI->setAttribute(column, NODE_BACKGROUND_COLOR, &item);
94    return column;
95}
96
97// ArkTS侧createNativeNode方法在Native侧的具体实现
98napi_value CreateNativeNode(napi_env env, napi_callback_info info) {
99    // napi相关处理空指针&数据越界等问题
100    if ((env == nullptr) || (info == nullptr)) {
101        return nullptr;
102    }
103
104    size_t argc = 1;
105    napi_value args[1] = { nullptr };
106    if (napi_get_cb_info(env, info, &argc, args, nullptr, nullptr) != napi_ok) {
107        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Manager", "CreateNativeNode napi_get_cb_info failed");
108    }
109
110    if (argc != 1) {
111        return nullptr;
112    }
113
114    nodeAPI = reinterpret_cast<ArkUI_NativeNodeAPI_1 *>(
115        OH_ArkUI_QueryModuleInterfaceByName(ARKUI_NATIVE_NODE, "ArkUI_NativeNodeAPI_1"));
116
117    // 将nodeContentHandle_指向ArkTS侧传入的nodeContent
118    OH_ArkUI_GetNodeContentFromNapiValue(env, args[0], &nodeContentHandle_);
119
120    if (nodeAPI != nullptr) {
121        if (nodeAPI->createNode != nullptr && nodeAPI->addChild != nullptr) {
122            ArkUI_NodeHandle component;
123            // 创建C侧组件
124            component = CreateNodeHandle();
125            // 将组件添加到nodeContent管理器中
126            OH_ArkUI_NodeContent_AddNode(nodeContentHandle_, component);
127        }
128    }
129    return nullptr;
130}
131```
132
133### Native侧主要接口使用说明
134
135- 注册上下树事件,并通过事件获取对应的Content对象。
136
137  ```c++
138  auto nodeContentEvent = [](ArkUI_NodeContentEvent *event) {
139      ArkUI_NodeContentHandle content = OH_ArkUI_NodeContentEvent_GetNodeContentHandle(event);
140      // 针对不同content需要额外做的逻辑
141      if (OH_ArkUINodeContentEvent_GetEventType(event) = NODE_CONTENT_EVENT_ON_ATTACH_TO_WINDOW) {
142          // ContentSlot上树时需要触发的逻辑
143      } else if (OH_ArkUINodeContentEvent_GetEventType(event) = NODE_CONTENT_EVENT_ON_DETACH_FROM_WINDOW) {
144          // ContentSlot下树时需要触发的逻辑
145      };
146  };
147  // 将该事件注册到nodeContent上
148  OH_ArkUI_NodeContent_RegisterCallback(nodeContentHandle_, nodeContentEvent);
149  ```
150
151- 添加子组件。
152
153  ```c++
154  ArkUINodeHandle component;
155  component = CreateNodeHandle();
156  // 将组件添加到nodeContent管理器中
157  OH_ArkUI_NodeContent_AddNode(nodeContentHandle_, component);
158  ```
159
160- 插入子组件。
161
162  ```c++
163  ArkUINodeHandle component;
164  component = CreateNodeHandle();
165  // 将组件插入nodeContent管理器对应位置
166  OH_ArkUI_NodeContent_InsertNode(nodeContentHandle_, component, position);
167  ```
168
169- 删除子组件。
170
171  ```c++
172  // 在nodeContent中移除对应组件
173  OH_ArkUI_NodeContent_RemoveNode(nodeContentHandle_, component);
174  ```
175
176- 设置自定义属性。
177
178  ```c++
179  // 创建需要定义的自定义数据
180  void *userData = CreateUserData();
181  OH_ArkUI_NodeContent_SetUserData(nodeContentHandle_, userData);
182  ```
183
184- 获取自定义属性。
185
186  ```
187  void *userData = OH_ArkUI_NodeContent_GetUserData(nodeContentHandle_);
188  ```
189
190## 绑定规则说明
191
192如果将同一个Content对象绑定到多个ContentSlot组件,最终该Content的内容仅在最后一个绑定的ContentSlot中显示,其他ContentSlot将不显示任何内容。
193
194**原因说明:**
195
196Content与ContentSlot节点具有一对一的绑定关系。同一Content不能同时关联多个ContentSlot节点。如果尝试将同一Content挂载到多个ContentSlot节点,仅最后一次挂载生效,之前的ContentSlot节点将失去Content的关联,导致组件内容无法显示。
197
198若需在多个ContentSlot节点下显示相同内容,每个节点需创建单独的Content。示例如下:
199
200```typescript
201import nativeNode from 'libNativeNode.so'; // 开发者自己实现的so
202import { NodeContent } from '@kit.ArkUI';
203
204@Entry
205@Component
206struct Parent {
207  private nodeContent_1: Content = new NodeContent();
208  private nodeContent_2: Content = new NodeContent();
209
210  aboutToAppear() {
211    // 通过C-API创建节点,并添加到管理器nodeContent_1和nodeContent_2上
212    nativeNode.createNativeNode(this.nodeContent_1);
213    nativeNode.createNativeNode(this.nodeContent_2);
214  }
215
216  build() {
217    Column() {
218      ContentSlot(this.nodeContent_1) // nodeContent_1将被挂载到下一个Contentslot节点,此处无法显示
219      ContentSlot(this.nodeContent_1) // 正常显示
220      ContentSlot(this.nodeContent_2) // 正常显示
221    }
222  }
223}
224```
225
226