• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 自定义渲染 (XComponent)
2<!--Kit: ArkUI-->
3<!--Subsystem: ArkUI-->
4<!--Owner: @zjsxstar-->
5<!--Designer: @sunbees-->
6<!--Tester: @liuli0427-->
7<!--Adviser: @HelloCrease-->
8
9## 概述
10
11XComponent组件作为一种渲染组件,可用于EGL/OpenGLES和媒体数据写入,通过使用XComponent持有的“[NativeWindow](../graphics/native-window-guidelines.md)”来渲染画面,通常用于满足开发者较为复杂的自定义渲染需求,例如相机预览流的显示和游戏画面的渲染。其可通过指定type字段来实现不同的渲染方式,分别为[XComponentType](../reference/apis-arkui/arkui-ts/ts-appendix-enums.md#xcomponenttype10).SURFACE和XComponentType.TEXTURE。对于SURFACE类型,开发者将定制的绘制内容单独展示到屏幕上。对于TEXTURE类型,开发者将定制的绘制内容和XComponent组件的内容合成后展示到屏幕上。
12
13XComponent持有一个Surface,开发者能通过调用[NativeWindow](../graphics/native-window-guidelines.md)等接口,申请并提交Buffer至图形队列,以此方式将自绘制内容传送至该Surface。XComponent负责将此Surface整合进UI界面,其中展示的内容正是开发者传送的自绘制内容。Surface的默认位置与大小与XComponent组件一致,开发者可利用[setXComponentSurfaceRect](../reference/apis-arkui/arkui-ts/ts-basic-components-xcomponent.md#setxcomponentsurfacerect12)接口自定义调整Surface的位置和大小。XComponent组件负责创建Surface,并通过回调将Surface的相关信息告知应用。应用可以通过一系列接口设定Surface的属性。该组件本身不对所绘制的内容进行感知,亦不提供渲染绘制的接口。
14
15目前XComponent组件主要有三个应用场景:
161. [使用XComponentController管理Surface生命周期场景](#使用xcomponentcontroller管理surface生命周期),该场景在ArkTS侧获取SurfaceId,生命周期回调、触摸、鼠标、按键等事件回调等均在ArkTS侧触发。
172. [使用OH_ArkUI_SurfaceHolder管理Surface生命周期场景](#使用oh_arkui_surfaceholder管理surface生命周期),该场景根据XComponent组件对应的ArkUI_NodeHandle中创建OH_ArkUI_SurfaceHolder,生命周期回调、触摸等事件回调、无障碍和可变帧率回调等均在Native侧触发。
183. [使用NativeXComponent管理Surface生命周期场景](#使用nativexcomponent管理surface生命周期),该场景在native层获取Native XComponent实例,在Native侧注册XComponent的生命周期回调,以及触摸、鼠标、按键等事件回调。
19
20## 约束与限制
21
22当开发者传输的绘制内容包含透明元素时,Surface区域的显示效果会与下方内容进行合成展示。例如,若传输的内容完全透明,且XComponent的背景色被设置为黑色,同时Surface保持默认的大小与位置,则最终显示的将是一片黑色区域。
23
24## 使用XComponentController管理Surface生命周期
25
26本场景通过在ArkTS侧获取SurfaceId,布局信息、生命周期回调、触摸、鼠标、按键等事件回调等均在ArkTS侧触发,按需传递到Native侧进行处理。主要开发场景如下:
27- 基于ArkTS侧获取的SurfaceId,在Native侧调用OH_NativeWindow_CreateNativeWindowFromSurfaceId接口创建出NativeWindow实例。
28- 利用NativeWindow和EGL接口开发自定义绘制内容以及申请和提交Buffer到图形队列。
29- ArkTS侧获取生命周期、事件等信息传递到Native侧处理。
30
31> **说明**:
32>
33> 1. Native侧的NativeWindow缓存在字典中,其key需要保证其唯一性,当对应的XComponent销毁后,需要及时从字典里将其删除。
34>
35> 2. 对于使用[typeNode](../reference/apis-arkui/js-apis-arkui-frameNode.md#typenode12)创建的SURFACE或TEXTURE类型的XComponent组件,由于typeNode组件的生命周期与声明式组件存在差异,组件在创建后的缓冲区尺寸为未设置状态,因此在开始绘制内容之前,应调用[OH_NativeWindow_NativeWindowHandleOpt](../reference/apis-arkgraphics2d/capi-external-window-h.md#oh_nativewindow_nativewindowhandleopt)接口进行缓冲区尺寸设置。
36>
37> 3. 多个XComponent开发时,缓存Native侧资源需要保证key是唯一的,key推荐使用id+随机数或者surfaceId。
38>
39> 4. 在onSurfaceCreated回调触发后,才能获取到有效的surfaceId。
40
41**效果预览**
42
43| 主页                                   | 绘制五角星                                         | 改变颜色                                                |
44|--------------------------------------|-----------------------------------------------|-----------------------------------------------------|
45| ![main](figures/main.png) | ![draw star](figures/drawStar.png) | ![change color](figures/changeColor.png) |
46
47>**说明:**
48>
49>1. 安装编译生成的hap包,并打开应用。
50>
51>2. 点击页面底部“Draw Star”按钮,页面将绘制一个五角星。
52>
53>3. 点击XComponent组件区域(页面中灰色区域)改变五角星颜色。
54
55**生命周期**:
56
57- onSurfaceCreated回调
58
59  触发时刻:XComponent创建完成且创建好Surface后触发。
60
61  ArkTS侧onSurfaceCreated的时序如下图:
62
63  ![OnSurfaceCreated](./figures/onSurfaceCreated1.png)
64
65- onSurfaceChanged回调
66
67  触发时刻:Surface大小变化触发重新布局之后触发。
68
69  ArkTS侧onSurfaceChanged的时序如下图:
70
71  ![OnSurfaceChanged](./figures/onSurfaceChanged1.png)
72
73- onSurfaceDestroyed回调
74
75  触发时刻:XComponent组件被销毁时触发,与一般ArkUI的组件销毁时机一致。
76
77  ArkTS侧onSurfaceDestroyed的时序图:
78
79  ![OnSurfaceDestroyed](./figures/onSurfaceDestroyed1.png)
80
81**接口说明**
82
83ArkTS侧的XComponentController
84
85| 接口名                                                       | 描述                                                         |
86| ------------------------------------------------------------ | ------------------------------------------------------------ |
87| getXComponentSurfaceId(): string                             | 获取XComponent对应Surface的ID。                                |
88| onSurfaceCreated(surfaceId: string): void                    | 当XComponent持有的Surface创建后进行该回调。                    |
89| onSurfaceChanged(surfaceId: string, rect: SurfaceRect): void | 当XComponent持有的Surface大小改变后(包括首次创建时的大小改变)进行该回调。 |
90| onSurfaceDestroyed(surfaceId: string): void                  | 当XComponent持有的Surface销毁后进行该回调。                    |
91
92Native侧
93
94| 接口名                                                       | 描述                                                         |
95| ------------------------------------------------------------ | ------------------------------------------------------------ |
96| int32_t OH_NativeWindow_CreateNativeWindowFromSurfaceId (uint64_t surfaceId, OHNativeWindow **window ) | 通过surfaceId创建对应的OHNativeWindow。                        |
97| void OH_NativeWindow_DestroyNativeWindow (OHNativeWindow* window) | 将OHNativeWindow对象的引用计数减1,当引用计数为0的时候,该OHNativeWindow对象会被析构掉。 |
98
99**开发步骤**
100
101核心开发流程如下图所示:
102
103![开发流程](figures/XComponent开发流程图.png)
104
105以下步骤以SURFACE类型为例,描述了如何使用`XComponent组件`在ArkTS侧传入SurfaceId,在Native侧创建NativeWindow实例,然后创建`EGL/GLES`环境,实现在主页面绘制图形,并可以改变图形的颜色。
106
1071. 在界面中定义XComponent,在cpp/types/libnativerender/Index.d.ts中声明接口,具体实现位于Native侧。
108
109    ```javascript
110    // 函数声明,在cpp/types/libnativerender/Index.d.ts中定义
111    type XComponentContextStatus = {
112        hasDraw: boolean,
113        hasChangeColor: boolean,
114    };
115    export const SetSurfaceId: (id: BigInt) => any;
116    export const ChangeSurface: (id: BigInt, w: number, h: number) =>any;
117    export const DrawPattern: (id: BigInt) => any;
118    export const GetXComponentStatus: (id: BigInt) => XComponentContextStatus
119    export const ChangeColor: (id: BigInt) => any;
120    export const DestroySurface: (id: BigInt) => any;
121    ```
122
123    ```typescript
124    import nativeRender from 'libnativerender.so';
125
126    // 重写XComponentController,设置生命周期回调
127    class MyXComponentController extends XComponentController {
128        onSurfaceCreated(surfaceId: string): void {
129            console.log(`onSurfaceCreated surfaceId: ${surfaceId}`)
130            nativeRender.SetSurfaceId(BigInt(surfaceId));
131        }
132
133        onSurfaceChanged(surfaceId: string, rect: SurfaceRect): void {
134            console.log(`onSurfaceChanged surfaceId: ${surfaceId}, rect: ${JSON.stringify(rect)}}`)
135            // 在onSurfaceChanged中调用ChangeSurface绘制内容
136            nativeRender.ChangeSurface(BigInt(surfaceId), rect.surfaceWidth, rect.surfaceHeight)
137        }
138
139        onSurfaceDestroyed(surfaceId: string): void {
140            console.log(`onSurfaceDestroyed surfaceId: ${surfaceId}`)
141            nativeRender.DestroySurface(BigInt(surfaceId))
142        }
143    }
144
145    @Entry
146    @Component
147    struct Index {
148        @State currentStatus: string = "index";
149        xComponentController: XComponentController = new MyXComponentController();
150
151        build() {
152            Column() {
153                //...
154                //在xxx.ets 中定义 XComponent
155                Column({ space: 10 }) {
156                    XComponent({
157                        type: XComponentType.SURFACE,
158                        controller: this.xComponentController
159                    })
160                Text(this.currentStatus)
161                    .fontSize('24fp')
162                    .fontWeight(500)
163                }
164                .onClick(() => {
165                    let surfaceId = this.xComponentController.getXComponentSurfaceId()
166                    nativeRender.ChangeColor(BigInt(surfaceId))
167                    let hasChangeColor: boolean = false;
168                    if (nativeRender.GetXComponentStatus(BigInt(surfaceId))) {
169                        hasChangeColor = nativeRender.GetXComponentStatus(BigInt(surfaceId)).hasChangeColor;
170                    }
171                    if (hasChangeColor) {
172                        this.currentStatus = "change color";
173                    }
174                })
175
176                //...
177                Row() {
178                    Button('Draw Star')
179                        .fontSize('16fp')
180                        .fontWeight(500)
181                        .margin({ bottom: 24 })
182                        .onClick(() => {
183                            let surfaceId = this.xComponentController.getXComponentSurfaceId()
184                            nativeRender.DrawPattern(BigInt(surfaceId))
185                            let hasDraw: boolean = false;
186                            if (nativeRender.GetXComponentStatus(BigInt(surfaceId))) {
187                                hasDraw = nativeRender.GetXComponentStatus(BigInt(surfaceId)).hasDraw;
188                            }
189                            if (hasDraw) {
190                                this.currentStatus = "draw star"
191                            }
192                        })
193                        .width('53.6%')
194                        .height(40)
195                }
196                .width('100%')
197                .justifyContent(FlexAlign.Center)
198                .alignItems(VerticalAlign.Bottom)
199                .layoutWeight(1)
200            }
201            .width('100%')
202            .height('100%')
203        }
204    }
205    ```
206
2072. Node-API模块注册,具体使用请参考[Node-API开发规范](../napi/napi-guidelines.md)。
208
209    ```typescript
210    #include <hilog/log.h>
211    #include "common/common.h"
212    #include "manager/plugin_manager.h"
213    namespace NativeXComponentSample {
214    // 在napi_init.cpp文件中,Init方法注册接口函数,从而将封装的C++方法传递出来,供ArkTS侧调用
215    EXTERN_C_START
216    static napi_value Init(napi_env env, napi_value exports) {
217        // ...
218        // 向ArkTS侧暴露接口SetSurfaceId(),ChangeSurface(),DestroySurface(),
219        // DrawPattern(),ChangeColor(),GetXComponentStatus()
220        napi_property_descriptor desc[] = {
221            {"SetSurfaceId", nullptr, PluginManager::SetSurfaceId, nullptr, nullptr, nullptr, napi_default, nullptr},
222            {"ChangeSurface", nullptr, PluginManager::ChangeSurface, nullptr, nullptr, nullptr, napi_default, nullptr},
223            {"DestroySurface", nullptr, PluginManager::DestroySurface, nullptr, nullptr, nullptr, napi_default, nullptr},
224            {"DrawPattern", nullptr, PluginManager::DrawPattern, nullptr, nullptr, nullptr, napi_default, nullptr},
225            {"ChangeColor", nullptr, PluginManager::ChangeColor, nullptr, nullptr, nullptr, napi_default, nullptr},
226            {"GetXComponentStatus", nullptr, PluginManager::GetXComponentStatus, nullptr, nullptr, nullptr, napi_default,
227             nullptr}};
228        if (napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc) != napi_ok) {
229            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Init", "napi_define_properties failed");
230            return nullptr;
231        }
232        return exports;
233    }
234    EXTERN_C_END
235    // 编写接口的描述信息,根据实际需要可以修改对应参数
236    static napi_module nativerenderModule = {.nm_version = 1,
237                                             .nm_flags = 0,
238                                             .nm_filename = nullptr,
239                                             // 入口函数
240                                             .nm_register_func = Init,
241                                             // 模块名称
242                                             .nm_modname = "nativerender",
243                                             .nm_priv = ((void *)0),
244                                             .reserved = {0}};
245    } // namespace NativeXComponentSample
246    // __attribute__((constructor))修饰的方法由系统自动调用,使用Node-API接口napi_module_register()传入模块描述信息进行模块注册
247    extern "C" __attribute__((constructor)) void RegisterModule(void) {
248        napi_module_register(&NativeXComponentSample::nativerenderModule);
249    }
250    ```
251
2523. 上述注册的六个函数在Native侧的具体实现如下:ChangeColor和DrawPattern利用OpenGL(https://developer.huawei.com/consumer/cn/doc/harmonyos-references/opengl)进行五角星的绘制;ChangeSurface根据传入的surfaceId、width、height调整Surface的大小;SetSurfaceId基于SurfaceId完成NativeWindow的初始化;DestroySurface销毁与Surface相关的资源;GetXComponentStatus获取xcomponent状态并返回至ArkTS侧。
253
254    ```cpp
255    // PluginManager类定义
256    class PluginManager {
257    public:
258        ~PluginManager();
259        static PluginRender *GetPluginRender(int64_t &id);
260        static napi_value ChangeColor(napi_env env, napi_callback_info info);
261        static napi_value DrawPattern(napi_env env, napi_callback_info info);
262        static napi_value SetSurfaceId(napi_env env, napi_callback_info info);
263        static napi_value ChangeSurface(napi_env env, napi_callback_info info);
264        static napi_value DestroySurface(napi_env env, napi_callback_info info);
265        static napi_value GetXComponentStatus(napi_env env, napi_callback_info info);
266
267    public:
268        static std::unordered_map<int64_t, PluginRender *> pluginRenderMap_;
269        static std::unordered_map<int64_t, OHNativeWindow *> windowMap_;
270    };
271
272    // 解析从ArkTS侧传入的surfaceId,此处surfaceId是一个64位int值
273    int64_t ParseId(napi_env env, napi_callback_info info) {
274        if ((env == nullptr) || (info == nullptr)) {
275            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ParseId", "env or info is null");
276            return -1;
277        }
278        size_t argc = 1;
279        napi_value args[1] = {nullptr};
280        if (napi_ok != napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)) {
281            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ParseId", "GetContext napi_get_cb_info failed");
282            return -1;
283        }
284        int64_t value = 0;
285        bool lossless = true;
286        if (napi_ok != napi_get_value_bigint_int64(env, args[0], &value, &lossless)) {
287            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ParseId", "Get value failed");
288            return -1;
289        }
290        return value;
291    }
292
293    // 设置SurfaceId,基于SurfaceId完成对NativeWindow的初始化
294    napi_value PluginManager::SetSurfaceId(napi_env env, napi_callback_info info) {
295        int64_t surfaceId = ParseId(env, info);
296        OHNativeWindow *nativeWindow;
297        PluginRender *pluginRender;
298        if (windowMap_.find(surfaceId) == windowMap_.end()) {
299            OH_NativeWindow_CreateNativeWindowFromSurfaceId(surfaceId, &nativeWindow);
300            windowMap_[surfaceId] = nativeWindow;
301        } else {
302            return nullptr;
303        }
304        if (pluginRenderMap_.find(surfaceId) == pluginRenderMap_.end()) {
305            pluginRender = new PluginRender(surfaceId);
306            pluginRenderMap_[surfaceId] = pluginRender;
307        }
308        pluginRender->InitNativeWindow(nativeWindow);
309        return nullptr;
310    }
311
312    void PluginRender::InitNativeWindow(OHNativeWindow *window) {
313        eglCore_->EglContextInit(window); // 参考Native XComponent场景EglContextInit的实现
314    }
315
316    // 根据传入的surfaceId、width、height实现Surface大小的变动
317    napi_value PluginManager::ChangeSurface(napi_env env, napi_callback_info info) {
318        if ((env == nullptr) || (info == nullptr)) {
319            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager",
320                         "ChangeSurface: OnLoad env or info is null");
321            return nullptr;
322        }
323        int64_t surfaceId = 0;
324        size_t argc = 3;
325        napi_value args[3] = {nullptr};
326
327        if (napi_ok != napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)) {
328            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager",
329                         "ChangeSurface: GetContext napi_get_cb_info failed");
330            return nullptr;
331        }
332        bool lossless = true;
333        int index = 0;
334        if (napi_ok != napi_get_value_bigint_int64(env, args[index++], &surfaceId, &lossless)) {
335            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "ChangeSurface: Get value failed");
336            return nullptr;
337        }
338        double width;
339        if (napi_ok != napi_get_value_double(env, args[index++], &width)) {
340            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "ChangeSurface: Get width failed");
341            return nullptr;
342        }
343        double height;
344        if (napi_ok != napi_get_value_double(env, args[index++], &height)) {
345            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "ChangeSurface: Get height failed");
346            return nullptr;
347        }
348        auto pluginRender = GetPluginRender(surfaceId);
349        if (pluginRender == nullptr) {
350            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "ChangeSurface: Get pluginRender failed");
351            return nullptr;
352        }
353        pluginRender->UpdateNativeWindowSize(width, height);
354        return nullptr;
355    }
356
357    void PluginRender::UpdateNativeWindowSize(int width, int height) {
358        eglCore_->UpdateSize(width, height); // 参考Native XComponent场景UpdateSize的实现
359        if (!hasChangeColor_ && !hasDraw_) {
360            eglCore_->Background(); // 参考Native XComponent场景Background的实现
361        }
362    }
363
364    // 销毁Surface
365    napi_value PluginManager::DestroySurface(napi_env env, napi_callback_info info) {
366        int64_t surfaceId = ParseId(env, info);
367        auto pluginRenderMapIter = pluginRenderMap_.find(surfaceId);
368        if (pluginRenderMapIter != pluginRenderMap_.end()) {
369            delete pluginRenderMapIter->second;
370            pluginRenderMap_.erase(pluginRenderMapIter);
371        }
372        auto windowMapIter = windowMap_.find(surfaceId);
373        if (windowMapIter != windowMap_.end()) {
374            OH_NativeWindow_DestroyNativeWindow(windowMapIter->second);
375            windowMap_.erase(windowMapIter);
376        }
377        return nullptr;
378    }
379
380    // 实现EGL绘画逻辑
381    napi_value PluginManager::DrawPattern(napi_env env, napi_callback_info info) {
382        int64_t surfaceId = ParseId(env, info);
383        auto pluginRender = GetPluginRender(surfaceId);
384        if (pluginRender == nullptr) {
385            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "DrawPattern: Get pluginRender failed");
386            return nullptr;
387        }
388        pluginRender->DrawPattern();
389        return nullptr;
390    }
391
392    PluginRender *PluginManager::GetPluginRender(int64_t &id) {
393        if (pluginRenderMap_.find(id) != pluginRenderMap_.end()) {
394            return pluginRenderMap_[id];
395        }
396        return nullptr;
397    }
398
399    void PluginRender::DrawPattern() {
400        eglCore_->Draw(hasDraw_); // 参考Native XComponent场景Draw实现
401    }
402
403    // 实现改变绘制图形颜色的功能
404    napi_value PluginManager::ChangeColor(napi_env env, napi_callback_info info) {
405        int64_t surfaceId = ParseId(env, info);
406        auto pluginRender = GetPluginRender(surfaceId);
407        if (pluginRender == nullptr) {
408            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "ChangeColor: Get pluginRender failed");
409            return nullptr;
410        }
411        pluginRender->ChangeColor(); // 参考Native XComponent场景ChangeColor实现
412        return nullptr;
413    }
414
415    void PluginRender::ChangeColor() { eglCore_->ChangeColor(hasChangeColor_); }
416
417    // 获得xcomponent状态,并返回至ArkTS侧
418    napi_value PluginManager::GetXComponentStatus(napi_env env, napi_callback_info info) {
419        int64_t surfaceId = ParseId(env, info);
420        auto pluginRender = GetPluginRender(surfaceId);
421        if (pluginRender == nullptr) {
422            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager",
423                         "GetXComponentStatus: Get pluginRender failed");
424            return nullptr;
425        }
426        napi_value hasDraw;
427        napi_value hasChangeColor;
428        napi_status ret = napi_create_int32(env, pluginRender->HasDraw(), &(hasDraw));
429        if (ret != napi_ok) {
430            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager",
431                         "GetXComponentStatus: napi_create_int32 hasDraw_ error");
432            return nullptr;
433        }
434        ret = napi_create_int32(env, pluginRender->HasChangedColor(), &(hasChangeColor));
435        if (ret != napi_ok) {
436            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager",
437                         "GetXComponentStatus: napi_create_int32 hasChangeColor_ error");
438            return nullptr;
439        }
440        napi_value obj;
441        ret = napi_create_object(env, &obj);
442        if (ret != napi_ok) {
443            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager",
444                         "GetXComponentStatus: napi_create_object error");
445            return nullptr;
446        }
447        ret = napi_set_named_property(env, obj, "hasDraw", hasDraw);
448        if (ret != napi_ok) {
449            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager",
450                         "GetXComponentStatus: napi_set_named_property hasDraw error");
451            return nullptr;
452        }
453        ret = napi_set_named_property(env, obj, "hasChangeColor", hasChangeColor);
454        if (ret != napi_ok) {
455            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager",
456                         "GetXComponentStatus: napi_set_named_property hasChangeColor error");
457            return nullptr;
458        }
459        return obj;
460    }
461
462    int32_t PluginRender::HasDraw() { return hasDraw_; }
463
464    int32_t PluginRender::HasChangedColor() { return hasChangeColor_; }
465    ```
466
4674. 配置具体的CMakeLists,使用CMake工具链将C++源代码编译成动态链接库文件。
468
469    ```cmake
470    # 设置CMake最小版本
471    cmake_minimum_required(VERSION 3.4.1)
472    # 项目名称
473    project(XComponent)
474
475    set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
476    add_definitions(-DOHOS_PLATFORM)
477    # 设置头文件搜索目录
478    include_directories(
479        ${NATIVERENDER_ROOT_PATH}
480        ${NATIVERENDER_ROOT_PATH}/include
481    )
482    # 添加名为nativerender的动态库,库文件名为libnativerender.so,添加cpp文件
483    add_library(nativerender SHARED
484        render/egl_core.cpp
485        render/plugin_render.cpp
486        manager/plugin_manager.cpp
487        napi_init.cpp
488    )
489
490    find_library(
491        # 设置路径变量的名称。
492        EGL-lib
493        # 指定要让CMake查找的NDK库的名称。
494        EGL
495    )
496
497    find_library(
498        # 设置路径变量的名称。
499        GLES-lib
500        # 指定要让CMake查找的NDK库的名称。
501        GLESv3
502    )
503
504    find_library(
505        # 设置路径变量的名称。
506        hilog-lib
507        # 指定要让CMake查找的NDK库的名称。
508        hilog_ndk.z
509    )
510
511    find_library(
512        # 设置路径变量的名称。
513        libace-lib
514        # 指定要让CMake查找的NDK库的名称。
515        ace_ndk.z
516    )
517
518    find_library(
519        # 设置路径变量的名称。
520        libnapi-lib
521        # 指定要让CMake查找的NDK库的名称。
522        ace_napi.z
523    )
524
525    find_library(
526        # 设置路径变量的名称。
527        libuv-lib
528        # 指定要让CMake查找的NDK库的名称。
529        uv
530    )
531    # 添加构建需要链接的库
532    target_link_libraries(nativerender PUBLIC
533        ${EGL-lib} ${GLES-lib} ${hilog-lib} ${libace-lib} ${libnapi-lib} ${libuv-lib} libnative_window.so)
534    ```
535
536上述用例具体实现可参考<!--RP2-->[ArkTSXComponent(API12)](https://gitee.com/openharmony/applications_app_samples/tree/master/code/BasicFeature/Native/ArkTSXComponent)<!--RP2nd-->。
537
538## 使用OH_ArkUI_SurfaceHolder管理Surface生命周期
539
540与使用XComponentController管理Surface生命周期场景不同,本场景允许应用根据XComponent组件对应的ArkUI_NodeHandle中创建OH_ArkUI_SurfaceHolder,并通过OH_ArkUI_SurfaceHolder上的相关接口注册Surface生命周期,XComponent组件相关的无障碍、可变帧率等能力也可根据ArkUI_NodeHandle通过相关接口来实现。同时,XComponent组件上的基础/手势事件也可通过ArkUI_NodeHandle对象使用ArkUI NDK接口来监听(具体可参考:[监听组件事件](./ndk-listen-to-component-events.md))。主要开发场景如下:
541- 在ArkTS侧创建的XComponent组件可以将其对应的FrameNode节点传递到Native侧以获取ArkUI_NodeHandle,或者在Native侧直接创建XComponent组件对应的ArkUI_NodeHandle,然后调用OH_ArkUI_SurfaceHolder_Create接口创建OH_ArkUI_SurfaceHolder实例。
542- 基于OH_ArkUI_SurfaceHolder实例注册相应的生命周期回调、事件回调,获取NativeWindow实例。
543- 利用NativeWindow和EGL接口开发自定义绘制内容以及申请和提交Buffer到图形队列。
544
545**生命周期**:
546
547- OnSurfaceCreated回调
548
549  触发时刻:XComponent创建完成且创建好Surface后达成以下两个条件中的一个触发。
550  1. 组件上树且autoInitialize = true。
551  2. 调用OH_ArkUI_XComponent_Initialize。
552
553  Native侧OnSurfaceCreated的时序如下图:
554
555  ![OnSurfaceCreated](./figures/onSurfaceCreated2.png)
556- OnSurfaceChanged回调
557
558  触发时刻:OnSurfaceCreated回调成功触发且Surface大小变化触发重新布局之后触发。
559
560  Native侧OnSurfaceChanged的时序如下图:
561
562  ![OnSurfaceChanged](./figures/onSurfaceChanged2.png)
563
564- OnSurfaceDestroyed回调
565
566  触发时刻:组件下树且autoInitialize=true 或者调用 OH_ArkUI_XComponent_Finalize后触发。
567
568  Native侧OnSurfaceDestroyed的时序图:
569
570  ![OnSurfaceDestroyed](./figures/onSurfaceDestroyed2.png)
571
572
573**接口说明**
574
575| 接口名                                                       | 描述                                                         |
576| ------------------------------------------------------------ | ------------------------------------------------------------ |
577| OH_ArkUI_QueryModuleInterfaceByName(ArkUI_NativeAPIVariantKind type, const char* structName) | 获取指定类型的Native模块接口集合。                                         |
578| OH_ArkUI_XComponent_GetNativeWindow(OH_ArkUI_SurfaceHolder* surfaceHolder) | 获取与OH_ArkUI_SurfaceHolder实例关联的nativeWindow。                                         |
579| OH_ArkUI_SurfaceHolder_RemoveSurfaceCallback(OH_ArkUI_SurfaceHolder* surfaceHolder, OH_ArkUI_SurfaceCallback* callback) | 从OH_ArkUI_SurfaceHolder实例中移除先前添加的Surface生命周期回调。                                         |
580| OH_ArkUI_SurfaceCallback_Dispose(OH_ArkUI_SurfaceCallback* callback) | 释放OH_ArkUI_SurfaceCallback对象。                                         |
581| OH_ArkUI_SurfaceHolder_Dispose(OH_ArkUI_SurfaceHolder* surfaceHolder) | 释放OH_ArkUI_SurfaceHolder对象。                                         |
582| OH_ArkUI_NodeEvent_GetEventType(ArkUI_NodeEvent* event) | 从组件事件获取事件类型。                                         |
583| OH_ArkUI_NodeEvent_GetNodeHandle(ArkUI_NodeEvent* event) | 获取触发组件事件的组件对象。                                         |
584| OH_ArkUI_GetNodeHandleFromNapiValue(napi_env env, napi_value frameNode, ArkUI_NodeHandle* handle) | 获取ArkTS侧创建的FrameNode节点对象映射到Native侧的ArkUI_NodeHandle。                                         |
585| OH_ArkUI_SurfaceHolder_Create(ArkUI_NodeHandle node) | 从XComponent节点创建一个OH_ArkUI_SurfaceHolder对象。                                       |
586| OH_ArkUI_SurfaceCallback_Create() | 创建一个OH_ArkUI_SurfaceCallback对象。                                         |
587| OH_ArkUI_SurfaceCallback_SetSurfaceCreatedEvent(OH_ArkUI_SurfaceCallback* callback, void (\*onSurfaceCreated)(OH_ArkUI_SurfaceHolder* surfaceHolder)) | 往OH_ArkUI_SurfaceCallback对象中注册onSurfaceCreated回调。                                         |
588| OH_ArkUI_SurfaceCallback_SetSurfaceChangedEvent(OH_ArkUI_SurfaceCallback* callback, void (\*onSurfaceChanged)(OH_ArkUI_SurfaceHolder* surfaceHolder, uint64_t width, uint64_t height)) | 往OH_ArkUI_SurfaceCallback对象中注册onSurfaceChanged回调。                                         |
589| OH_ArkUI_SurfaceCallback_SetSurfaceDestroyedEvent(OH_ArkUI_SurfaceCallback* callback, void (\*onSurfaceDestroyed)(OH_ArkUI_SurfaceHolder* surfaceHolder)) | 往OH_ArkUI_SurfaceCallback对象中注册onSurfaceDestroyed回调。                                         |
590| OH_ArkUI_SurfaceCallback_SetSurfaceShowEvent(OH_ArkUI_SurfaceCallback* callback, void (\*onSurfaceShow)(OH_ArkUI_SurfaceHolder* surfaceHolder)) | 往OH_ArkUI_SurfaceCallback对象中注册onSurfaceShow回调。                                         |
591| OH_ArkUI_SurfaceCallback_SetSurfaceHideEvent(OH_ArkUI_SurfaceCallback* callback, void (\*onSurfaceHide)(OH_ArkUI_SurfaceHolder* surfaceHolder)) | 往OH_ArkUI_SurfaceCallback对象中注册onSurfaceHide回调。                                         |
592| OH_ArkUI_XComponent_RegisterOnFrameCallback(ArkUI_NodeHandle node, void (*callback)(ArkUI_NodeHandle node, uint64_t timestamp, uint64_t targetTimestamp)) | 为XComponent节点注册onFrame回调。                                         |
593| OH_ArkUI_SurfaceHolder_AddSurfaceCallback(OH_ArkUI_SurfaceHolder* surfaceHolder, OH_ArkUI_SurfaceCallback* callback) | 往OH_ArkUI_SurfaceHolder实例注册OH_ArkUI_SurfaceCallback对象。                                         |
594| OH_ArkUI_AccessibilityProvider_Create(ArkUI_NodeHandle node) | 从XComponent节点创建一个ArkUI_AccessibilityProvider对象。                                         |
595| OH_ArkUI_XComponent_UnregisterOnFrameCallback(ArkUI_NodeHandle node) | 取消注册XComponent节点的onFrame回调。                                         |
596| OH_ArkUI_AccessibilityProvider_Dispose(ArkUI_AccessibilityProvider* provider) | 释放ArkUI_AccessibilityProvider对象。                                        |
597| OH_ArkUI_XComponent_SetExpectedFrameRateRange(ArkUI_NodeHandle node, OH_NativeXComponent_ExpectedRateRange range) | 为XComponent节点设置预期的帧率范围。                                         |
598| OH_ArkUI_XComponent_SetNeedSoftKeyboard(ArkUI_NodeHandle node, bool needSoftKeyboard) | 设置XComponent节点在获得焦点时是否需要显示软键盘。                                         |
599
600**开发步骤**
601
602以下步骤通过在ArkTS侧创建SURFACE类型的XComponent为例(Native侧如何创建XComponent组件对应的ArkUI_NodeHandle可参考[ArkUI_NativeNodeAPI_1](../reference/apis-arkui/capi-arkui-nativemodule-arkui-nativenodeapi-1.md)),描述了如何使用`XComponent组件`调用OH_ArkUI_SurfaceHolder相关接口管理Surface生命周期,并在Native侧创建`EGL/GLES`环境,实现在主页面绘制图形,以及可以改变图形的颜色。
603
6041. 在界面中定义XComponent。
605
606    ```typescript
607    import native from 'libnativerender.so';
608
609    @Entry
610    @Component
611    struct Index {
612        xcomponentId: string = 'xcp' + (new Date().getTime());
613        @State isShow: boolean = true;
614        @State minRate: number = 0;
615        @State maxRate: number = 120;
616        @State expected: number = 60;
617        needSoftKeyboard: boolean = false;
618        @State needSoftKeyboardState: string = 'needSoftKeyboard=' + this.needSoftKeyboard;
619        @State text: string = '单指点击XComponent软件盘消失'
620        controller: TextInputController = new TextInputController()
621
622        build() {
623            Column() {
624                TextInput({ text: this.text, placeholder: 'please input ...', controller: this.controller })
625                    .placeholderColor(Color.Grey)
626                    .placeholderFont({ size: 14, weight: 400 })
627                    .caretColor(Color.Blue)
628                    .width(400)
629                    .height(40)
630                    .margin(20)
631                    .fontSize(14)
632                    .fontColor(Color.Black)
633                    .onChange((value: string) => {
634                        this.text = value
635                    })
636                Column() {
637                    if (this.isShow) {
638                        XComponent({
639                            type: XComponentType.SURFACE
640                        })
641                            .id(this.xcomponentId)
642                            .onAttach(() => {
643                                let node = this.getUIContext().getFrameNodeById(this.xcomponentId)
644                                native.bindNode(this.xcomponentId, node)
645                            })
646                            .onDetach(() => {
647                                native.unbindNode(this.xcomponentId)
648                            })
649                            .width(200)
650                            .height(200)
651                            .focusable(true)
652                            .focusOnTouch(true)
653                            .defaultFocus(true)
654                    }
655                }.height(200)
656
657                Button('创建/销毁').onClick(() => {
658                    this.isShow = !this.isShow;
659                })
660
661                Column() {
662                    Text('期望帧率设置:')
663                        .textAlign(TextAlign.Start)
664                        .fontSize(15)
665                        .border({ width: 1 })
666                        .padding(10)
667                        .width('100%')
668                        .margin(5)
669                    Text('min: ' + this.minRate)
670                    Slider({
671                        value: this.minRate,
672                        min: 0,
673                        max: 240,
674                        step: 1
675                    }).onChange((value: number, mode: SliderChangeMode) => {
676                        this.minRate = value;
677                        native.setFrameRate(this.xcomponentId, this.minRate, this.maxRate, this.expected)
678                    }).width('100%')
679                    Text('max: ' + this.maxRate)
680                    Slider({
681                        value: this.maxRate,
682                        min: 0,
683                        max: 240,
684                        step: 1
685                    }).onChange((value: number, mode: SliderChangeMode) => {
686                        this.maxRate = value;
687                        native.setFrameRate(this.xcomponentId, this.minRate, this.maxRate, this.expected)
688                    }).width('100%')
689                    Text('expected: ' + this.expected)
690                    Slider({
691                        value: this.expected,
692                        min: 0,
693                        max: 240,
694                        step: 1
695                    }).onChange((value: number, mode: SliderChangeMode) => {
696                        this.expected = value;
697                        native.setFrameRate(this.xcomponentId, this.minRate, this.maxRate, this.expected)
698                    }).width('100%')
699                }.backgroundColor("#F0FAFF")
700
701                Button(this.needSoftKeyboardState)
702                    .onClick(() => {
703                        this.needSoftKeyboard = !this.needSoftKeyboard;
704                        this.needSoftKeyboardState = 'needSoftKeyboard=' + this.needSoftKeyboard;
705                        native.setNeedSoftKeyboard(this.xcomponentId, this.needSoftKeyboard);
706                        this.text = this.needSoftKeyboard ? '单指点击XComponent软键盘不消失' : '单指点击XComponent软件盘消失'
707                    })
708            }
709            .width('100%')
710        }
711    }
712    ```
713
7142. Node-API模块注册,具体使用请参考[Node-API开发规范](../napi/napi-guidelines.md)。
715
716    ```c++
717    #include "manager/plugin_manager.h"
718
719    namespace NativeXComponentSample {
720
721    // 在napi_init.cpp文件中,Init方法注册接口函数,从而将封装的C++方法传递出来,供ArkTS侧调用
722    EXTERN_C_START
723    static napi_value Init(napi_env env, napi_value exports)
724    {
725        // 向ArkTS侧暴露接口
726        napi_property_descriptor desc[] = {
727            {"bindNode", nullptr, PluginManager::BindNode,
728                nullptr, nullptr, nullptr, napi_default, nullptr},
729            {"unbindNode", nullptr, PluginManager::UnbindNode,
730                nullptr, nullptr, nullptr, napi_default, nullptr},
731            {"setFrameRate", nullptr, PluginManager::SetFrameRate,
732                nullptr, nullptr, nullptr, napi_default, nullptr},
733            {"setNeedSoftKeyboard", nullptr, PluginManager::SetNeedSoftKeyboard,
734                nullptr, nullptr, nullptr, napi_default, nullptr},
735        };
736        napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
737        return exports;
738    }
739    EXTERN_C_END
740
741    // 编写接口的描述信息,根据实际需要可以修改对应参数
742    static napi_module demoModule = {
743        .nm_version = 1,
744        .nm_flags = 0,
745        .nm_filename = nullptr,
746        // 入口函数
747        .nm_register_func = Init, // 指定加载对应模块时的回调函数
748        // 模块名称
749        .nm_modname = "nativerender", // 指定模块名称,对于XComponent相关开发,这个名称必须和ArkTS侧XComponent中libraryname的值保持一致
750        .nm_priv = ((void*)0),
751        .reserved = { 0 },
752    };
753    }
754
755    // __attribute__((constructor))修饰的方法由系统自动调用,使用Node-API接口napi_module_register()传入模块描述信息进行模块注册
756    extern "C" __attribute__((constructor)) void RegisterEntryModule(void)
757    {
758        napi_module_register(&NativeXComponentSample::demoModule);
759    }
760    ```
761
7623. 注册XComponent生命周期、事件、无障碍和可变帧率回调,使用CAPI实现往XComponent注册回调函数。
763
764    (1) 定义BindNode、UnbindNode、SetFrameRate、SetNeedSoftKeyboard方法,暴露到ArkTS侧的bindNode、unbindNode、setFrameRate、setNeedSoftKeyboard方法会执行该方法。
765
766    ```c++
767    // plugin_manager.h
768    namespace NativeXComponentSample {
769    class PluginManager {
770    public:
771        ~PluginManager();
772        static napi_value BindNode(napi_env env, napi_callback_info info);
773        static napi_value UnbindNode(napi_env env, napi_callback_info info);
774        static napi_value SetFrameRate(napi_env env, napi_callback_info info);
775        static napi_value SetNeedSoftKeyboard(napi_env env, napi_callback_info info);
776
777    public:
778        static std::unordered_map<std::string, ArkUI_NodeHandle> nodeHandleMap_;
779        static std::unordered_map<void *, EGLRender *> renderMap_;
780        static std::unordered_map<void *, OH_ArkUI_SurfaceCallback *> callbackMap_;
781        static std::unordered_map<void *, OH_ArkUI_SurfaceHolder *> surfaceHolderMap_;
782        static ArkUI_AccessibilityProvider* provider_;
783    };
784    }
785    ```
786
787    ```c++
788    // plugin_manager.cpp
789    std::unordered_map<std::string, ArkUI_NodeHandle> PluginManager::nodeHandleMap_;
790    std::unordered_map<void *, EGLRender *> PluginManager::renderMap_;
791    std::unordered_map<void *, OH_ArkUI_SurfaceCallback *> PluginManager::callbackMap_;
792    std::unordered_map<void *, OH_ArkUI_SurfaceHolder *> PluginManager::surfaceHolderMap_;
793    ArkUI_NativeNodeAPI_1 *nodeAPI = reinterpret_cast<ArkUI_NativeNodeAPI_1 *>(
794        OH_ArkUI_QueryModuleInterfaceByName(ARKUI_NATIVE_NODE, "ArkUI_NativeNodeAPI_1"));
795
796    std::string value2String(napi_env env, napi_value value) { // 将napi_value转化为string类型的变量
797        size_t stringSize = 0;
798        napi_get_value_string_utf8(env, value, nullptr, 0, &stringSize);
799        std::string valueString;
800        valueString.resize(stringSize);
801        napi_get_value_string_utf8(env, value, &valueString[0], stringSize + 1, &stringSize);
802        return valueString;
803    }
804
805    napi_value PluginManager::BindNode(napi_env env, napi_callback_info info) {
806        size_t argc = 2;
807        napi_value args[2] = {nullptr};
808        napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
809        std::string nodeId = value2String(env, args[0]);
810        ArkUI_NodeHandle handle;
811        OH_ArkUI_GetNodeHandleFromNapiValue(env, args[1], &handle); // 获取nodeHandle
812        OH_ArkUI_SurfaceHolder *holder = OH_ArkUI_SurfaceHolder_Create(handle); // 获取SurfaceHolder
813        nodeHandleMap_[nodeId] = handle;
814        surfaceHolderMap_[handle] = holder;
815        auto callback = OH_ArkUI_SurfaceCallback_Create(); // 创建SurfaceCallback
816        callbackMap_[holder] = callback;
817        OH_ArkUI_SurfaceCallback_SetSurfaceCreatedEvent(callback, OnSurfaceCreated); // 注册OnSurfaceCreated回调
818        OH_ArkUI_SurfaceCallback_SetSurfaceChangedEvent(callback, OnSurfaceChanged); // 注册OnSurfaceChanged回调
819        OH_ArkUI_SurfaceCallback_SetSurfaceDestroyedEvent(callback, OnSurfaceDestroyed); // 注册OnSurfaceDestroyed回调
820        OH_ArkUI_SurfaceCallback_SetSurfaceShowEvent(callback, OnSurfaceShow); // 注册OnSurfaceShow回调
821        OH_ArkUI_SurfaceCallback_SetSurfaceHideEvent(callback, OnSurfaceHide); // 注册OnSurfaceHide回调
822        OH_ArkUI_XComponent_RegisterOnFrameCallback(handle, OnFrameCallback); // 注册OnFrameCallback回调
823        OH_ArkUI_SurfaceHolder_AddSurfaceCallback(holder, callback); // 注册SurfaceCallback回调
824        if (!nodeAPI->addNodeEventReceiver(handle, onEvent)) { // 添加事件监听,返回成功码 0
825            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "onBind", "addNodeEventReceiver error");
826        }
827        if (!nodeAPI->registerNodeEvent(handle, NODE_TOUCH_EVENT, 0, nullptr)) { // 用C接口注册touch事件,返回成功码 0
828            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "onBind", "registerTouchEvent error");
829        }
830        provider_ = OH_ArkUI_AccessibilityProvider_Create(handle); // 创建一个ArkUI_AccessibilityProvider类型的对象
831        /**
832        * 获取ArkUI_AccessibilityProvider后,如果注册无障碍回调函数请参考:
833        * https://gitcode.com/openharmony/docs/blob/OpenHarmony-5.1.0-Release/zh-cn/application-dev/ui/ndk-accessibility-xcomponent.md
834        * **/
835        return nullptr;
836    }
837
838    napi_value PluginManager::UnbindNode(napi_env env, napi_callback_info info)
839    {
840        size_t argc = 1;
841        napi_value args[1] = {nullptr};
842        napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
843        std::string nodeId = value2String(env, args[0]);
844        auto node = nodeHandleMap_[nodeId];
845        OH_ArkUI_XComponent_UnregisterOnFrameCallback(node); // 解注册帧回调
846        OH_ArkUI_AccessibilityProvider_Dispose(provider_); // 销毁ArkUI_AccessibilityProvider
847        nodeAPI->disposeNode(node); // 销毁nodeHandle
848        nodeHandleMap_.erase(nodeId);
849        return nullptr;
850    }
851
852    napi_value PluginManager::SetFrameRate(napi_env env, napi_callback_info info)
853    {
854        size_t argc = 4;
855        napi_value args[4] = {nullptr};
856        napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
857        std::string nodeId = value2String(env, args[0]);
858        auto node = nodeHandleMap_[nodeId];
859
860        int32_t min = 0;
861        napi_get_value_int32( env, args[1], &min);
862
863        int32_t max = 0;
864        napi_get_value_int32(env, args[2], &max);
865
866        int32_t expected = 0;
867        napi_get_value_int32(env, args[3], &expected);
868        OH_NativeXComponent_ExpectedRateRange range = {
869            .min = min,
870            .max = max,
871            .expected = expected
872        };
873        OH_ArkUI_XComponent_SetExpectedFrameRateRange(node, range); // 设置期望帧率
874        return nullptr;
875    }
876
877    napi_value PluginManager::SetNeedSoftKeyboard(napi_env env, napi_callback_info info)
878    {
879        size_t argc = 2;
880        napi_value args[2] = {nullptr};
881        napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
882        std::string nodeId = value2String(env, args[0]);
883        auto node = nodeHandleMap_[nodeId];
884
885        bool needSoftKeyboard = false;
886        napi_get_value_bool( env, args[1], &needSoftKeyboard);
887        OH_ArkUI_XComponent_SetNeedSoftKeyboard(node, needSoftKeyboard); // 设置是否需要软键盘
888        return nullptr;
889    }
890    ```
891
892    (2) 定义Surface创建成功,发生改变,销毁和事件,可变帧率回调接口。
893
894    ```c++
895    void OnSurfaceCreated(OH_ArkUI_SurfaceHolder *holder) {
896        auto window = OH_ArkUI_XComponent_GetNativeWindow(holder); // 获取native window
897        auto render = new EGLRender();
898        PluginManager::renderMap_[holder] = render;
899        render->SetUpEGLContext(window);
900    }
901
902    void OnSurfaceChanged(OH_ArkUI_SurfaceHolder *holder, uint64_t width, uint64_t height) {
903        if (PluginManager::renderMap_.count(holder)) {
904            auto render = PluginManager::renderMap_[holder];
905            render->SetEGLWindowSize(width, height); // 设置绘制区域大小
906            render->DrawStar(true); // 绘制五角星
907        }
908    }
909
910    void OnSurfaceDestroyed(OH_ArkUI_SurfaceHolder *holder) {
911        OH_LOG_Print(LOG_APP, LOG_ERROR, 0xff00, "onBind", "on destroyed");
912        if (PluginManager::renderMap_.count(holder)) { // 销毁render对象
913            auto render = PluginManager::renderMap_[holder];
914            delete render;
915            PluginManager::renderMap_.erase(holder);
916        }
917        if (PluginManager::callbackMap_.count(holder)) {
918            auto callback = PluginManager::callbackMap_[holder];
919            OH_ArkUI_SurfaceHolder_RemoveSurfaceCallback(holder, callback); // 移除SurfaceCallback
920            OH_ArkUI_SurfaceCallback_Dispose(callback); // 销毁surfaceCallback
921            PluginManager::callbackMap_.erase(holder);
922        }
923        OH_ArkUI_SurfaceHolder_Dispose(holder); // 销毁surfaceHolder
924    }
925
926    void OnSurfaceShow(OH_ArkUI_SurfaceHolder* holder)
927    {
928        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "onBind", "on surface show");
929    }
930
931    void OnSurfaceHide(OH_ArkUI_SurfaceHolder* holder)
932    {
933        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "onBind", "on surface hide");
934    }
935
936    void OnFrameCallback(ArkUI_NodeHandle node, uint64_t timestamp, uint64_t targetTimestamp)
937    {
938        if (!PluginManager::surfaceHolderMap_.count(node)) {
939            return;
940        }
941        static uint64_t count = 0;
942        count++;
943        if (count % 50 == 0) {
944            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "onBind", "OnFrameCallback count = %{public}ld", count);
945        }
946    }
947
948    void onEvent(ArkUI_NodeEvent *event) {
949        auto eventType = OH_ArkUI_NodeEvent_GetEventType(event); // 获取组件事件类型
950        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "onBind", "on event");
951        if (eventType == NODE_TOUCH_EVENT) {
952            ArkUI_NodeHandle handle = OH_ArkUI_NodeEvent_GetNodeHandle(event); // 获取触发该事件的组件对象
953            auto holder = PluginManager::surfaceHolderMap_[handle];
954            if (PluginManager::renderMap_.count(holder)) {
955                auto render = PluginManager::renderMap_[holder];
956                render->DrawStar(false); // 绘制五角星
957            }
958            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "onBind", "on touch");
959        }
960    }
961    ```
962
9634. 初始化环境,包括初始化可用的EGLDisplay、确定可用的Surface配置、创建渲染区域Surface、创建并关联上下文等。
964
965    ```c++
966    // EGLConst.h
967    #include <EGL/egl.h>
968    #include <EGL/eglext.h>
969    #include <GLES3/gl3.h>
970
971    const unsigned int LOG_PRINT_DOMAIN = 0xFF00;
972
973    /**
974    * Program 错误。
975    */
976    const GLuint PROGRAM_ERROR = 0;
977
978    /**
979    * 位置错误。
980    */
981    const GLint POSITION_ERROR = -1;
982
983    /**
984    * 默认x坐标。
985    */
986    const int DEFAULT_X_POSITION = 0;
987
988    /**
989    * 默认y坐标。
990    */
991    const int DEFAULT_Y_POSITION = 0;
992
993    /**
994    * Gl 红色默认值。
995    */
996    const GLfloat GL_RED_DEFAULT = 0.0;
997
998    /**
999    * Gl 绿色默认值。
1000    */
1001    const GLfloat GL_GREEN_DEFAULT = 0.0;
1002
1003    /**
1004    * Gl 蓝色默认值。
1005    */
1006    const GLfloat GL_BLUE_DEFAULT = 0.0;
1007
1008    /**
1009    * Gl 透明度。
1010    */
1011    const GLfloat GL_ALPHA_DEFAULT = 1.0;
1012
1013    /**
1014    * Pointer 数量。
1015    */
1016    const GLint POINTER_SIZE = 2;
1017
1018    /**
1019    * Triangle fan 尺寸。
1020    */
1021    const GLsizei TRIANGLE_FAN_SIZE = 4;
1022
1023    /**
1024    *  50%。
1025    */
1026    const float FIFTY_PERCENT = 0.5;
1027
1028    /**
1029    * 位置句柄名字。
1030    */
1031    const char POSITION_NAME[] = "a_position";
1032
1033    /**
1034    * 背景色 #f4f4f4。
1035    */
1036    const GLfloat BACKGROUND_COLOR[] = {244.0f / 255, 244.0f / 255, 244.0f / 255, 1.0f};
1037
1038    /**
1039    * Draw 颜色 #7E8FFB。
1040    */
1041    const GLfloat DRAW_COLOR[] = {126.0f / 255, 143.0f / 255, 251.0f / 255, 1.0f};
1042
1043    /**
1044    * Change 颜色 #92D6CC。
1045    */
1046    const GLfloat CHANGE_COLOR[] = {146.0f / 255, 214.0f / 255, 204.0f / 255, 1.0f};
1047
1048    /**
1049    * 背景区域。
1050    */
1051    const GLfloat BACKGROUND_RECTANGLE_VERTICES[] = {-1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, -1.0f, -1.0f};
1052
1053    const EGLint ATTRIB_LIST[] = {
1054        // 键,值。
1055        EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8,
1056        EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
1057        // 结束。
1058        EGL_NONE};
1059
1060    const EGLint CONTEXT_ATTRIBS[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
1061
1062    /**
1063    * 顶点着色器。
1064    */
1065    const char VERTEX_SHADER[] = "#version 300 es\n"
1066                                "layout(location = 0) in vec4 a_position;\n"
1067                                "layout(location = 1) in vec4 a_color;   \n"
1068                                "out vec4 v_color;                       \n"
1069                                "void main()                             \n"
1070                                "{                                       \n"
1071                                "   gl_Position = a_position;            \n"
1072                                "   v_color = a_color;                   \n"
1073                                "}                                       \n";
1074
1075    /**
1076    * 片元着色器。
1077    */
1078    const char FRAGMENT_SHADER[] = "#version 300 es\n"
1079                                "precision mediump float;                  \n"
1080                                "in vec4 v_color;                          \n"
1081                                "out vec4 fragColor;                       \n"
1082                                "void main()                               \n"
1083                                "{                                         \n"
1084                                "   fragColor = v_color;                   \n"
1085                                "}                                         \n";
1086    ```
1087
1088    ```c++
1089    // EGLRender.h
1090    #include "EGLConst.h"
1091    #include <EGL/egl.h>
1092    #include <EGL/eglext.h>
1093    #include <EGL/eglplatform.h>
1094    #include <GLES3/gl3.h>
1095    #include <string>
1096
1097    class EGLRender {
1098    public:
1099        bool SetUpEGLContext(void *window);
1100        void SetEGLWindowSize(int width, int height);
1101        void DrawStar(bool drawColor);
1102
1103        std::string xcomponentId;
1104        EGLNativeWindowType eglWindow_;
1105
1106        EGLDisplay eglDisplay_ = EGL_NO_DISPLAY;
1107        EGLConfig eglConfig_ = EGL_NO_CONFIG_KHR;
1108        EGLSurface eglSurface_ = EGL_NO_SURFACE;
1109        EGLContext eglContext_ = EGL_NO_CONTEXT;
1110        GLuint program_;
1111        int width_ = 0;
1112        int height_ = 0;
1113        ~EGLRender();
1114
1115    private:
1116        GLint PrepareDraw();
1117        bool ExecuteDraw(GLint position, const GLfloat *color, const GLfloat shapeVertices[]);
1118    };
1119    ```
1120
1121    ```c++
1122    // EGLRender.cpp
1123    #include "EGLRender.h"
1124    #include "EGLConst.h"
1125    #include <EGL/egl.h>
1126    #include <EGL/eglext.h>
1127    #include <GLES3/gl3.h>
1128    #include <cmath>
1129    #include <cstdio>
1130    #include <algorithm>
1131    #include <hilog/log.h>
1132    #include <iostream>
1133
1134    namespace {
1135    void Rotate2d(GLfloat centerX, GLfloat centerY, GLfloat *rotateX, GLfloat *rotateY, GLfloat theta) {
1136        GLfloat tempX = cos(theta) * (*rotateX - centerX) - sin(theta) * (*rotateY - centerY);
1137        GLfloat tempY = sin(theta) * (*rotateX - centerX) + cos(theta) * (*rotateY - centerY);
1138        *rotateX = tempX + centerX;
1139        *rotateY = tempY + centerY;
1140    }
1141
1142    GLuint LoadShader(GLenum type, const char *shaderSrc) {
1143        if ((type == 0) || (shaderSrc == nullptr)) {
1144            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLRender", "glCreateShader type or shaderSrc error");
1145            return PROGRAM_ERROR;
1146        }
1147
1148        GLuint shader = glCreateShader(type);
1149        if (shader == 0) {
1150            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLRender", "glCreateShader unable to load shader");
1151            return PROGRAM_ERROR;
1152        }
1153
1154        // The gl function has no return value.
1155        glShaderSource(shader, 1, &shaderSrc, nullptr);
1156        glCompileShader(shader);
1157
1158        GLint compiled;
1159        glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
1160        if (compiled != 0) {
1161            return shader;
1162        }
1163
1164        GLint infoLen = 0;
1165        glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
1166        if (infoLen <= 1) {
1167            glDeleteShader(shader);
1168            return PROGRAM_ERROR;
1169        }
1170
1171        char *infoLog = (char *)malloc(sizeof(char) * (infoLen + 1));
1172        if (infoLog != nullptr) {
1173            memset(infoLog, 0, infoLen + 1);
1174            glGetShaderInfoLog(shader, infoLen, nullptr, infoLog);
1175            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLRender", "glCompileShader error = %s", infoLog);
1176            free(infoLog);
1177            infoLog = nullptr;
1178        }
1179        glDeleteShader(shader);
1180        return PROGRAM_ERROR;
1181    }
1182
1183    // 创建program
1184    GLuint CreateProgram(const char *vertexShader, const char *fragShader) {
1185        if ((vertexShader == nullptr) || (fragShader == nullptr)) {
1186            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLRender",
1187                        "createProgram: vertexShader or fragShader is null");
1188            return PROGRAM_ERROR;
1189        }
1190
1191        GLuint vertex = LoadShader(GL_VERTEX_SHADER, vertexShader);
1192        if (vertex == PROGRAM_ERROR) {
1193            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLRender", "createProgram vertex error");
1194            return PROGRAM_ERROR;
1195        }
1196
1197        GLuint fragment = LoadShader(GL_FRAGMENT_SHADER, fragShader);
1198        if (fragment == PROGRAM_ERROR) {
1199            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLRender", "createProgram fragment error");
1200            return PROGRAM_ERROR;
1201        }
1202
1203        GLuint program = glCreateProgram();
1204        if (program == PROGRAM_ERROR) {
1205            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLRender", "createProgram program error");
1206            glDeleteShader(vertex);
1207            glDeleteShader(fragment);
1208            return PROGRAM_ERROR;
1209        }
1210
1211        //  该gl函数没有返回值。
1212        glAttachShader(program, vertex);
1213        glAttachShader(program, fragment);
1214        glLinkProgram(program);
1215
1216        GLint linked;
1217        glGetProgramiv(program, GL_LINK_STATUS, &linked);
1218        if (linked != 0) {
1219            glDeleteShader(vertex);
1220            glDeleteShader(fragment);
1221            return program;
1222        }
1223
1224        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLRender", "createProgram linked error");
1225        GLint infoLen = 0;
1226        glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLen);
1227        if (infoLen > 1) {
1228            char *infoLog = (char *)malloc(sizeof(char) * (infoLen + 1));
1229            memset(infoLog, 0, infoLen + 1);
1230            glGetProgramInfoLog(program, infoLen, nullptr, infoLog);
1231            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLRender", "glLinkProgram error = %s", infoLog);
1232            free(infoLog);
1233            infoLog = nullptr;
1234        }
1235        glDeleteShader(vertex);
1236        glDeleteShader(fragment);
1237        glDeleteProgram(program);
1238        return PROGRAM_ERROR;
1239    }
1240    } // namespace
1241
1242    bool EGLRender::SetUpEGLContext(void *window) {
1243        OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "EGLRender", "EglContextInit execute");
1244        eglWindow_ = (EGLNativeWindowType)(window);
1245        // 初始化display。
1246        eglDisplay_ = eglGetDisplay(EGL_DEFAULT_DISPLAY);
1247        if (eglDisplay_ == EGL_NO_DISPLAY) {
1248            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLRender", "eglGetDisplay: unable to get EGL display");
1249            return false;
1250        }
1251        EGLint majorVersion;
1252        EGLint minorVersion;
1253        if (!eglInitialize(eglDisplay_, &majorVersion, &minorVersion)) {
1254            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLRender",
1255                        "eglInitialize: unable to get initialize EGL display");
1256            return false;
1257        };
1258        // 选择配置。
1259        const EGLint maxConfigSize = 1;
1260        EGLint numConfigs;
1261        if (!eglChooseConfig(eglDisplay_, ATTRIB_LIST, &eglConfig_, maxConfigSize, &numConfigs)) {
1262            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLRender", "eglChooseConfig: unable to choose configs");
1263            return false;
1264        };
1265        // 创建环境。
1266        // 创建 Surface。
1267        eglSurface_ = eglCreateWindowSurface(eglDisplay_, eglConfig_, eglWindow_, NULL);
1268        if (eglSurface_ == nullptr) {
1269            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLRender",
1270                        "eglCreateWindowSurface: unable to create Surface");
1271            return false;
1272        }
1273        // 创建上下文。
1274        eglContext_ = eglCreateContext(eglDisplay_, eglConfig_, EGL_NO_CONTEXT, CONTEXT_ATTRIBS);
1275        if (!eglMakeCurrent(eglDisplay_, eglSurface_, eglSurface_, eglContext_)) {
1276            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLRender", "eglMakeCurrent failed");
1277            return false;
1278        }
1279        // 创建program。
1280        program_ = CreateProgram(VERTEX_SHADER, FRAGMENT_SHADER);
1281        if (program_ == PROGRAM_ERROR) {
1282            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLRender", "CreateProgram: unable to create program");
1283            return false;
1284        }
1285        return true;
1286    }
1287
1288    GLint EGLRender::PrepareDraw() {
1289        if ((eglDisplay_ == nullptr) || (eglSurface_ == nullptr) || (eglContext_ == nullptr) ||
1290            (!eglMakeCurrent(eglDisplay_, eglSurface_, eglSurface_, eglContext_))) {
1291            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLRender", "PrepareDraw: param error");
1292            return POSITION_ERROR;
1293        }
1294
1295        // 该gl函数没有返回值。
1296        glViewport(DEFAULT_X_POSITION, DEFAULT_Y_POSITION, width_, height_);
1297        glClearColor(GL_RED_DEFAULT, GL_GREEN_DEFAULT, GL_BLUE_DEFAULT, GL_ALPHA_DEFAULT);
1298        glClear(GL_COLOR_BUFFER_BIT);
1299        glUseProgram(program_);
1300
1301        return glGetAttribLocation(program_, POSITION_NAME);
1302    }
1303
1304    // 绘制五角星
1305    void EGLRender::DrawStar(bool drawColor) {
1306        OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "EGLRender", "Draw");
1307        GLint position = PrepareDraw();
1308        if (position == POSITION_ERROR) {
1309            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLRender", "Draw get position failed");
1310            return;
1311        }
1312
1313        // 绘制背景
1314        if (!ExecuteDraw(position, BACKGROUND_COLOR, BACKGROUND_RECTANGLE_VERTICES)) {
1315            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLRender", "Draw execute draw background failed");
1316            return;
1317        }
1318
1319        // 将其划分为五个四边形,并计算其中一个四边形的顶点
1320        GLfloat rotateX = 0;
1321        GLfloat rotateY = FIFTY_PERCENT * height_;
1322        GLfloat centerX = 0;
1323        // 将角度 54° 和 18° 转换为弧度
1324        GLfloat centerY = -rotateY * (M_PI / 180 * 54) * (M_PI / 180 * 18);
1325        // 将角度 18° 转换为弧度
1326        GLfloat leftX = -rotateY * (M_PI / 180 * 18);
1327        GLfloat leftY = 0;
1328        // 将角度 18° 转换为弧度
1329        GLfloat rightX = rotateY * (M_PI / 180 * 18);
1330        GLfloat rightY = 0;
1331
1332        // 确定绘制四边形的顶点,使用绘制区域的百分比表示
1333        const GLfloat shapeVertices[] = {centerX / width_, centerY / height_, leftX / width_,  leftY / height_,
1334                                        rotateX / width_, rotateY / height_, rightX / width_, rightY / height_};
1335        auto color = drawColor ? DRAW_COLOR : CHANGE_COLOR;
1336        if (!ExecuteDraw(position, color, shapeVertices)) {
1337            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLRender", "Draw execute draw shape failed");
1338            return;
1339        }
1340
1341        // 将角度 72° 转换为弧度
1342        GLfloat rad = M_PI / 180 * 72;
1343        // 旋转四次。
1344        for (int i = 0; i < 4; ++i) {
1345            // 旋转得其他四个四边形的顶点
1346            Rotate2d(centerX, centerY, &rotateX, &rotateY, rad);
1347            Rotate2d(centerX, centerY, &leftX, &leftY, rad);
1348            Rotate2d(centerX, centerY, &rightX, &rightY, rad);
1349
1350            // 确定绘制四边形的顶点,使用绘制区域的百分比表示
1351            const GLfloat shapeVertices[] = {centerX / width_, centerY / height_, leftX / width_,  leftY / height_,
1352                                            rotateX / width_, rotateY / height_, rightX / width_, rightY / height_};
1353
1354            // 绘制图形
1355            if (!ExecuteDraw(position, color, shapeVertices)) {
1356                OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLRender", "Draw execute draw shape failed");
1357                return;
1358            }
1359        }
1360        // 将绘制命令提交给GPU,GPU执行完成后将渲染结果显示到屏幕
1361        glFlush();
1362        glFinish();
1363        if (!eglSwapBuffers(eglDisplay_, eglSurface_)) {
1364            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLRender", "Draw FinishDraw failed");
1365            return;
1366        }
1367    }
1368
1369    bool EGLRender::ExecuteDraw(GLint position, const GLfloat *color, const GLfloat shapeVertices[]) {
1370        if ((position > 0) || (color == nullptr)) {
1371            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLRender", "ExecuteDraw: param error");
1372            return false;
1373        }
1374
1375        // 该gl函数没有返回值。
1376        glVertexAttribPointer(position, POINTER_SIZE, GL_FLOAT, GL_FALSE, 0, shapeVertices);
1377        glEnableVertexAttribArray(position);
1378        glVertexAttrib4fv(1, color);
1379        glDrawArrays(GL_TRIANGLE_FAN, 0, TRIANGLE_FAN_SIZE);
1380        glDisableVertexAttribArray(position);
1381
1382        return true;
1383    }
1384
1385    void EGLRender::SetEGLWindowSize(int width, int height) {
1386        width_ = width;
1387        height_ = height;
1388    }
1389
1390    // 释放相关资源
1391    EGLRender::~EGLRender() {
1392        if ((eglDisplay_ == nullptr) || (eglSurface_ == nullptr) || (!eglDestroySurface(eglDisplay_, eglSurface_))) {
1393            OH_LOG_Print(LOG_APP, LOG_ERROR, 0xff00, "EGLRender", "Release eglDestroySurface failed");
1394        }
1395
1396        if ((eglDisplay_ == nullptr) || (eglContext_ == nullptr) || (!eglDestroyContext(eglDisplay_, eglContext_))) {
1397            OH_LOG_Print(LOG_APP, LOG_ERROR, 0xff00, "EGLRender", "Release eglDestroySurface failed");
1398        }
1399
1400        if ((eglDisplay_ == nullptr) || (!eglTerminate(eglDisplay_))) {
1401            OH_LOG_Print(LOG_APP, LOG_ERROR, 0xff00, "EGLRender", "Release eglDestroySurface failed");
1402        }
1403    }
1404    ```
1405
14065. CMakeLists,使用CMake工具链将C++源代码编译成动态链接库文件。
1407
1408    ```CMake
1409    # the minimum version of CMake.
1410    cmake_minimum_required(VERSION 3.5.0)
1411    project(LCNXComponent2)
1412
1413    set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
1414
1415    if(DEFINED PACKAGE_FIND_FILE)
1416        include(${PACKAGE_FIND_FILE})
1417    endif()
1418
1419    include_directories(${NATIVERENDER_ROOT_PATH}
1420                        ${NATIVERENDER_ROOT_PATH}/render
1421                        ${NATIVERENDER_ROOT_PATH}/manager)
1422
1423    add_library(nativerender SHARED
1424                render/EGLRender.cpp
1425                manager/plugin_manager.cpp
1426                napi_init.cpp)
1427    find_library(
1428        # 设置路径变量的名称。
1429        EGL-lib
1430        # 指定要让CMake查找的NDK库的名称。
1431        EGL
1432    )
1433
1434    find_library(
1435        # 设置路径变量的名称。
1436        GLES-lib
1437        # 指定要让CMake查找的NDK库的名称。
1438        GLESv3
1439    )
1440
1441    find_library(
1442        # 设置路径变量的名称。
1443        hilog-lib
1444        # 指定要让CMake查找的NDK库的名称。
1445        hilog_ndk.z
1446    )
1447
1448    find_library(
1449        # 设置路径变量的名称。
1450        libace-lib
1451        # 指定要让CMake查找的NDK库的名称。
1452        ace_ndk.z
1453    )
1454
1455    find_library(
1456        # 设置路径变量的名称。
1457        libnapi-lib
1458        # 指定要让CMake查找的NDK库的名称。
1459        ace_napi.z
1460    )
1461
1462    find_library(
1463        # 设置路径变量的名称。
1464        libuv-lib
1465        # 指定要让CMake查找的NDK库的名称。
1466        uv
1467    )
1468
1469    target_link_libraries(nativerender PUBLIC ${EGL-lib} ${GLES-lib} ${hilog-lib} ${libace-lib} ${libnapi-lib} ${libuv-lib} libnative_window.so)
1470    ```
1471
1472<!--RP3-->上述用例具体实现可参考[NativeXComponent](https://gitcode.com/openharmony/applications_app_samples/tree/master/code/BasicFeature/Native/NativeXComponent)。<!--RP3End-->
1473
1474![示意图](./figures/drawStar.jpeg)
1475
1476## 使用NativeXComponent管理Surface生命周期
1477
1478与上述两种场景不同,本场景在Native侧使用ArkUI NDK 接口创建XComponent组件进行自定义绘制。具体步骤包括:创建组件,获取NativeXComponent实例,注册XComponent的生命周期回调及触摸、鼠标、按键等事件回调,通过回调获取NativeWindow,使用OpenGL ES/EGL接口在XComponent组件上进行图形绘制,最后在ArkTS层使用ContentSlot占位组件进行挂载显示。针对Native侧创建XComponent的主要开发场景如下:
1479
1480- 利用Native XComponent提供的接口注册XComponent的生命周期和事件回调。
1481- 在这些回调中进行初始化环境、获取当前状态、响应各类事件的开发。
1482- 利用NativeWindow和EGL接口开发自定义绘制内容以及申请和提交Buffer到图形队列。
1483
1484**约束条件**:
1485
1486构造XComponent时需要根据需要的type使用对应的节点类型。
1487
1488> **说明**:
1489>
1490> 1. Native侧的OH_NativeXComponent缓存在字典中,其key需要保证其唯一性,当对应的XComponent销毁后,需要及时从字典里将其删除。
1491>
1492> 2. 多个XComponent开发时,缓存Native侧资源需要保证key是唯一的,key推荐使用id+随机数或者surfaceId。
1493
1494**生命周期**:
1495
1496- OnSurfaceCreated回调
1497
1498  触发时刻:XComponent创建完成且创建好Surface后触发。
1499
1500  Native侧OnSurfaceCreated的时序如下图:
1501
1502  ![OnSurfaceCreated](./figures/onSurfaceCreated.png)
1503
1504- OnSurfaceChanged回调
1505
1506  触发时刻:Surface大小变化触发重新布局后触发。
1507
1508  Native侧OnSurfaceChanged的时序如下图:
1509
1510  ![OnSurfaceChanged](./figures/onSurfaceChanged.png)
1511
1512- OnSurfaceDestroyed回调
1513
1514  触发时刻:XComponent组件被销毁时触发,与一般ArkUI的组件销毁时机一致。
1515
1516  Native侧OnSurfaceDestroyed的时序图:
1517
1518  ![OnSurfaceDestroyed](./figures/onSurfaceDestroyed.png)
1519
1520**接口说明**
1521
1522| 接口名                                                       | 描述                                                         |
1523| ------------------------------------------------------------ | ------------------------------------------------------------ |
1524| OH_NativeXComponent_GetXComponentId(OH_NativeXComponent* component, char* id, uint64_t* size) | 获取XComponent的id。                                         |
1525| OH_NativeXComponent_GetXComponentSize(OH_NativeXComponent* component, const void* window, uint64_t* width, uint64_t* height) | 获取XComponent持有的Surface的大小。                          |
1526| OH_NativeXComponent_GetXComponentOffset(OH_NativeXComponent* component, const void* window, double* x, double* y) | 获取XComponent持有的Surface相对其父组件左顶点的偏移量。      |
1527| OH_NativeXComponent_GetTouchEvent(OH_NativeXComponent* component, const void* window, OH_NativeXComponent_TouchEvent* touchEvent) | 获取由XComponent触发的触摸事件。touchEvent内的具体属性值可参考[OH_NativeXComponent_TouchEvent](../reference/apis-arkui/capi-oh-nativexcomponent-native-xcomponent-oh-nativexcomponent-touchevent.md)。 |
1528| OH_NativeXComponent_GetTouchPointToolType(OH_NativeXComponent* component, uint32_t pointIndex, OH_NativeXComponent_TouchPointToolType* toolType) | 获取XComponent触摸点的工具类型。                             |
1529| OH_NativeXComponent_GetTouchPointTiltX(OH_NativeXComponent* component, uint32_t pointIndex, float* tiltX) | 获取XComponent触摸点处相对X轴的倾斜角度。                    |
1530| OH_NativeXComponent_GetTouchPointTiltY(OH_NativeXComponent* component, uint32_t pointIndex, float* tiltY) | 获取XComponent触摸点处相对Y轴的倾斜角度。                    |
1531| OH_NativeXComponent_GetMouseEvent(OH_NativeXComponent* component, const void* window, OH_NativeXComponent_MouseEvent* mouseEvent) | 获取由XComponent触发的鼠标事件。                             |
1532| OH_NativeXComponent_RegisterCallback(OH_NativeXComponent* component, OH_NativeXComponent_Callback* callback) | 为此OH_NativeXComponent实例注册生命周期和触摸事件回调。      |
1533| OH_NativeXComponent_RegisterMouseEventCallback(OH_NativeXComponent* component, OH_NativeXComponent_MouseEvent_Callback* callback) | 为此OH_NativeXComponent实例注册鼠标事件回调。                |
1534| OH_NativeXComponent_RegisterFocusEventCallback(OH_NativeXComponent* component, void (\*callback)(OH_NativeXComponent* component, void* window)) | 为此OH_NativeXComponent实例注册获得焦点事件回调。            |
1535| OH_NativeXComponent_RegisterKeyEventCallback(OH_NativeXComponent* component, void (\*callback)(OH_NativeXComponent* component, void* window)) | 为此OH_NativeXComponent实例注册按键事件回调。                |
1536| OH_NativeXComponent_RegisterBlurEventCallback(OH_NativeXComponent* component, void (\*callback)(OH_NativeXComponent* component, void* window)) | 为此OH_NativeXComponent实例注册失去焦点事件回调。            |
1537| OH_NativeXComponent_GetKeyEvent(OH_NativeXComponent* component, OH_NativeXComponent_KeyEvent\** keyEvent) | 获取由XComponent触发的按键事件。                             |
1538| OH_NativeXComponent_GetKeyEventAction(OH_NativeXComponent_KeyEvent* keyEvent, OH_NativeXComponent_KeyAction* action) | 获取按键事件的动作。                                         |
1539| OH_NativeXComponent_GetKeyEventCode(OH_NativeXComponent_KeyEvent* keyEvent, OH_NativeXComponent_KeyCode* code) | 获取按键事件的键码值。                                       |
1540| OH_NativeXComponent_GetKeyEventSourceType(OH_NativeXComponent_KeyEvent* keyEvent, OH_NativeXComponent_EventSourceType* sourceType) | 获取按键事件的输入源类型。                                   |
1541| OH_NativeXComponent_GetKeyEventDeviceId(OH_NativeXComponent_KeyEvent* keyEvent, int64_t* deviceId) | 获取按键事件的设备ID。                                       |
1542| OH_NativeXComponent_GetKeyEventTimestamp(OH_NativeXComponent_KeyEvent* keyEvent, int64_t* timestamp) | 获取按键事件的时间戳。                                       |
1543| OH_ArkUI_QueryModuleInterfaceByName(ArkUI_NativeAPIVariantKind type, const char* structName) | 获取指定类型的Native模块接口集合。                           |
1544| OH_ArkUI_GetNodeContentFromNapiValue(napi_env env, napi_value value, ArkUI_NodeContentHandle* content) | 获取ArkTS侧创建的NodeContent对象映射到Native侧的ArkUI_NodeContentHandle。 |
1545| OH_ArkUI_NodeContent_SetUserData(ArkUI_NodeContentHandle content, void* userData) | 在NodeContent对象上保存自定义数据。                          |
1546| OH_ArkUI_NodeContentEvent_GetNodeContentHandle(ArkUI_NodeContentEvent* event) | 获取触发事件的NodeContent对象。                              |
1547| OH_ArkUI_NodeContent_GetUserData(ArkUI_NodeContentHandle content) | 获取在NodeContent对象上保存的自定义数据。                    |
1548| OH_ArkUI_NodeContentEvent_GetEventType(ArkUI_NodeContentEvent* event) | 获取触发NodeContent事件的事件类型。                          |
1549| OH_ArkUI_NodeContent_AddNode(ArkUI_NodeContentHandle content, ArkUI_NodeHandle node) | 将一个ArkUI组件节点添加到对应的NodeContent对象下。           |
1550| OH_ArkUI_NodeContent_RegisterCallback(ArkUI_NodeContentHandle content, ArkUI_NodeContentCallback callback) | 注册NodeContent事件函数。                                    |
1551| OH_NativeXComponent_GetNativeXComponent(ArkUI_NodeHandle node) | 基于Native接口创建的组件实例获取OH_NativeXComponent类型的指针。 |
1552| OH_NativeXComponent_GetHistoricalPoints(OH_NativeXComponent* component, const void* window, int32_t* size, OH_NativeXComponent_HistoricalPoint** historicalPoints ) | 获取当前XComponent触摸事件的历史点信息。由于部分输入设备上报触点的频率非常高(最高可达每1 ms上报一次),而对输入事件的响应通常是为了使UI界面发生变化以响应用户操作,如果将触摸事件按照上报触点的频率如此高频率上报给应用,大多会造成冗余,因此触摸事件在一帧内只会上报一次给应用。在当前帧内上报的触点均作为历史点保存,如果应用需要直接处理这些数据,可调用该接口获取历史点信息。历史接触点historicalPoints的具体规格可参考[重采样与历史点](arkts-interaction-development-guide-touch-screen.md#重采样与历史点)。 |
1553
1554> **说明 :**
1555>
1556> 上述接口不支持跨线程访问。
1557>
1558> XComponent销毁(onSurfaceDestroyed回调触发后)时会释放上述接口中获取的OH_NativeXComponent和window对象。如果再次使用获取的对象,有可能会导致使用野指针或空指针的崩溃问题。
1559
1560**开发步骤**
1561
1562以下步骤以SURFACE类型为例,描述了如何使用`XComponent组件`调用`Node-API`接口来创建`EGL/GLES`环境,实现在主页面绘制图形,并可以改变图形的颜色。
1563
15641. 在界面中定义XComponent。
1565
1566    ```typescript
1567    import nativeNode from 'libnativenode.so';
1568    import {NodeContent} from '@kit.ArkUI';
1569
1570    @Entry
1571    @Component
1572    struct Index {
1573      @State currentStatus: string = "init";
1574      private nodeContent: NodeContent = new NodeContent();
1575      aboutToAppear():void{
1576        // 通过C-API创建节点,并添加到管理器nodeContent上
1577        nativeNode.createNativeNode(this.nodeContent);
1578      }
1579
1580      build() {
1581        Column() {
1582          Row() {
1583            Text('Native XComponent Sample')
1584            .fontSize('24fp')
1585            .fontWeight(500)
1586            .margin({
1587                left: 24,
1588                top: 12
1589            })
1590          }
1591          .margin({ top: 24 })
1592          .width('100%')
1593          .height(56)
1594
1595          Column({ space: 10 }) {
1596            // 显示nodeContent管理器里存放的Native侧的组件
1597            ContentSlot(this.nodeContent);
1598
1599            Text(this.currentStatus)
1600            .fontSize('24fp')
1601            .fontWeight(500)
1602          }
1603          .onClick(() => {
1604            let hasChangeColor: boolean = false;
1605            // 获取当前绘制内容状态
1606            if (nativeNode.getStatus()) {
1607              hasChangeColor = nativeNode.getStatus().hasChangeColor;
1608            }
1609            if (hasChangeColor) {
1610              this.currentStatus = "change color";
1611            }
1612          })
1613          .margin({
1614            top: 27,
1615            left: 12,
1616            right: 12
1617          })
1618          .height('40%')
1619          .width('90%')
1620
1621          Row() {
1622            Button('Draw Star')
1623            .fontSize('16fp')
1624            .fontWeight(500)
1625            .margin({ bottom: 24 })
1626            .onClick(() => {
1627              // 调用drawPattern绘制内容
1628              nativeNode.drawPattern();
1629              let hasDraw: boolean = false;
1630              // 获取当前绘制内容状态
1631              if (nativeNode.getStatus()) {
1632                hasDraw = nativeNode.getStatus().hasDraw;
1633              }
1634              if (hasDraw) {
1635                this.currentStatus = "draw star";
1636              }
1637            })
1638            .width('53.6%')
1639            .height(40)
1640          }
1641          .width('100%')
1642          .justifyContent(FlexAlign.Center)
1643          .alignItems(VerticalAlign.Bottom)
1644          .layoutWeight(1)
1645        }
1646        .width('100%')
1647        .height('100%')
1648      }
1649    }
1650    ```
1651
16522. Node-API模块注册,具体使用请参考[Node-API开发规范](../napi/napi-guidelines.md)。
1653
1654    ```c++
1655    #include <hilog/log.h>
1656    #include "common/common.h"
1657    #include "manager/plugin_manager.h"
1658
1659    // 在napi_init.cpp文件中,Init方法注册接口函数,从而将封装的C++方法传递出来,供ArkTS侧调用
1660    EXTERN_C_START
1661    static napi_value Init(napi_env env, napi_value exports) {
1662        // ...
1663        // 向ArkTS侧暴露接口
1664        napi_property_descriptor desc[] = {
1665            {"createNativeNode", nullptr, PluginManager::createNativeNode, nullptr, nullptr, nullptr,
1666            napi_default, nullptr },
1667            {"getStatus", nullptr, PluginManager::GetXComponentStatus, nullptr, nullptr,
1668            nullptr, napi_default, nullptr},
1669            {"drawPattern", nullptr, PluginManager::NapiDrawPattern, nullptr, nullptr,
1670            nullptr, napi_default, nullptr}
1671        };
1672        if (napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc) != napi_ok) {
1673            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Init", "napi_define_properties failed");
1674            return nullptr;
1675        }
1676        return exports;
1677    }
1678    EXTERN_C_END
1679
1680    // 编写接口的描述信息,根据实际需要可以修改对应参数
1681    static napi_module nativerenderModule = {
1682        .nm_version = 1,
1683        .nm_flags = 0,
1684        .nm_filename = nullptr,
1685        // 入口函数
1686        .nm_register_func = Init,// 指定加载对应模块时的回调函数
1687        // 模块名称
1688        .nm_modname =
1689            "nativerender", // 指定模块名称,对于XComponent相关开发,这个名称必须和ArkTS侧XComponent中libraryname的值保持一致
1690        .nm_priv = ((void *)0),
1691        .reserved = {0}};
1692
1693    // __attribute__((constructor))修饰的方法由系统自动调用,使用Node-API接口napi_module_register()传入模块描述信息进行模块注册
1694    extern "C" __attribute__((constructor)) void RegisterModule(void) { napi_module_register(&nativerenderModule); }
1695    ```
1696
16973. 注册XComponent事件回调,使用Node-API实现XComponent事件回调函数。
1698
1699    (1) 定义Surface创建成功,发生改变,销毁和XComponent的touch事件回调接口。
1700
1701    ```c++
1702    // 在头文件中定义PluginManager类
1703    class PluginManager {
1704    public:
1705        static OH_NativeXComponent_Callback callback_;
1706        PluginManager();
1707        ~PluginManager();
1708        static PluginManager* GetInstance()
1709        {
1710            return &PluginManager::pluginManager_;
1711        }
1712
1713        static napi_value createNativeNode(napi_env env, napi_callback_info info);
1714        static napi_value GetXComponentStatus(napi_env env, napi_callback_info info);
1715        static napi_value NapiDrawPattern(napi_env env, napi_callback_info info);
1716
1717        // CAPI XComponent
1718        void OnSurfaceChanged(OH_NativeXComponent* component, void* window);
1719        void OnSurfaceDestroyed(OH_NativeXComponent* component, void* window);
1720        void DispatchTouchEvent(OH_NativeXComponent* component, void* window);
1721        void OnSurfaceCreated(OH_NativeXComponent* component, void* window);
1722
1723    public:
1724        EGLCore *eglcore_;
1725        uint64_t width_;
1726        uint64_t height_;
1727        OH_NativeXComponent_TouchEvent touchEvent_;
1728        static int32_t hasDraw_;
1729        static int32_t hasChangeColor_;
1730
1731    private:
1732        static PluginManager pluginManager_;
1733        std::unordered_map<std::string, OH_NativeXComponent*> nativeXComponentMap_;
1734        std::unordered_map<std::string, PluginManager*> pluginManagerMap_;
1735    };
1736    ```
1737
1738    ```c++
1739    // 定义一个函数OnSurfaceCreatedCB(),封装初始化环境与绘制背景
1740    void OnSurfaceCreatedCB(OH_NativeXComponent *component, void *window) {
1741        // ...
1742        // 初始化环境与绘制背景
1743        auto *pluginManager = PluginManager::GetInstance();
1744        pluginManager->OnSurfaceCreated(component, window);
1745    }
1746
1747    // 定义一个函数OnSurfaceChangedCB()
1748    void OnSurfaceChangedCB(OH_NativeXComponent *component, void *window) {
1749        // ...
1750        auto *pluginManager = PluginManager::GetInstance();
1751        // 封装OnSurfaceChanged方法
1752        pluginManager->OnSurfaceChanged(component, window);
1753    }
1754
1755    // 定义一个函数OnSurfaceDestroyedCB(),将PluginRender类内释放资源的方法Release()封装在其中
1756    void OnSurfaceDestroyedCB(OH_NativeXComponent *component, void *window) {
1757        // ...
1758        auto *pluginManager = PluginManager::GetInstance();
1759        pluginManager->OnSurfaceDestroyed(component, window);
1760    }
1761
1762    // 定义一个函数DispatchTouchEventCB(),响应触摸事件时触发该回调
1763    void DispatchTouchEventCB(OH_NativeXComponent *component, void *window) {
1764        // ...
1765        auto *pluginManager = PluginManager::GetInstance();
1766        pluginManager->DispatchTouchEvent(component, window);
1767    }
1768    ```
1769
1770    (2) 定义createNativeNode方法,暴露到ArkTS侧的createNativeNode()方法会执行该方法。
1771
1772    ```c++
1773    napi_value PluginManager::createNativeNode(napi_env env, napi_callback_info info)
1774    {
1775        if ((env == nullptr) || (info == nullptr)) {
1776            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "CreateNativeNode env or info is null");
1777            return nullptr;
1778        }
1779        size_t argCnt = 2;
1780        napi_value args[2] = { nullptr, nullptr };
1781        if (napi_get_cb_info(env, info, &argCnt, args, nullptr, nullptr) != napi_ok) {
1782            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "CreateNativeNode napi_get_cb_info failed");
1783            return nullptr;
1784        }
1785        if (argCnt != ARG_CNT) {
1786            napi_throw_type_error(env, NULL, "Wrong number of arguments");
1787            return nullptr;
1788        }
1789        ArkUI_NodeContentHandle nodeContentHandle_ = nullptr;
1790        OH_ArkUI_GetNodeContentFromNapiValue(env, args[0], &nodeContentHandle_);
1791        nodeAPI = reinterpret_cast<ArkUI_NativeNodeAPI_1*>(
1792            OH_ArkUI_QueryModuleInterfaceByName(ARKUI_NATIVE_NODE, "ArkUI_NativeNodeAPI_1")
1793        );
1794        std::string tag = value2String(env, args[1]);
1795        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "tag=%{public}s", tag.c_str());
1796        int32_t ret = OH_ArkUI_NodeContent_SetUserData(nodeContentHandle_, new std::string(tag));
1797        if (ret != ARKUI_ERROR_CODE_NO_ERROR) {
1798            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "setUserData failed error=%{public}d", ret);
1799        }
1800        if (nodeAPI != nullptr && nodeAPI->createNode != nullptr && nodeAPI->addChild != nullptr) {
1801            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager",
1802                        "CreateNativeNode tag=%{public}s", tag.c_str());
1803            auto nodeContentEvent = [](ArkUI_NodeContentEvent *event) {
1804                ArkUI_NodeContentHandle handle = OH_ArkUI_NodeContentEvent_GetNodeContentHandle(event);
1805                std::string *userDate = reinterpret_cast<std::string*>(OH_ArkUI_NodeContent_GetUserData(handle));
1806                if (OH_ArkUI_NodeContentEvent_GetEventType(event) == NODE_CONTENT_EVENT_ON_ATTACH_TO_WINDOW) {
1807                    ArkUI_NodeHandle testNode;
1808                    if (userDate) {
1809                        testNode = CreateNodeHandle(*userDate);
1810                        delete userDate;
1811                        userDate = nullptr;
1812                    } else {
1813                        testNode = CreateNodeHandle("noUserData");
1814                    }
1815                    OH_ArkUI_NodeContent_AddNode(handle, testNode);
1816                }
1817            };
1818            OH_ArkUI_NodeContent_RegisterCallback(nodeContentHandle_, nodeContentEvent);
1819        }
1820        return nullptr;
1821    }
1822
1823    ArkUI_NodeHandle CreateNodeHandle(const std::string &tag)
1824    {
1825        ArkUI_NodeHandle column = nodeAPI->createNode(ARKUI_NODE_COLUMN);
1826        ArkUI_NumberValue value[] = {480};
1827        ArkUI_NumberValue value1[] = {{.u32 = 15}, {.f32 = 15}};
1828        ArkUI_AttributeItem item = {value, 1, "changeSize"};
1829        ArkUI_AttributeItem item1 = {value1, 2};
1830        nodeAPI->setAttribute(column, NODE_WIDTH, &item);
1831        value[0].f32 = COLUMN_MARGIN;
1832        nodeAPI->setAttribute(column, NODE_MARGIN, &item);
1833        // 创建XComponent组件
1834        xc = nodeAPI->createNode(ARKUI_NODE_XCOMPONENT);
1835        // 设置XComponent组件属性
1836        value[0].u32 = ARKUI_XCOMPONENT_TYPE_SURFACE;
1837        nodeAPI->setAttribute(xc, NODE_XCOMPONENT_TYPE, &item);
1838        nodeAPI->setAttribute(xc, NODE_XCOMPONENT_ID, &item);
1839        nodeAPI->setAttribute(xc, NODE_XCOMPONENT_SURFACE_SIZE, &item1);
1840        ArkUI_NumberValue focusable[] = {1};
1841        focusable[0].i32 = 1;
1842        ArkUI_AttributeItem focusableItem = {focusable, 1};
1843        nodeAPI->setAttribute(xc, NODE_FOCUSABLE, &focusableItem);
1844        ArkUI_NumberValue valueSize[] = {480};
1845        ArkUI_AttributeItem itemSize = {valueSize, 1};
1846        valueSize[0].f32 = XC_WIDTH;
1847        nodeAPI->setAttribute(xc, NODE_WIDTH, &itemSize);
1848        valueSize[0].f32 = XC_HEIGHT;
1849        nodeAPI->setAttribute(xc, NODE_HEIGHT, &itemSize);
1850        ArkUI_AttributeItem item2 = {value, 1, "ndkxcomponent"};
1851        nodeAPI->setAttribute(xc, NODE_ID, &item2);
1852
1853        auto *nativeXComponent = OH_NativeXComponent_GetNativeXComponent(xc);
1854        if (!nativeXComponent) {
1855            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "GetNativeXComponent error");
1856            return column;
1857        }
1858        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "GetNativeXComponent success");
1859        // 注册XComponent回调函数
1860        OH_NativeXComponent_RegisterCallback(nativeXComponent, &PluginManager::callback_);
1861        auto typeRet = nodeAPI->getAttribute(xc, NODE_XCOMPONENT_TYPE);
1862        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "xcomponent type: %{public}d",
1863                    typeRet->value[0].i32);
1864        auto idRet = nodeAPI->getAttribute(xc, NODE_XCOMPONENT_ID);
1865        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "xcomponent id: %{public}s",
1866                    idRet->string);
1867        nodeAPI->addChild(column, xc);
1868        return column;
1869    }
1870    ```
1871
1872    (3) 定义NapiDrawPattern方法,暴露到ArkTS侧的drawPattern()方法会执行该方法。
1873
1874    ```c++
1875    napi_value PluginManager::NapiDrawPattern(napi_env env, napi_callback_info info) {
1876        // ...
1877        // 获取环境变量参数
1878        napi_value thisArg;
1879        if (napi_get_cb_info(env, info, nullptr, nullptr, &thisArg, nullptr) != napi_ok) {
1880            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "NapiDrawPattern: napi_get_cb_info fail");
1881            return nullptr;
1882        }
1883
1884        auto *pluginManager = PluginManager::GetInstance();
1885        // 调用绘制方法
1886        pluginManager->eglcore_->Draw(hasDraw_);
1887        OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "PluginManager", "render->eglCore_->Draw() executed");
1888
1889        return nullptr;
1890    }
1891    ```
1892
18934. 初始化环境,包括初始化可用的EGLDisplay、确定可用的Surface配置、创建渲染区域Surface、创建并关联上下文等。
1894
1895    ```c++
1896    void EGLCore::UpdateSize(int width, int height) {
1897        // width_和height_在头文件中定义
1898        width_ = width;
1899        height_ = height;
1900        if (width_ > 0) {
1901            widthPercent_ = FIFTY_PERCENT * height_ / width_;
1902        }
1903    }
1904
1905    bool EGLCore::EglContextInit(void *window, int width, int height) {
1906        // ...
1907        UpdateSize(width, height);
1908        eglWindow_ = static_cast<EGLNativeWindowType>(window);
1909
1910        // 初始化display
1911        eglDisplay_ = eglGetDisplay(EGL_DEFAULT_DISPLAY);
1912        if (eglDisplay_ == EGL_NO_DISPLAY) {
1913            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "eglGetDisplay: unable to get EGL display");
1914            return false;
1915        }
1916
1917        // 初始化EGL
1918        EGLint majorVersion;
1919        EGLint minorVersion;
1920        if (!eglInitialize(eglDisplay_, &majorVersion, &minorVersion)) {
1921            OH_LOG_Print(
1922                LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "eglInitialize: unable to get initialize EGL display");
1923            return false;
1924        }
1925
1926        // 选择配置
1927        const EGLint maxConfigSize = 1;
1928        EGLint numConfigs;
1929        if (!eglChooseConfig(eglDisplay_, ATTRIB_LIST, &eglConfig_, maxConfigSize, &numConfigs)) {
1930            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "eglChooseConfig: unable to choose configs");
1931            return false;
1932        }
1933        // 创建环境
1934        return CreateEnvironment();
1935        }
1936    ```
1937
1938    ```c++
1939    bool EGLCore::CreateEnvironment() {
1940        // 创建Surface
1941        if (eglWindow_ == nullptr) {
1942            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "eglWindow_ is null");
1943            return false;
1944        }
1945        eglSurface_ = eglCreateWindowSurface(eglDisplay_, eglConfig_, eglWindow_, NULL);
1946        if (eglSurface_ == nullptr) {
1947            OH_LOG_Print(
1948                LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "eglCreateWindowSurface: unable to create surface");
1949            return false;
1950        }
1951
1952        // 创建context
1953        eglContext_ = eglCreateContext(eglDisplay_, eglConfig_, EGL_NO_CONTEXT, CONTEXT_ATTRIBS);
1954        if (!eglMakeCurrent(eglDisplay_, eglSurface_, eglSurface_, eglContext_)) {
1955            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "eglMakeCurrent failed");
1956            return false;
1957        }
1958
1959        // 创建program
1960        program_ = CreateProgram(VERTEX_SHADER, FRAGMENT_SHADER);
1961        if (program_ == PROGRAM_ERROR) {
1962            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "CreateProgram: unable to create program");
1963            return false;
1964        }
1965        return true;
1966    }
1967
1968    GLuint EGLCore::CreateProgram(const char* vertexShader, const char* fragShader) {
1969        if ((vertexShader == nullptr) || (fragShader == nullptr)) {
1970            OH_LOG_Print(
1971                LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "createProgram: vertexShader or fragShader is null");
1972            return PROGRAM_ERROR;
1973        }
1974
1975        GLuint vertex = LoadShader(GL_VERTEX_SHADER, vertexShader);
1976        if (vertex == PROGRAM_ERROR) {
1977            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "createProgram vertex error");
1978            return PROGRAM_ERROR;
1979        }
1980
1981        GLuint fragment = LoadShader(GL_FRAGMENT_SHADER, fragShader);
1982        if (fragment == PROGRAM_ERROR) {
1983            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "createProgram fragment error");
1984            return PROGRAM_ERROR;
1985        }
1986
1987        GLuint program = glCreateProgram();
1988        if (program == PROGRAM_ERROR) {
1989            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "createProgram program error");
1990            glDeleteShader(vertex);
1991            glDeleteShader(fragment);
1992            return PROGRAM_ERROR;
1993        }
1994
1995        // 该gl函数没有返回值。
1996        glAttachShader(program, vertex);
1997        glAttachShader(program, fragment);
1998        glLinkProgram(program);
1999
2000        GLint linked;
2001        glGetProgramiv(program, GL_LINK_STATUS, &linked);
2002        if (linked != 0) {
2003            glDeleteShader(vertex);
2004            glDeleteShader(fragment);
2005            return program;
2006        }
2007
2008        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "createProgram linked error");
2009        GLint infoLen = 0;
2010        glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLen);
2011        if (infoLen > 1) {
2012            char* infoLog = (char*)malloc(sizeof(char) * (infoLen + 1));
2013            memset(infoLog, 0, infoLen + 1);
2014            glGetProgramInfoLog(program, infoLen, nullptr, infoLog);
2015            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "glLinkProgram error = %s", infoLog);
2016            free(infoLog);
2017            infoLog = nullptr;
2018        }
2019        glDeleteShader(vertex);
2020        glDeleteShader(fragment);
2021        glDeleteProgram(program);
2022        return PROGRAM_ERROR;
2023    }
2024
2025    GLuint EGLCore::LoadShader(GLenum type, const char* shaderSrc) {
2026        if ((type == 0) || (shaderSrc == nullptr)) {
2027            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "glCreateShader type or shaderSrc error");
2028            return PROGRAM_ERROR;
2029        }
2030
2031        GLuint shader = glCreateShader(type);
2032        if (shader == 0) {
2033            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "glCreateShader unable to load shader");
2034            return PROGRAM_ERROR;
2035        }
2036
2037        // 该gl函数没有返回值。
2038        glShaderSource(shader, 1, &shaderSrc, nullptr);
2039        glCompileShader(shader);
2040
2041        GLint compiled;
2042        glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
2043        if (compiled != 0) {
2044            return shader;
2045        }
2046
2047        GLint infoLen = 0;
2048        glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
2049        if (infoLen <= 1) {
2050            glDeleteShader(shader);
2051            return PROGRAM_ERROR;
2052        }
2053
2054        char *infoLog = (char*)malloc(sizeof(char) * (infoLen + 1));
2055        if (infoLog != nullptr) {
2056            memset(infoLog, 0, infoLen + 1);
2057            glGetShaderInfoLog(shader, infoLen, nullptr, infoLog);
2058            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "glCompileShader error = %s", infoLog);
2059            free(infoLog);
2060            infoLog = nullptr;
2061        }
2062        glDeleteShader(shader);
2063        return PROGRAM_ERROR;
2064    }
2065
2066    ```
2067
20685. 渲染功能实现。
2069
2070   (1) 绘制背景。
2071
2072    ```c++
2073    // ...
2074    // 绘制背景颜色 #f4f4f4
2075    const GLfloat BACKGROUND_COLOR[] = { 244.0f / 255, 244.0f / 255, 244.0f / 255, 1.0f };
2076
2077    // 绘制图案颜色
2078    const GLfloat DRAW_COLOR[] = {126.0f / 255, 143.0f / 255, 251.0f / 255, 1.0f};
2079
2080    // 绘制图案改变后的颜色
2081    const GLfloat CHANGE_COLOR[] = {146.0f / 255, 214.0f / 255, 204.0f / 255, 1.0f};
2082
2083    // 绘制背景顶点
2084    const GLfloat BACKGROUND_RECTANGLE_VERTICES[] = {
2085        -1.0f, 1.0f,
2086        1.0f, 1.0f,
2087        1.0f, -1.0f,
2088        -1.0f, -1.0f
2089    };
2090    // ...
2091    ```
2092
2093    ```c++
2094    // 绘制背景颜色
2095    void EGLCore::Background() {
2096        GLint position = PrepareDraw();
2097        if (position == POSITION_ERROR) {
2098            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Background get position failed");
2099            return;
2100        }
2101
2102        if (!ExecuteDraw(position, BACKGROUND_COLOR, BACKGROUND_RECTANGLE_VERTICES,
2103                         sizeof(BACKGROUND_RECTANGLE_VERTICES))) {
2104            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Background execute draw failed");
2105            return;
2106        }
2107
2108        if (!FinishDraw()) {
2109            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Background FinishDraw failed");
2110            return;
2111        }
2112    }
2113
2114    // 绘前准备,获取position,创建成功时position值从0开始
2115    GLint EGLCore::PrepareDraw() {
2116        if ((eglDisplay_ == nullptr) || (eglSurface_ == nullptr) || (eglContext_ == nullptr) ||
2117            (!eglMakeCurrent(eglDisplay_, eglSurface_, eglSurface_, eglContext_))) {
2118            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "PrepareDraw: param error");
2119            return POSITION_ERROR;
2120        }
2121
2122        // 该gl函数没有返回值。
2123        glViewport(DEFAULT_X_POSITION, DEFAULT_Y_POSITION, width_, height_);
2124        glClearColor(GL_RED_DEFAULT, GL_GREEN_DEFAULT, GL_BLUE_DEFAULT, GL_ALPHA_DEFAULT);
2125        glClear(GL_COLOR_BUFFER_BIT);
2126        glUseProgram(program_);
2127
2128        return glGetAttribLocation(program_, POSITION_NAME);
2129    }
2130
2131    // 依据传入参数在指定区域绘制指定颜色
2132    bool EGLCore::ExecuteDraw(GLint position, const GLfloat *color, const GLfloat shapeVertices[], unsigned long vertSize) {
2133        if ((position > 0) || (color == nullptr) || (vertSize / sizeof(shapeVertices[0])) != SHAPE_VERTICES_SIZE) {
2134            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "ExecuteDraw: param error");
2135            return false;
2136        }
2137
2138        // 该gl函数没有返回值。
2139        glVertexAttribPointer(position, POINTER_SIZE, GL_FLOAT, GL_FALSE, 0, shapeVertices);
2140        glEnableVertexAttribArray(position);
2141        glVertexAttrib4fv(1, color);
2142        glDrawArrays(GL_TRIANGLE_FAN, 0, TRIANGLE_FAN_SIZE);
2143        glDisableVertexAttribArray(position);
2144
2145        return true;
2146    }
2147
2148    // 结束绘制操作
2149    bool EGLCore::FinishDraw() {
2150        // 强制刷新缓冲
2151        glFlush();
2152        glFinish();
2153        return eglSwapBuffers(eglDisplay_, eglSurface_);
2154    }
2155    ```
2156
2157   (2) 绘制图形。
2158
2159    ```c++
2160    void EGLCore::Draw(int& hasDraw) {
2161        flag_ = false;
2162        OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "EGLCore", "Draw");
2163        GLint position = PrepareDraw();
2164        if (position == POSITION_ERROR) {
2165            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw get position failed");
2166            return;
2167        }
2168
2169        // 绘制背景
2170        if (!ExecuteDraw(position, BACKGROUND_COLOR,
2171                        BACKGROUND_RECTANGLE_VERTICES, sizeof(BACKGROUND_RECTANGLE_VERTICES))) {
2172            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw execute draw background failed");
2173            return;
2174        }
2175
2176        // 将五角星分为五个四边形,计算其中一个四边形的四个顶点
2177        GLfloat rotateX = 0;
2178        GLfloat rotateY = FIFTY_PERCENT * height_;
2179        GLfloat centerX = 0;
2180        GLfloat centerY = -rotateY * (M_PI / 180 * 54) * (M_PI / 180 * 18);
2181        GLfloat leftX = -rotateY * (M_PI / 180 * 18);
2182        GLfloat leftY = 0;
2183        GLfloat rightX = rotateY * (M_PI / 180 * 18);
2184        GLfloat rightY = 0;
2185
2186        // 确定绘制四边形的顶点,使用绘制区域的百分比表示
2187        const GLfloat shapeVertices[] = {
2188            centerX / width_, centerY / height_,
2189            leftX / width_, leftY / height_,
2190            rotateX / width_, rotateY / height_,
2191            rightX / width_, rightY / height_
2192        };
2193
2194        if (!ExecuteDrawStar(position, DRAW_COLOR, shapeVertices, sizeof(shapeVertices))) {
2195            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw execute draw star failed");
2196            return;
2197        }
2198
2199        GLfloat rad = M_PI / 180 * 72;
2200        for (int i = 0; i < NUM_4; ++i) {
2201            // 旋转得其他四个四边形的顶点
2202            Rotate2d(centerX, centerY, &rotateX, &rotateY, rad);
2203            Rotate2d(centerX, centerY, &leftX, &leftY, rad);
2204            Rotate2d(centerX, centerY, &rightX, &rightY, rad);
2205
2206            // 确定绘制四边形的顶点,使用绘制区域的百分比表示
2207            const GLfloat shapeVertices[] = {
2208                centerX / width_, centerY / height_,
2209                leftX / width_, leftY / height_,
2210                rotateX / width_, rotateY / height_,
2211                rightX / width_, rightY / height_
2212            };
2213
2214            // 绘制图形
2215            if (!ExecuteDrawStar(position, DRAW_COLOR, shapeVertices, sizeof(shapeVertices))) {
2216                OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw execute draw shape failed");
2217                return;
2218            }
2219        }
2220
2221        // 结束绘制
2222        if (!FinishDraw()) {
2223            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw FinishDraw failed");
2224            return;
2225        }
2226        hasDraw = 1;
2227
2228        flag_ = true;
2229    }
2230    ```
2231
2232   (3) 改变颜色,重新画一个大小相同颜色不同的图形,与原图形替换,达到改变颜色的效果。
2233
2234    ```c++
2235    void EGLCore::ChangeColor(int& hasChangeColor) {
2236        if (!flag_) {
2237            return;
2238        }
2239        OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "EGLCore", "ChangeColor");
2240        GLint position = PrepareDraw();
2241        if (position == POSITION_ERROR) {
2242            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "ChangeColor get position failed");
2243            return;
2244        }
2245
2246        // 绘制背景
2247        if (!ExecuteDraw(position, BACKGROUND_COLOR,
2248                        BACKGROUND_RECTANGLE_VERTICES, sizeof(BACKGROUND_RECTANGLE_VERTICES))) {
2249            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "ChangeColor execute draw background failed");
2250            return;
2251        }
2252
2253        // 确定绘制四边形的顶点,使用绘制区域的百分比表示
2254        GLfloat rotateX = 0;
2255        GLfloat rotateY = FIFTY_PERCENT * height_;
2256        GLfloat centerX = 0;
2257        GLfloat centerY = -rotateY * (M_PI / 180 * 54) * (M_PI / 180 * 18);
2258        GLfloat leftX = -rotateY * (M_PI / 180 * 18);
2259        GLfloat leftY = 0;
2260        GLfloat rightX = rotateY * (M_PI / 180 * 18);
2261        GLfloat rightY = 0;
2262
2263        // 确定绘制四边形的顶点,使用绘制区域的百分比表示
2264        const GLfloat shapeVertices[] = {
2265            centerX / width_, centerY / height_,
2266            leftX / width_, leftY / height_,
2267            rotateX / width_, rotateY / height_,
2268            rightX / width_, rightY / height_
2269        };
2270
2271        // 使用新的颜色绘制
2272        if (!ExecuteDrawNewStar(position, CHANGE_COLOR, shapeVertices, sizeof(shapeVertices))) {
2273            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw execute draw star failed");
2274            return;
2275        }
2276
2277        GLfloat rad = M_PI / 180 * 72;
2278        for (int i = 0; i < NUM_4; ++i) {
2279            // 旋转得其他四个四边形的顶点
2280            Rotate2d(centerX, centerY, &rotateX, &rotateY, rad);
2281            Rotate2d(centerX, centerY, &leftX, &leftY, rad);
2282            Rotate2d(centerX, centerY, &rightX, &rightY, rad);
2283
2284            // 确定绘制四边形的顶点,使用绘制区域的百分比表示
2285            const GLfloat shapeVertices[] = {
2286                centerX / width_, centerY / height_,
2287                leftX / width_, leftY / height_,
2288                rotateX / width_, rotateY / height_,
2289                rightX / width_, rightY / height_
2290            };
2291
2292            // 使用新的颜色绘制
2293            if (!ExecuteDrawNewStar(position, CHANGE_COLOR, shapeVertices, sizeof(shapeVertices))) {
2294                OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw execute draw shape failed");
2295                return;
2296            }
2297        }
2298
2299        // 结束绘制
2300        if (!FinishDraw()) {
2301            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "ChangeColor FinishDraw failed");
2302        }
2303        hasChangeColor = 1;
2304    }
2305
2306   bool EGLCore::ExecuteDrawNewStar(
2307       GLint position, const GLfloat* color, const GLfloat shapeVertices[], unsigned long vertSize) {
2308       if ((position > 0) || (color == nullptr) || (vertSize / sizeof(shapeVertices[0])) != SHAPE_VERTICES_SIZE) {
2309            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "ExecuteDraw: param error");
2310            return false;
2311        }
2312
2313        // 该gl函数没有返回值。
2314        glVertexAttribPointer(position, POINTER_SIZE, GL_FLOAT, GL_FALSE, 0, shapeVertices);
2315        glEnableVertexAttribArray(position);
2316        glVertexAttrib4fv(1, color);
2317        glDrawArrays(GL_TRIANGLE_FAN, 0, TRIANGLE_FAN_SIZE);
2318        glDisableVertexAttribArray(position);
2319
2320       return true;
2321   }
2322    ```
2323
23246. 释放相关资源。
2325
2326    (1) EGLCore类下创建Release()方法,释放初始化环境时申请的资源,包含窗口display、渲染区域surface、环境上下文context等。
2327
2328    ```c++
2329    void EGLCore::Release() {
2330        // 释放Surface
2331        if ((eglDisplay_ == nullptr) || (eglSurface_ == nullptr) || (!eglDestroySurface(eglDisplay_, eglSurface_))) {
2332            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Release eglDestroySurface failed");
2333        }
2334        // 释放context
2335        if ((eglDisplay_ == nullptr) || (eglContext_ == nullptr) || (!eglDestroyContext(eglDisplay_, eglContext_))) {
2336            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Release eglDestroyContext failed");
2337        }
2338        // 释放display
2339        if ((eglDisplay_ == nullptr) || (!eglTerminate(eglDisplay_))) {
2340            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Release eglTerminate failed");
2341        }
2342    }
2343    ```
2344
23457. CMakeLists,使用CMake工具链将C++源代码编译成动态链接库文件。
2346
2347    ```CMake
2348    # 设置CMake最小版本
2349    cmake_minimum_required(VERSION 3.4.1)
2350    # 项目名称
2351    project(XComponent)
2352
2353    set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
2354    add_definitions(-DOHOS_PLATFORM)
2355    # 设置头文件搜索目录
2356    include_directories(
2357        ${NATIVERENDER_ROOT_PATH}
2358        ${NATIVERENDER_ROOT_PATH}/include
2359    )
2360    # 添加名为nativerender的动态库,库文件名为libnativerender.so,添加cpp文件
2361    add_library(nativerender SHARED
2362        render/egl_core.cpp
2363        render/plugin_render.cpp
2364        manager/plugin_manager.cpp
2365        napi_init.cpp
2366    )
2367
2368    find_library(
2369        EGL-lib
2370        EGL
2371    )
2372
2373    find_library(
2374        GLES-lib
2375        GLESv3
2376    )
2377
2378    find_library(
2379        hilog-lib
2380        hilog_ndk.z
2381    )
2382
2383    find_library(
2384        libace-lib
2385        ace_ndk.z
2386    )
2387
2388    find_library(
2389        libnapi-lib
2390        ace_napi.z
2391    )
2392
2393    find_library(
2394        libuv-lib
2395        uv
2396    )
2397    # 添加构建需要链接的库
2398    target_link_libraries(nativerender PUBLIC
2399        ${EGL-lib} ${GLES-lib} ${hilog-lib} ${libace-lib} ${libnapi-lib} ${libuv-lib})
2400    ```
2401
2402## 相关实例
2403
2404针对Native XComponent的使用,有以下相关实例可供参考:
2405
2406- [XComponent3D(API10)](https://gitcode.com/openharmony/applications_app_samples/tree/master/code/BasicFeature/Native/XComponent3D)
2407- [XComponent(API10)](https://gitcode.com/openharmony/applications_app_samples/tree/master/code/BasicFeature/Native/XComponent)
2408- [Native XComponent(API12)](https://gitcode.com/openharmony/applications_app_samples/tree/master/code/BasicFeature/Native/NdkXComponent)
2409- [OpenGL三棱椎(API10)](https://gitcode.com/openharmony/applications_app_samples/tree/master/code/BasicFeature/Native/NdkOpenGL)
2410- [NativeXComponent(api19)](https://gitcode.com/openharmony/applications_app_samples/tree/master/code/BasicFeature/Native/NativeXComponent)
2411
2412针对ArkTS XComponent的使用,有以下相关实例可供参考:
2413
2414- [ArkTSXComponent(API12)](https://gitcode.com/openharmony/applications_app_samples/tree/master/code/BasicFeature/Native/ArkTSXComponent)
2415
2416<!--RP1--><!--RP1End-->
2417