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