ContentSlot：混合开发
================

用于渲染并管理Native层使用C-API创建的组件。

支持[混合模式](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/complex-drawing-effect-c#混合模式)开发。当容器为ArkTS组件，且子组件在Native侧创建时，推荐使用ContentSlot占位组件。

![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/d4/v3/e-FG6e7WTqGYsG3zRNFC8Q/note_3.0-zh-cn.png?HW-CC-KV=V1&HW-CC-Date=20260709T132256Z&HW-CC-Expire=86400&HW-CC-Sign=C129C6240FA17478D09335AAC9B3996129CCE609F374B603D42FFC87B4AAF0FE)

ContentSlot从API version 12开始支持。

本文档仅为开发指南。组件接口规范见[ContentSlot API参数说明](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-components-contentslot)。

#### 接口

#### \[h2\]ArkTS侧接口

| 接口名 | 描述 |
| :-- | :-- |
| ContentSlot(content: Content) | Content作为ContentSlot的管理器，通过Native侧提供的接口，可以注册并触发ContentSlot的上下树事件回调以及管理ContentSlot的子组件。 |

abstract class Content {
}

#### \[h2\]Native侧接口

| 接口名 | 描述 |
| :-- | :-- |
| [OH\_ArkUI\_NodeContent\_RegisterCallback(ArkUI\_NodeContentHandle content, ArkUI\_NodeContentCallback callback)](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-native-node-h#oh_arkui_nodecontent_registercallback) | 向管理器Content上注册事件。 |
| [OH\_ArkUI\_NodeContentEvent\_GetEventType(ArkUI\_NodeContentEvent\* event)](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-native-node-h#oh_arkui_nodecontentevent_geteventtype) | 获取Content上触发的事件类型。 |
| [OH\_ArkUI\_NodeContent\_AddNode(ArkUI\_NodeContentHandle content, ArkUI\_NodeHandle node)](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-native-node-h#oh_arkui_nodecontent_addnode) | 在Content上添加子组件。 |
| [OH\_ArkUI\_NodeContent\_InsertNode(ArkUI\_NodeContentHandle content, ArkUI\_NodeHandle node, int32\_t position)](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-native-node-h#oh_arkui_nodecontent_insertnode) | 在Content上插入子组件。 |
| [OH\_ArkUI\_NodeContent\_RemoveNode(ArkUI\_NodeContentHandle content, ArkUI\_NodeHandle node)](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-native-node-h#oh_arkui_nodecontent_removenode) | 在Content上移除子组件。 |
| [OH\_ArkUI\_GetNodeContentFromNapiValue(napi\_env env, napi\_value value, ArkUI\_NodeContentHandle\* content)](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-native-node-napi-h#oh_arkui_getnodecontentfromnapivalue) | 获取ArkTS侧创建的NodeContent对象，映射到Native侧的ArkUI\_NodeContentHandle。 |
| [OH\_ArkUI\_NodeContentEvent\_GetNodeContentHandle(ArkUI\_NodeContentEvent\* event)](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-native-node-h#oh_arkui_nodecontentevent_getnodecontenthandle) | 获取触发上下树事件的Content对象。 |
| [OH\_ArkUI\_NodeContent\_SetUserData(ArkUI\_NodeContentHandle content, void\* userData)](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-native-node-h#oh_arkui_nodecontent_setuserdata) | 在Content上设置用户自定义属性。 |
| [OH\_ArkUI\_NodeContent\_GetUserData(ArkUI\_NodeContentHandle content)](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-native-node-h#oh_arkui_nodecontent_getuserdata) | 在Content上获取用户自定义属性。 |
| 
typedef enum {

NODE\_CONTENT\_EVENT\_ON\_ATTACH\_TO\_WINDOW = 0,

NODE\_CONTENT\_EVENT\_ON\_DETACH\_FROM\_WINDOW = 1,

} [ArkUI\_NodeContentEventType](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-native-node-h#arkui_nodecontenteventtype)

 | Content上会触发的上树和下树事件类型。 |

#### 开发实现

#### \[h2\]ArkTS侧代码实现

import nativeNode from 'libentry.so'; // 开发者自己实现的so
import { NodeContent } from '@kit.ArkUI';

@Entry
@Component
struct Parent {
  private nodeContent: Content = new NodeContent();
  // ...

  aboutToAppear() {
    // 通过C-API创建节点，并添加到管理器nodeContent上
    nativeNode.createNativeNode(this.nodeContent);
    // ...
  }

  build() {
    Column() {
      // 显示nodeContent管理器里存放的Native侧的组件
      ContentSlot(this.nodeContent);
      // ...
    }
  }
}

#### \[h2\]Native侧代码实现

Napi的基础开发知识请查看以下文档：[开发导读](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/ndk-development-overview)。

本章节描述实现ContentSlot相关逻辑代码。创建C侧组件的具体步骤，请参阅[使用NDK接口构建UI](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/ndk-build-ui-overview)。

#include "napi/native\_api.h"
#include "arkui/native\_type.h"
#include "arkui/native\_node.h"
#include "arkui/native\_node\_napi.h"
#include "arkui/native\_interface.h"
#include "hilog/log.h"
// ···
ArkUI\_NodeContentHandle nodeContentHandle\_ = nullptr;
ArkUI\_NativeNodeAPI\_1 \*nodeAPI;
const unsigned int LOG\_PRINT\_DOMAIN = 0xFF00;

// 在Native侧创建一个宽高为480vp\*480vp、背景色为0xFFFF0000（红色）的Column组件。对于更详细的节点树创建方法，请参考ArkUI API文档的C API章节。
ArkUI\_NodeHandle NodeManager::CreateNodeHandle()
{
    ArkUI\_NodeHandle column = nodeAPI->createNode(ARKUI\_NODE\_COLUMN);
    ArkUI\_NumberValue value\[\] = {480};
    ArkUI\_AttributeItem item{value, 1};
    nodeAPI->setAttribute(column, NODE\_WIDTH, &item);
    nodeAPI->setAttribute(column, NODE\_HEIGHT, &item);
    value\[0\].u32 = 0xFFFF0000;
    nodeAPI->setAttribute(column, NODE\_BACKGROUND\_COLOR, &item);
    return column;
}
    
// ArkTS侧createNativeNode方法在Native侧的具体实现
napi\_value NodeManager::CreateNativeNode(napi\_env env, napi\_callback\_info info)
{
    // napi相关处理空指针&数据越界等问题
    if ((env == nullptr) || (info == nullptr)) {
        return nullptr;
    }

    size\_t argc = 1;
    napi\_value args\[1\] = { nullptr };
    if (napi\_get\_cb\_info(env, info, &argc, args, nullptr, nullptr) != napi\_ok) {
        OH\_LOG\_Print(LOG\_APP, LOG\_ERROR, LOG\_PRINT\_DOMAIN, "Manager", "CreateNativeNode napi\_get\_cb\_info failed");
    }

    if (argc != 1) {
        return nullptr;
    }

    nodeAPI = reinterpret\_cast<ArkUI\_NativeNodeAPI\_1 \*>(
        OH\_ArkUI\_QueryModuleInterfaceByName(ARKUI\_NATIVE\_NODE, "ArkUI\_NativeNodeAPI\_1"));

    // 将nodeContentHandle\_指向ArkTS侧传入的nodeContent
    OH\_ArkUI\_GetNodeContentFromNapiValue(env, args\[0\], &nodeContentHandle\_);

    if (nodeAPI != nullptr) {
        if (nodeAPI->createNode != nullptr && nodeAPI->addChild != nullptr) {
            ArkUI\_NodeHandle component;
            // 创建C侧组件
            component = CreateNodeHandle();
            // 将组件添加到nodeContent管理器中
            OH\_ArkUI\_NodeContent\_AddNode(nodeContentHandle\_, component);
            // ···
        }
    }
    return nullptr;
}

#### \[h2\]Native侧主要接口使用说明

*   注册上下树事件，并通过事件获取对应的Content对象。
    
    auto nodeContentEvent = \[\](ArkUI\_NodeContentEvent \*event) {
        ArkUI\_NodeContentHandle content = OH\_ArkUI\_NodeContentEvent\_GetNodeContentHandle(event);
        // 针对不同content需要额外做的逻辑
        if (OH\_ArkUI\_NodeContentEvent\_GetEventType(event) == NODE\_CONTENT\_EVENT\_ON\_ATTACH\_TO\_WINDOW) {
            // ContentSlot上树时需要触发的逻辑
            // ···
        } else if (OH\_ArkUI\_NodeContentEvent\_GetEventType(event) == NODE\_CONTENT\_EVENT\_ON\_DETACH\_FROM\_WINDOW) {
            // ContentSlot下树时需要触发的逻辑
            // ···
        };
    };
    // 将该事件注册到nodeContent上
    OH\_ArkUI\_NodeContent\_RegisterCallback(nodeContentHandle\_, nodeContentEvent);
    
*   添加子组件。
    
    ArkUI\_NodeHandle component;
    // 创建C侧组件
    component = CreateNodeHandle();
    // 将组件添加到nodeContent管理器中
    OH\_ArkUI\_NodeContent\_AddNode(nodeContentHandle\_, component);
    
*   插入子组件。
    
    size\_t position = 0;
    ArkUI\_NodeHandle component1 = CreateNodeHandle();
    // 将组件插入nodeContent管理器对应位置
    OH\_ArkUI\_NodeContent\_InsertNode(nodeContentHandle\_, component1, position);
    
*   删除子组件。
    
    // 在nodeContent中移除对应组件
    OH\_ArkUI\_NodeContent\_RemoveNode(nodeContentHandle\_, component1);
    
*   设置自定义属性。
    
    // 创建需要定义的自定义数据
    void \*userData = CreateUserData();
    OH\_ArkUI\_NodeContent\_SetUserData(nodeContentHandle\_, userData);
    
*   获取自定义属性。
    
    void \*userData = OH\_ArkUI\_NodeContent\_GetUserData(nodeContentHandle\_);
    

#### 绑定规则说明

如果将同一个Content对象绑定到多个ContentSlot组件，最终该Content的内容仅在最后一个绑定的ContentSlot中显示，其他ContentSlot将不显示任何内容。

**原因说明：**

Content与ContentSlot节点具有一对一的绑定关系。同一Content不能同时关联多个ContentSlot节点。如果尝试将同一Content挂载到多个ContentSlot节点，仅最后一次挂载生效，之前的ContentSlot节点将失去Content的关联，导致组件内容无法显示。

若需在多个ContentSlot节点下显示相同内容，每个节点需创建单独的Content。示例如下：

import nativeNode from 'libentry.so'; // 开发者自己实现的so
import { NodeContent } from '@kit.ArkUI';

@Entry
@Component
struct Parent {
  // ···
  private nodeContent\_1: Content = new NodeContent();
  private nodeContent\_2: Content = new NodeContent();

  aboutToAppear() {
    // ···
    // 通过C-API创建节点，并添加到管理器nodeContent\_1和nodeContent\_2上
    nativeNode.createNativeNode(this.nodeContent\_1);
    nativeNode.createNativeNode(this.nodeContent\_2);
  }

  build() {
    Column() {
      // ···
      ContentSlot(this.nodeContent\_1);// nodeContent\_1将被挂载到下一个Contentslot节点，此处无法显示
      ContentSlot(this.nodeContent\_1); // 正常显示
      ContentSlot(this.nodeContent\_2); // 正常显示
    }
  }
}