• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 自定义渲染 (XComponent)
2
3## 概述
4
5XComponent组件作为一种渲染组件,可用于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组件的内容合成后展示到屏幕上。
6
7目前XComponent组件主要有两个应用场景。一个是Native XComponent场景,在native层获取Native XComponent实例,在native侧注册XComponent的生命周期回调,以及触摸、鼠标、按键等事件回调。另一个是ArkTS XComponent场景,在ArkTS侧获取SurfaceId,生命周期回调、触摸、鼠标、按键等事件回调等均在ArkTS侧触发。
8
9## Native XComponent场景
10在XComponent组件构造函数的libraryname中定义需要加载的动态库,而后应用就可以在Native层获取Native XComponent实例,其是XComponent组件提供在Native层的实例,可作为ArkTS层和Native层XComponent绑定的桥梁。XComponent所提供的NDK接口都依赖于该实例。接口能力包括获取NativeWindow实例、获取XComponent的布局/事件信息、注册XComponent的生命周期回调、注册XComponent的触摸、鼠标、按键等事件回调。针对Native XComponent,主要的开发场景如下:
11
12- 利用Native XComponent提供的接口注册XComponent的生命周期和事件回调。
13- 在这些回调中进行初始化环境、获取当前状态、响应各类事件的开发。
14- 利用NativeWindow和EGL接口开发自定义绘制内容以及申请和提交Buffer到图形队列。
15
16**接口说明**
17
18| 接口名                                                       | 描述                                                         |
19| ------------------------------------------------------------ | ------------------------------------------------------------ |
20| OH_NativeXComponent_GetXComponentId(OH_NativeXComponent* component, char* id, uint64_t* size) | 获取XComponent的id。                                         |
21| OH_NativeXComponent_GetXComponentSize(OH_NativeXComponent* component, const void* window, uint64_t* width, uint64_t* height) | 获取XComponent持有的surface的大小。                          |
22| OH_NativeXComponent_GetXComponentOffset(OH_NativeXComponent* component, const void* window, double* x, double* y) | 获取XComponent持有的surface相对其父组件左顶点的偏移量。      |
23| OH_NativeXComponent_GetTouchEvent(OH_NativeXComponent* component, const void* window, OH_NativeXComponent_TouchEvent* touchEvent) | 获取由XComponent触发的触摸事件。touchEvent内的具体属性值可参考[OH_NativeXComponent_TouchEvent](../reference/apis-arkui/_o_h___native_x_component___touch_event.md)。 |
24| OH_NativeXComponent_GetTouchPointToolType(OH_NativeXComponent* component, uint32_t pointIndex, OH_NativeXComponent_TouchPointToolType* toolType) | 获取XComponent触摸点的工具类型。                             |
25| OH_NativeXComponent_GetTouchPointTiltX(OH_NativeXComponent* component, uint32_t pointIndex, float* tiltX) | 获取XComponent触摸点处相对X轴的倾斜角度。                    |
26| OH_NativeXComponent_GetTouchPointTiltY(OH_NativeXComponent* component, uint32_t pointIndex, float* tiltY) | 获取XComponent触摸点处相对Y轴的倾斜角度。                    |
27| OH_NativeXComponent_GetMouseEvent(OH_NativeXComponent* component, const void* window, OH_NativeXComponent_MouseEvent* mouseEvent) | 获取由XComponent触发的鼠标事件。                             |
28| OH_NativeXComponent_RegisterCallback(OH_NativeXComponent* component, OH_NativeXComponent_Callback* callback) | 为此OH_NativeXComponent实例注册生命周期和触摸事件回调。      |
29| OH_NativeXComponent_RegisterMouseEventCallback(OH_NativeXComponent* component, OH_NativeXComponent_MouseEvent_Callback* callback) | 为此OH_NativeXComponent实例注册鼠标事件回调。                |
30| OH_NativeXComponent_RegisterFocusEventCallback(OH_NativeXComponent* component, void (\*callback)(OH_NativeXComponent* component, void* window)) | 为此OH_NativeXComponent实例注册获得焦点事件回调。            |
31| OH_NativeXComponent_RegisterKeyEventCallback(OH_NativeXComponent* component, void (\*callback)(OH_NativeXComponent* component, void* window)) | 为此OH_NativeXComponent实例注册按键事件回调。                |
32| OH_NativeXComponent_RegisterBlurEventCallback(OH_NativeXComponent* component, void (\*callback)(OH_NativeXComponent* component, void* window)) | 为此OH_NativeXComponent实例注册失去焦点事件回调。            |
33| OH_NativeXComponent_GetKeyEvent(OH_NativeXComponent* component, OH_NativeXComponent_KeyEvent\** keyEvent) | 获取由XComponent触发的按键事件。                             |
34| OH_NativeXComponent_GetKeyEventAction(OH_NativeXComponent_KeyEvent* keyEvent, OH_NativeXComponent_KeyAction* action) | 获取按键事件的动作。                                         |
35| OH_NativeXComponent_GetKeyEventCode(OH_NativeXComponent_KeyEvent* keyEvent, OH_NativeXComponent_KeyCode* code) | 获取按键事件的键码值。                                       |
36| OH_NativeXComponent_GetKeyEventSourceType(OH_NativeXComponent_KeyEvent* keyEvent, OH_NativeXComponent_EventSourceType* sourceType) | 获取按键事件的输入源类型。                                   |
37| OH_NativeXComponent_GetKeyEventDeviceId(OH_NativeXComponent_KeyEvent* keyEvent, int64_t* deviceId) | 获取按键事件的设备ID。                                       |
38| OH_NativeXComponent_GetKeyEventTimestamp(OH_NativeXComponent_KeyEvent* keyEvent, int64_t* timestamp) | 获取按键事件的时间戳。                                       |
39
40> **说明 :**
41>
42> 上述接口不支持跨线程访问。
43>
44> XComponent销毁(onSurfaceDestroyed回调触发后)时会释放上述接口中获取的OH_NativeXComponent和window对象。如果再次使用获取的对象,有可能会导致使用野指针或空指针的崩溃问题。
45
46**开发步骤**
47
48以下步骤以SURFACE类型为例,描述了如何使用`XComponent组件`调用`Node-API`接口来创建`EGL/GLES`环境,实现在主页面绘制图形,并可以改变图形的颜色。
49
501. 在界面中定义XComponent。
51
52    ```typescript
53    //在ets/interface/XComponentContext.ts中声明native侧接口
54    export default interface XComponentContext {
55      drawPattern(): void;
56
57      getStatus(): XComponentContextStatus;
58    };
59
60    type XComponentContextStatus = {
61      hasDraw: boolean,
62      hasChangeColor: boolean,
63    };
64    ```
65
66    ```typescript
67    import XComponentContext from "../interface/XComponentContext"
68
69    @Entry
70    @Component
71    struct Index {
72        xComponentContext: XComponentContext | undefined = undefined;
73        xComponentAttrs: XComponentAttrs = {
74            id: 'xcomponentId',
75            type: XComponentType.SURFACE,
76            libraryname: 'nativerender'
77        }
78
79        build() {
80        Row() {
81            // ...
82            // 在xxx.ets 中定义 XComponent
83            XComponent(this.xComponentAttrs)
84                .focusable(true) // 可响应键盘事件
85                .onLoad((xComponentContext) => {
86                    console.log("onLoad");
87                    this.xComponentContext = xComponentContext as XComponentContext;
88
89                    // 调用drawPattern绘制内容
90                    if (this.xComponentContext) {
91                        this.xComponentContext.drawPattern();
92                        if (this.xComponentContext.getStatus()) {
93                            this.xComponentContext.getStatus().hasDraw;
94                        }
95                    }
96                })
97                .onDestroy(() => {
98                    console.log("onDestroy");
99                })
100            // ...
101            }
102            .onClick(() => {
103                // 调用getStatus改变绘制内容
104                if (this.xComponentContext && this.xComponentContext.getStatus()) {
105                    this.xComponentContext.getStatus().hasChangeColor;
106                }
107            })
108            .height('100%')
109        }
110    }
111
112    interface XComponentAttrs {
113        id: string;
114        type: number;
115        libraryname: string;
116    }
117    ```
118
1192. Node-API模块注册,具体使用请参考[Native API在应用工程中的使用指导](../napi/napi-guidelines.md)。
120
121    ```c++
122    #include <hilog/log.h>
123    #include "common/common.h"
124    #include "manager/plugin_manager.h"
125
126    // 在napi_init.cpp文件中,Init方法注册接口函数,从而将封装的C++方法传递出来,供ArkTS侧调用
127    EXTERN_C_START
128    static napi_value Init(napi_env env, napi_value exports) {
129        // ...
130        // 向ArkTS侧暴露接口getContext()
131        napi_property_descriptor desc[] = {
132            { "getContext", nullptr, PluginManager::GetContext, nullptr, nullptr, nullptr, napi_default, nullptr }
133        };
134        if (napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc) != napi_ok) {
135            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Init", "napi_define_properties failed");
136            return nullptr;
137        }
138        // 方法内检查环境变量是否包含XComponent组件实例,若实例存在则导出绘制相关接口
139        PluginManager::GetInstance()->Export(env, exports);
140        return exports;
141    }
142    EXTERN_C_END
143
144    // 编写接口的描述信息,根据实际需要可以修改对应参数
145    static napi_module nativerenderModule = {
146        .nm_version = 1,
147        .nm_flags = 0,
148        .nm_filename = nullptr,
149        // 入口函数
150        .nm_register_func = Init,// 指定加载对应模块时的回调函数
151        // 模块名称
152        .nm_modname =
153            "nativerender", // 指定模块名称,对于XComponent相关开发,这个名称必须和ArkTS侧XComponent中libraryname的值保持一致
154        .nm_priv = ((void *)0),
155        .reserved = {0}};
156
157    // __attribute__((constructor))修饰的方法由系统自动调用,使用Node-API接口napi_module_register()传入模块描述信息进行模块注册
158    extern "C" __attribute__((constructor)) void RegisterModule(void) { napi_module_register(&nativerenderModule); }
159    ```
160
161    ```c++
162    // 检查环境变量是否包含XComponent组件实例,若实例存在则导出绘制相关接口
163    void PluginManager::Export(napi_env env, napi_value exports)
164    {
165        if ((env == nullptr) || (exports == nullptr)) {
166            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "Export: env or exports is null");
167            return;
168        }
169
170        napi_value exportInstance = nullptr;
171        if (napi_get_named_property(env, exports, OH_NATIVE_XCOMPONENT_OBJ, &exportInstance) != napi_ok) {
172            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "Export: napi_get_named_property fail");
173        }
174
175        // 获取nativeXComponent
176        OH_NativeXComponent* nativeXComponent = nullptr;
177        if (napi_unwrap(env, exportInstance, reinterpret_cast<void**>(&nativeXComponent)) != napi_ok) {
178            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "Export: napi_unwrap fail");
179            return;
180        }
181
182        // 获取XComponent的id,即ArkTS侧XComponent组件构造中的id参数
183        char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {'\0'};
184        uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
185        if (OH_NativeXComponent_GetXComponentId(nativeXComponent, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
186            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager",
187                         "Export: OH_NativeXComponent_GetXComponentId fail");
188            return;
189        }
190
191        std::string id(idStr);
192        auto context = PluginManager::GetInstance();
193        if ((context != nullptr) && (nativeXComponent != nullptr)) {
194            context->SetNativeXComponent(id, nativeXComponent);
195            auto render = context->GetRender(id);
196            if (render != nullptr) {
197                // 注册回调函数
198                render->RegisterCallback(nativeXComponent);
199                // 方法内使用Node-API,导出绘制相关接口,向ArkTS侧暴露绘制相关方法
200                render->Export(env, exports);
201            }
202        }
203    }
204    ```
205    ```c++
206    // 使用Node-API中的napi_define_properties方法,向ArkTS侧暴露drawPattern()和getStatus()方法,在ArkTS侧调用drawPattern()来绘制内容,
207    // 调用getStatus()来改变绘制内容
208    void PluginRender::Export(napi_env env, napi_value exports)
209    {
210        // ...
211        // 将接口函数注册为ArkTS侧接口drawPattern和getStatus
212        napi_property_descriptor desc[] = {
213            {"drawPattern", nullptr, PluginRender::NapiDrawPattern, nullptr, nullptr, nullptr, napi_default, nullptr},
214            {"getStatus", nullptr, PluginRender::TestGetXComponentStatus, nullptr, nullptr, nullptr, napi_default,
215             nullptr}};
216        if (napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc) != napi_ok) {
217            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginRender", "Export: napi_define_properties failed");
218        }
219    }
220    ```
221
2223. 注册XComponent事件回调,使用Node-API实现XComponent事件回调函数。
223
224    (1) 定义surface创建成功,发生改变,销毁和XComponent的touch事件回调接口。
225
226    ```c++
227    // 在头文件中定义PluginRender类
228    class PluginRender {
229    public:
230        explicit PluginRender(std::string& id);
231        ~PluginRender() {
232            if (eglCore_ != nullptr) {
233                eglCore_->Release();
234                delete eglCore_;
235                eglCore_ = nullptr;
236           }
237       }
238       static PluginRender* GetInstance(std::string& id);
239       static void Release(std::string& id);
240       static napi_value NapiDrawPattern(napi_env env, napi_callback_info info);
241       static napi_value TestGetXComponentStatus(napi_value env, napi_callback_info info);
242       void Export(napi_env env, napi_value exports);
243       void OnSurfaceChanged(OH_NativeXComponent* component, void* window);
244       void OnTouchEvent(OH_NativeXComponent* component, void* window);
245       void OnMouseEvent(OH_NativeXComponent* component, void* window);
246       void OnHoverEvent(OH_NativeXComponent* component, bool isHover);
247       void OnFocusEvent(OH_NativeXComponent* component, void* window);
248       void OnBlurEvent(OH_NativeXComponent* component, void* window);
249       void OnKeyEvent(OH_NativeXComponent* component, void* window);
250       void RegisterCallback(OH_NativeXComponent* NativeXComponent);
251
252    public:
253        static std::unordered_map<std::string, PluginRender*> instance_;
254        EGLCore* eglCore_;
255        std::string id_;
256        static int32_t hasDraw_;
257        static int32_t hasChangeColor_;
258
259    private:
260        OH_NativeXComponent_Callback renderCallback_;
261        OH_NativeXComponent_MouseEvent_Callback mouseCallback_;
262    };
263    ```
264
265    ```c++
266    // 在源文件中实现PluginRender类中方法
267    std::unordered_map<std::string, PluginRender *> PluginRender::instance_;
268    int32_t PluginRender::hasDraw_ = 0;
269    int32_t PluginRender::hasChangeColor_ = 0;
270
271    PluginRender::PluginRender(std::string& id) {
272        this->id_ = id;
273        this->eglCore_ = new EGLCore();
274   }
275
276    PluginRender* PluginRender::GetInstance(std::string& id) {
277        if (instance_.find(id) == instance_.end()) {
278            PluginRender* instance = new PluginRender(id);
279            instance_[id] = instance;
280            return instance;
281        } else {
282            return instance_[id];
283        }
284    }
285
286    // 定义一个函数OnSurfaceCreatedCB(),封装初始化环境与绘制背景
287    void OnSurfaceCreatedCB(OH_NativeXComponent *component, void *window) {
288   	    // ...
289   	    // 获取XComponent的id,即ArkTS侧XComponent组件构造中的id参数
290   	    char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {'\0'};
291   	    uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
292   	    if (OH_NativeXComponent_GetXComponentId(component, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
293   		    OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Callback",
294   			             "OnSurfaceCreatedCB: Unable to get XComponent id");
295   		    return;
296   	    }
297
298   	    // 初始化环境与绘制背景
299   	    std::string id(idStr);
300   	    auto render = PluginRender::GetInstance(id);
301   	    uint64_t width;
302   	    uint64_t height;
303   	    // 获取XComponent拥有的surface的大小
304   	    int32_t xSize = OH_NativeXComponent_GetXComponentSize(component, window, &width, &height);
305   	    if ((xSize == OH_NATIVEXCOMPONENT_RESULT_SUCCESS) && (render != nullptr)) {
306   		    if (render->eglCore_->EglContextInit(window, width, height)) {
307   			    render->eglCore_->Background();
308   		    }
309   	    }
310    }
311
312    // 定义一个函数OnSurfaceChangedCB()
313    void OnSurfaceChangedCB(OH_NativeXComponent *component, void *window) {
314   	    // ...
315   	    // 获取XComponent的id
316   	    char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {'\0'};
317   	    uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
318   	    if (OH_NativeXComponent_GetXComponentId(component, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
319   		    OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Callback",
320   			             "OnSurfaceChangedCB: Unable to get XComponent id");
321   		    return;
322   	    }
323
324   	    std::string id(idStr);
325   	    auto render = PluginRender::GetInstance(id);
326   	    if (render != nullptr) {
327   		    // 封装OnSurfaceChanged方法
328   		    render->OnSurfaceChanged(component, window);
329   	    }
330    }
331
332    // 定义一个函数OnSurfaceDestroyedCB(),将PluginRender类内释放资源的方法Release()封装在其中
333    void OnSurfaceDestroyedCB(OH_NativeXComponent *component, void *window) {
334   	    // ...
335   	    // 获取XComponent的id
336   	    char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {'\0'};
337   	    uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
338   	    if (OH_NativeXComponent_GetXComponentId(component, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
339   		    OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Callback",
340   			             "OnSurfaceDestroyedCB: Unable to get XComponent id");
341   		    return;
342   	    }
343
344   	    std::string id(idStr);
345   	    // 释放资源
346   	    PluginRender::Release(id);
347    }
348
349    // 定义一个函数DispatchTouchEventCB(),响应触摸事件时触发该回调
350    void DispatchTouchEventCB(OH_NativeXComponent *component, void *window) {
351   	    // ...
352   	    // 获取XComponent的id
353   	    char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = { '\0' };
354   	    uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
355   	    if (OH_NativeXComponent_GetXComponentId(component, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
356   		    OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Callback",
357   			             "DispatchTouchEventCB: Unable to get XComponent id");
358   		    return;
359   	    }
360
361   	    std::string id(idStr);
362   	    PluginRender *render = PluginRender::GetInstance(id);
363   	    if (render != nullptr) {
364   		    // 封装OnTouchEvent方法
365   		    render->OnTouchEvent(component, window);
366   	    }
367    }
368
369    // 定义一个函数DispatchMouseEventCB(),响应鼠标事件时触发该回调
370    void DispatchMouseEventCB(OH_NativeXComponent *component, void *window) {
371   	    // ...
372   	    int32_t ret;
373   	    char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {};
374   	    uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
375   	    ret = OH_NativeXComponent_GetXComponentId(component, idStr, &idSize);
376   	    if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
377   		    return;
378   	    }
379
380   	    std::string id(idStr);
381   	    auto render = PluginRender::GetInstance(id);
382   	    if (render) {
383   		    // 封装OnMouseEvent方法
384   		    render->OnMouseEvent(component, window);
385   	    }
386    }
387
388    // 定义一个函数DispatchHoverEventCB(),响应鼠标悬停事件时触发该回调
389    void DispatchHoverEventCB(OH_NativeXComponent *component, bool isHover) {
390   	    // ...
391   	    int32_t ret;
392   	    char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {};
393   	    uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
394   	    ret = OH_NativeXComponent_GetXComponentId(component, idStr, &idSize);
395   	    if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
396   		    return;
397   	    }
398
399   	    std::string id(idStr);
400   	    auto render = PluginRender::GetInstance(id);
401   	    if (render) {
402   		    // 封装OnHoverEvent方法
403   		    render->OnHoverEvent(component, isHover);
404   	    }
405    }
406
407    // 定义一个函数OnFocusEventCB(),响应获焦事件时触发该回调
408    void OnFocusEventCB(OH_NativeXComponent *component, void *window) {
409        // ...
410   	    int32_t ret;
411   	    char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {};
412   	    uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
413   	    ret = OH_NativeXComponent_GetXComponentId(component, idStr, &idSize);
414   	    if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
415   		    return;
416   	    }
417
418   	    std::string id(idStr);
419   	    auto render = PluginRender::GetInstance(id);
420   	    if (render) {
421   		    // 封装OnFocusEvent方法
422   		    render->OnFocusEvent(component, window);
423   	    }
424    }
425
426    // 定义一个函数OnBlurEventCB(),响应失去焦点事件时触发该回调
427    void OnBlurEventCB(OH_NativeXComponent *component, void *window) {
428   	    // ...
429   	    int32_t ret;
430   	    char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {};
431   	    uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
432   	    ret = OH_NativeXComponent_GetXComponentId(component, idStr, &idSize);
433   	    if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
434   	    	return;
435   	    }
436
437   	    std::string id(idStr);
438   	    auto render = PluginRender::GetInstance(id);
439   	    if (render) {
440   		    // 封装OnBlurEvent方法
441   		    render->OnBlurEvent(component, window);
442   	    }
443    }
444
445    // 定义一个函数OnKeyEventCB(),响应按键事件时触发该回调
446    void OnKeyEventCB(OH_NativeXComponent *component, void *window) {
447   	    // ...
448   	    int32_t ret;
449   	    char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {};
450   	    uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
451   	    ret = OH_NativeXComponent_GetXComponentId(component, idStr, &idSize);
452   	    if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
453   		    return;
454   	    }
455
456   	    std::string id(idStr);
457   	    auto render = PluginRender::GetInstance(id);
458   	    if (render) {
459   		    // 封装OnKeyEvent方法
460   		    render->OnKeyEvent(component, window);
461   	    }
462    }
463
464    // 定义一个OnSurfaceChanged()方法
465    void PluginRender::OnSurfaceChanged(OH_NativeXComponent* component, void* window) {
466        char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = { '\0' };
467   	    uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
468   	    if (OH_NativeXComponent_GetXComponentId(component, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
469   		    OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Callback", "OnSurfaceChanged: Unable to get XComponent id");
470   		    return;
471   	    }
472
473        std::string id(idStr);
474        PluginRender* render = PluginRender::GetInstance(id);
475        double offsetX;
476        double offsetY;
477        // 获取XComponent持有的surface相对其父组件左顶点的偏移量
478        OH_NativeXComponent_GetXComponentOffset(component, window, &offsetX, &offsetY);
479        OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "OH_NativeXComponent_GetXComponentOffset",
480                     "offsetX = %{public}lf, offsetY = %{public}lf", offsetX, offsetY);
481        uint64_t width;
482        uint64_t height;
483        OH_NativeXComponent_GetXComponentSize(component, window, &width, &height);
484        if (render != nullptr) {
485            render->eglCore_->UpdateSize(width, height);
486        }
487    }
488
489    // 定义一个OnTouchEvent()方法
490    void PluginRender::OnTouchEvent(OH_NativeXComponent* component, void* window) {
491        char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = { '\0' };
492   	    uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
493   	    if (OH_NativeXComponent_GetXComponentId(component, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
494   		    OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Callback",
495   			             "OnTouchEvent: Unable to get XComponent id");
496   		    return;
497   	    }
498
499        OH_NativeXComponent_TouchEvent touchEvent;
500        // 获取由XComponent触发的触摸事件
501        OH_NativeXComponent_GetTouchEvent(component, window, &touchEvent);
502        // 获取XComponent触摸点相对于XComponent组件左边缘的坐标x和相对于XComponent组件上边缘的坐标y
503        OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "OnTouchEvent", "touch info: x = %{public}lf, y = %{public}lf",
504                     touchEvent.x, touchEvent.y);
505        // 获取XComponent触摸点相对于XComponent所在应用窗口左上角的x坐标和相对于XComponent所在应用窗口左上角的y坐标
506        OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "OnTouchEvent",
507                     "touch info: screenX = %{public}lf, screenY = %{public}lf", touchEvent.screenX, touchEvent.screenY);
508        std::string id(idStr);
509        PluginRender* render = PluginRender::GetInstance(id);
510        if (render != nullptr && touchEvent.type == OH_NativeXComponent_TouchEventType::OH_NATIVEXCOMPONENT_UP) {
511            render->eglCore_->ChangeColor();
512            hasChangeColor_ = 1;
513        }
514        float tiltX = 0.0f;
515        float tiltY = 0.0f;
516        OH_NativeXComponent_TouchPointToolType toolType =
517            OH_NativeXComponent_TouchPointToolType::OH_NATIVEXCOMPONENT_TOOL_TYPE_UNKNOWN;
518        // 获取XComponent触摸点的工具类型
519        OH_NativeXComponent_GetTouchPointToolType(component, 0, &toolType);
520        // 获取XComponent触摸点处相对X轴的倾斜角度
521        OH_NativeXComponent_GetTouchPointTiltX(component, 0, &tiltX);
522        // 获取XComponent触摸点处相对Y轴的倾斜角度
523        OH_NativeXComponent_GetTouchPointTiltY(component, 0, &tiltY);
524        OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "OnTouchEvent",
525                     "touch info: toolType = %{public}d, tiltX = %{public}lf, tiltY = %{public}lf", toolType, tiltX, tiltY);
526    }
527
528    // 定义一个OnMouseEvent()方法
529    void PluginRender::OnMouseEvent(OH_NativeXComponent *component, void *window) {
530        OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "PluginRender", "OnMouseEvent");
531        OH_NativeXComponent_MouseEvent mouseEvent;
532        // 获取由XComponent触发的鼠标事件
533        int32_t ret = OH_NativeXComponent_GetMouseEvent(component, window, &mouseEvent);
534        if (ret == OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
535   	        OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "PluginRender",
536                         "MouseEvent Info: x = %{public}f, y = %{public}f, action = %{public}d, button = %{public}d",
537                         mouseEvent.x, mouseEvent.y, mouseEvent.action, mouseEvent.button);
538        } else {
539   	        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginRender", "GetMouseEvent error");
540        }
541    }
542
543    // 定义一个OnHoverEvent()方法
544    void PluginRender::OnHoverEvent(OH_NativeXComponent* component, bool isHover) {
545        OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "PluginRender", "OnHoverEvent isHover_ = %{public}d", isHover);
546    }
547
548    // 定义一个OnFocusEvent()方法
549    void PluginRender::OnFocusEvent(OH_NativeXComponent* component, void* window) {
550        OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "PluginRender", "OnFocusEvent");
551    }
552
553    // 定义一个OnBlurEvent()方法
554    void PluginRender::OnBlurEvent(OH_NativeXComponent* component, void* window) {
555        OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "PluginRender", "OnBlurEvent");
556    }
557
558    // 定义一个OnKeyEvent()方法
559    void PluginRender::OnKeyEvent(OH_NativeXComponent *component, void *window) {
560        OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "PluginRender", "OnKeyEvent");
561
562        OH_NativeXComponent_KeyEvent *keyEvent = nullptr;
563        // 获取由XComponent触发的按键事件
564        if (OH_NativeXComponent_GetKeyEvent(component, &keyEvent) >= 0) {
565   	        OH_NativeXComponent_KeyAction action;
566            // 获取按键事件的动作
567   	        OH_NativeXComponent_GetKeyEventAction(keyEvent, &action);
568   	        OH_NativeXComponent_KeyCode code;
569            // 获取按键事件的键码值
570   	        OH_NativeXComponent_GetKeyEventCode(keyEvent, &code);
571   	        OH_NativeXComponent_EventSourceType sourceType;
572            // 获取按键事件的输入源类型
573   	        OH_NativeXComponent_GetKeyEventSourceType(keyEvent, &sourceType);
574   	        int64_t deviceId;
575            // 获取按键事件的设备ID
576   	        OH_NativeXComponent_GetKeyEventDeviceId(keyEvent, &deviceId);
577   	        int64_t timeStamp;
578            // 获取按键事件的时间戳
579   	        OH_NativeXComponent_GetKeyEventTimestamp(keyEvent, &timeStamp);
580   	        OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "PluginRender",
581                         "KeyEvent Info: action=%{public}d, code=%{public}d, sourceType=%{public}d, deviceId=%{public}ld, "
582                         "timeStamp=%{public}ld",
583                         action, code, sourceType, deviceId, timeStamp);
584        } else {
585   	        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginRender", "GetKeyEvent error");
586        }
587    }
588    ```
589
590    (2) 注册XComponent事件回调函数,在XComponent事件触发时调用3.1步骤中定义的方法。
591
592    ```c++
593    void PluginRender::RegisterCallback(OH_NativeXComponent *NativeXComponent) {
594        // 设置组件创建事件的回调函数,组件创建时触发相关操作,初始化环境与绘制背景
595        renderCallback_.OnSurfaceCreated = OnSurfaceCreatedCB;
596        // 设置组件改变事件的回调函数,组件改变时触发相关操作
597        renderCallback_.OnSurfaceChanged = OnSurfaceChangedCB;
598        // 设置组件销毁事件的回调函数,组件销毁时触发相关操作,释放申请的资源
599        renderCallback_.OnSurfaceDestroyed = OnSurfaceDestroyedCB;
600        // 设置触摸事件的回调函数,在触摸事件触发时调用Node-API接口函数,从而调用原C++方法
601        renderCallback_.DispatchTouchEvent = DispatchTouchEventCB;
602        // 将OH_NativeXComponent_Callback注册给NativeXComponent
603        OH_NativeXComponent_RegisterCallback(NativeXComponent, &renderCallback_);
604
605        // 设置鼠标事件的回调函数,在触摸事件触发时调用Node-API接口函数,从而调用原C++方法
606        mouseCallback_.DispatchMouseEvent = DispatchMouseEventCB;
607        // 设置鼠标悬停事件的回调函数,在触摸事件触发时调用Node-API接口函数,从而调用原C++方法
608        mouseCallback_.DispatchHoverEvent = DispatchHoverEventCB;
609        // 将OH_NativeXComponent_MouseEvent_Callback注册给NativeXComponent
610        OH_NativeXComponent_RegisterMouseEventCallback(NativeXComponent, &mouseCallback_);
611
612        // 将OnFocusEventCB方法注册给NativeXComponent
613        OH_NativeXComponent_RegisterFocusEventCallback(NativeXComponent, OnFocusEventCB);
614        // 将OnKeyEventCB方法注册给NativeXComponent
615        OH_NativeXComponent_RegisterKeyEventCallback(NativeXComponent, OnKeyEventCB);
616        // 将OnBlurEventCB方法注册给 NativeXComponent
617        OH_NativeXComponent_RegisterBlurEventCallback(NativeXComponent, OnBlurEventCB);
618    }
619    ```
620
621    (3) 定义NapiDrawPattern方法,暴露到ArkTS侧的drawPattern()方法会执行该方法。
622
623    ```c++
624    napi_value PluginRender::NapiDrawPattern(napi_env env, napi_callback_info info) {
625        // ...
626        // 获取环境变量参数
627        napi_value thisArg;
628        if (napi_get_cb_info(env, info, nullptr, nullptr, &thisArg, nullptr) != napi_ok) {
629            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginRender", "NapiDrawPattern: napi_get_cb_info fail");
630            return nullptr;
631        }
632
633        // 获取环境变量中XComponent实例
634        napi_value exportInstance;
635        if (napi_get_named_property(env, thisArg, OH_NATIVE_XCOMPONENT_OBJ, &exportInstance) != napi_ok) {
636            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginRender",
637                         "NapiDrawPattern: napi_get_named_property fail");
638            return nullptr;
639        }
640
641        // 通过napi_unwrap接口,获取XComponent的实例指针
642        OH_NativeXComponent *NativeXComponent = nullptr;
643        if (napi_unwrap(env, exportInstance, reinterpret_cast<void **>(&NativeXComponent)) != napi_ok) {
644            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginRender", "NapiDrawPattern: napi_unwrap fail");
645            return nullptr;
646        }
647
648        // 获取XComponent实例的id
649        char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = { '\0' };
650        uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
651        if (OH_NativeXComponent_GetXComponentId(NativeXComponent, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
652            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginRender",
653                         "NapiDrawPattern: Unable to get XComponent id");
654            return nullptr;
655        }
656
657        std::string id(idStr);
658        PluginRender *render = PluginRender::GetInstance(id);
659        if (render) {
660            // 调用绘制方法
661            render->eglCore_->Draw();
662            OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "PluginRender", "render->eglCore_->Draw() executed");
663        }
664        return nullptr;
665    }
666    ```
667
668    (4) 定义TestGetXComponentStatus方法,暴露到ArkTS侧的getStatus()方法会执行该方法。
669
670    ```c++
671    napi_value PluginRender::TestGetXComponentStatus(napi_env env, napi_callback_info info) {
672        napi_value hasDraw;
673        napi_value hasChangeColor;
674
675        napi_create_int32(env, hasDraw_, &(hasDraw));
676        napi_create_int32(env, hasChangeColor_, &(hasChangeColor));
677
678        napi_value obj;
679        napi_create_object(env, &obj);
680        napi_set_named_property(env, obj, "hasDraw", hasDraw);
681        napi_set_named_property(env, obj, "hasChangeColor", hasChangeColor);
682
683        return obj;
684    }
685    ```
686
6874. 初始化环境,包括初始化可用的EGLDisplay、确定可用的surface配置、创建渲染区域surface、创建并关联上下文等。
688
689    ```c++
690    void EGLCore::UpdateSize(int width, int height) {
691        // width_和height_在头文件中定义
692        width_ = width;
693        height_ = height;
694    }
695
696    bool EGLCore::EglContextInit(void *window, int width, int height) {
697        // ...
698        UpdateSize(width, height);
699        eglWindow_ = static_cast<EGLNativeWindowType>(window);
700
701        // 初始化display
702        eglDisplay_ = eglGetDisplay(EGL_DEFAULT_DISPLAY);
703        if (eglDisplay_ == EGL_NO_DISPLAY) {
704            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "eglGetDisplay: unable to get EGL display");
705            return false;
706        }
707
708        // 初始化EGL
709        EGLint majorVersion;
710        EGLint minorVersion;
711        if (!eglInitialize(eglDisplay_, &majorVersion, &minorVersion)) {
712            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore",
713                "eglInitialize: unable to get initialize EGL display");
714            return false;
715        }
716
717        // 选择配置
718        const EGLint maxConfigSize = 1;
719        EGLint numConfigs;
720        if (!eglChooseConfig(eglDisplay_, ATTRIB_LIST, &eglConfig_, maxConfigSize, &numConfigs)) {
721            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "eglChooseConfig: unable to choose configs");
722            return false;
723        }
724
725        // 创建环境
726        return CreateEnvironment();
727    }
728    ```
729
730    ```c++
731    bool EGLCore::CreateEnvironment() {
732        // ...
733        // 创建surface
734        eglSurface_ = eglCreateWindowSurface(eglDisplay_, eglConfig_, eglWindow_, NULL);
735
736        // ...
737        // 创建context
738        eglContext_ = eglCreateContext(eglDisplay_, eglConfig_, EGL_NO_CONTEXT, CONTEXT_ATTRIBS);
739        if (!eglMakeCurrent(eglDisplay_, eglSurface_, eglSurface_, eglContext_)) {
740            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "eglMakeCurrent failed");
741            return false;
742        }
743
744        // 创建program
745        program_ = CreateProgram(VERTEX_SHADER, FRAGMENT_SHADER);
746        if (program_ == PROGRAM_ERROR) {
747            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "CreateProgram: unable to create program");
748            return false;
749        }
750        return true;
751    }
752
753    GLuint EGLCore::CreateProgram(const char* vertexShader, const char* fragShader) {
754        if ((vertexShader == nullptr) || (fragShader == nullptr)) {
755            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore",
756                         "createProgram: vertexShader or fragShader is null");
757            return PROGRAM_ERROR;
758        }
759
760        GLuint vertex = LoadShader(GL_VERTEX_SHADER, vertexShader);
761        if (vertex == PROGRAM_ERROR) {
762            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "createProgram vertex error");
763            return PROGRAM_ERROR;
764        }
765
766        GLuint fragment = LoadShader(GL_FRAGMENT_SHADER, fragShader);
767        if (fragment == PROGRAM_ERROR) {
768            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "createProgram fragment error");
769            return PROGRAM_ERROR;
770        }
771
772        GLuint program = glCreateProgram();
773        if (program == PROGRAM_ERROR) {
774            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "createProgram program error");
775            glDeleteShader(vertex);
776            glDeleteShader(fragment);
777            return PROGRAM_ERROR;
778        }
779
780        // The gl function has no return value.
781        glAttachShader(program, vertex);
782        glAttachShader(program, fragment);
783        glLinkProgram(program);
784
785        GLint linked;
786        glGetProgramiv(program, GL_LINK_STATUS, &linked);
787        if (linked != 0) {
788            glDeleteShader(vertex);
789            glDeleteShader(fragment);
790            return program;
791        }
792
793        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "createProgram linked error");
794        GLint infoLen = 0;
795        glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLen);
796        if (infoLen > 1) {
797            char* infoLog = (char*)malloc(sizeof(char) * (infoLen + 1));
798            memset(infoLog, 0, infoLen + 1);
799            glGetProgramInfoLog(program, infoLen, nullptr, infoLog);
800            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "glLinkProgram error = %s", infoLog);
801            free(infoLog);
802            infoLog = nullptr;
803        }
804        glDeleteShader(vertex);
805        glDeleteShader(fragment);
806        glDeleteProgram(program);
807        return PROGRAM_ERROR;
808    }
809
810    GLuint EGLCore::LoadShader(GLenum type, const char* shaderSrc) {
811        if ((type <= 0) || (shaderSrc == nullptr)) {
812            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "glCreateShader type or shaderSrc error");
813            return PROGRAM_ERROR;
814        }
815
816        GLuint shader = glCreateShader(type);
817        if (shader == 0) {
818            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "glCreateShader unable to load shader");
819            return PROGRAM_ERROR;
820        }
821
822        // The gl function has no return value.
823        glShaderSource(shader, 1, &shaderSrc, nullptr);
824        glCompileShader(shader);
825
826        GLint compiled;
827        glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
828        if (compiled != 0) {
829            return shader;
830        }
831
832        GLint infoLen = 0;
833        glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
834        if (infoLen <= 1) {
835            glDeleteShader(shader);
836            return PROGRAM_ERROR;
837        }
838
839        char *infoLog = (char*)malloc(sizeof(char) * (infoLen + 1));
840        if (infoLog != nullptr) {
841            memset(infoLog, 0, infoLen + 1);
842            glGetShaderInfoLog(shader, infoLen, nullptr, infoLog);
843            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "glCompileShader error = %s", infoLog);
844            free(infoLog);
845            infoLog = nullptr;
846        }
847        glDeleteShader(shader);
848        return PROGRAM_ERROR;
849    }
850
851    ```
852
8535. 渲染功能实现。
854
855   (1) 绘制背景。
856
857    ```c++
858    // ...
859    // 绘制背景颜色 #f4f4f4
860    const GLfloat BACKGROUND_COLOR[] = { 244.0f / 255, 244.0f / 255, 244.0f / 255, 1.0f };
861
862    // 绘制图案颜色
863    const GLfloat DRAW_COLOR[] = {126.0f / 255, 143.0f / 255, 251.0f / 255, 1.0f};
864
865    // 绘制图案改变后的颜色
866    const GLfloat CHANGE_COLOR[] = {146.0f / 255, 214.0f / 255, 204.0f / 255, 1.0f};
867
868    // 绘制背景顶点
869    const GLfloat BACKGROUND_RECTANGLE_VERTICES[] = {
870        -1.0f, 1.0f,
871        1.0f, 1.0f,
872        1.0f, -1.0f,
873        -1.0f, -1.0f
874    };
875    // ...
876    ```
877
878    ```c++
879    // 绘制背景颜色
880    void EGLCore::Background() {
881        GLint position = PrepareDraw();
882        if (position == POSITION_ERROR) {
883            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Background get position failed");
884            return;
885        }
886
887        if (!ExecuteDraw(position, BACKGROUND_COLOR, BACKGROUND_RECTANGLE_VERTICES,
888                         sizeof(BACKGROUND_RECTANGLE_VERTICES))) {
889            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Background execute draw failed");
890            return;
891        }
892
893        if (!FinishDraw()) {
894            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Background FinishDraw failed");
895            return;
896        }
897    }
898
899    // 绘前准备,获取position,创建成功时position值从0开始
900    GLint EGLCore::PrepareDraw() {
901        if ((eglDisplay_ == nullptr) || (eglSurface_ == nullptr) || (eglContext_ == nullptr) ||
902            (!eglMakeCurrent(eglDisplay_, eglSurface_, eglSurface_, eglContext_))) {
903            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "PrepareDraw: param error");
904            return POSITION_ERROR;
905        }
906
907        glViewport(DEFAULT_X_POSITION, DEFAULT_Y_POSITION, width_, height_);
908        glClearColor(GL_RED_DEFAULT, GL_GREEN_DEFAULT, GL_BLUE_DEFAULT, GL_ALPHA_DEFAULT);
909        glClear(GL_COLOR_BUFFER_BIT);
910        glUseProgram(program_);
911
912        return glGetAttribLocation(program_, POSITION_NAME);
913    }
914
915    // 依据传入参数在指定区域绘制指定颜色
916    bool EGLCore::ExecuteDraw(GLint position, const GLfloat *color, const GLfloat shapeVertices[], unsigned long vertSize) {
917        if ((position > 0) || (color == nullptr) || (vertSize / sizeof(shapeVertices[0]) != SHAPE_VERTICES_SIZE)) {
918            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "ExecuteDraw: param error");
919            return false;
920        }
921
922        glVertexAttribPointer(position, POINTER_SIZE, GL_FLOAT, GL_FALSE, 0, shapeVertices);
923        glEnableVertexAttribArray(position);
924        glVertexAttrib4fv(1, color);
925        glDrawArrays(GL_TRIANGLE_FAN, 0, TRIANGLE_FAN_SIZE);
926        glDisableVertexAttribArray(position);
927
928        return true;
929    }
930
931    // 结束绘制操作
932    bool EGLCore::FinishDraw() {
933        // 强制刷新缓冲
934        glFlush();
935        glFinish();
936        return eglSwapBuffers(eglDisplay_, eglSurface_);
937    }
938    ```
939
940   (2) 绘制图形。
941
942    ```c++
943    void EGLCore::Draw() {
944        flag_ = false;
945        OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "EGLCore", "Draw");
946        GLint position = PrepareDraw();
947        if (position == POSITION_ERROR) {
948            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw get position failed");
949            return;
950        }
951
952        // 绘制背景
953        if (!ExecuteDraw(position, BACKGROUND_COLOR, BACKGROUND_RECTANGLE_VERTICES,
954                         sizeof(BACKGROUND_RECTANGLE_VERTICES))) {
955            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw execute draw background failed");
956            return;
957        }
958
959        // 将五角星分为五个四边形,计算其中一个四边形的四个顶点
960        GLfloat rotateX = 0;
961        GLfloat rotateY = FIFTY_PERCENT * height_;
962        GLfloat centerX = 0;
963        GLfloat centerY = -rotateY * (M_PI / 180 * 54) * (M_PI / 180 * 18);
964        GLfloat leftX = -rotateY * (M_PI / 180 * 18);
965        GLfloat leftY = 0;
966        GLfloat rightX = rotateY * (M_PI / 180 * 18);
967        GLfloat rightY = 0;
968
969        // 确定绘制四边形的顶点,使用绘制区域的百分比表示
970        const GLfloat shapeVertices[] = {
971            centerX / width_, centerY / height_,
972            leftX / width_, leftY / height_,
973            rotateX / width_, rotateY / height_,
974            rightX / width_, rightY / height_
975        };
976
977        if (!ExecuteDrawStar(position, DRAW_COLOR, shapeVertices, sizeof(shapeVertices))) {
978            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw execute draw star failed");
979            return;
980        }
981
982        GLfloat rad = M_PI / 180 * 72;
983        for (int i = 0; i < 4; ++i)
984        {
985            // 旋转得其他四个四边形的顶点
986            rotate2d(centerX, centerY, &rotateX, &rotateY,rad);
987            rotate2d(centerX, centerY, &leftX, &leftY,rad);
988            rotate2d(centerX, centerY, &rightX, &rightY,rad);
989
990            // 确定绘制四边形的顶点,使用绘制区域的百分比表示
991            const GLfloat shapeVertices[] = {
992                    centerX / width_, centerY / height_,
993                    leftX / width_, leftY / height_,
994                    rotateX / width_, rotateY / height_,
995                    rightX / width_, rightY / height_
996            };
997
998            // 绘制图形
999            if (!ExecuteDrawStar(position, DRAW_COLOR, shapeVertices, sizeof(shapeVertices))) {
1000                OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw execute draw star failed");
1001                return;
1002            }
1003        }
1004
1005        // 结束绘制
1006        if (!FinishDraw()) {
1007            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw FinishDraw failed");
1008            return;
1009        }
1010
1011        flag_ = true;
1012    }
1013    ```
1014
1015   (3) 改变颜色,重新画一个大小相同颜色不同的图形,与原图形替换,达到改变颜色的效果。
1016
1017    ```c++
1018    void EGLCore::ChangeColor() {
1019        if (!flag_) {
1020            return;
1021        }
1022        OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "EGLCore", "ChangeColor");
1023        GLint position = PrepareDraw();
1024        if (position == POSITION_ERROR) {
1025            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "ChangeColor get position failed");
1026            return;
1027        }
1028
1029        // 绘制背景
1030        if (!ExecuteDraw(position, BACKGROUND_COLOR, BACKGROUND_RECTANGLE_VERTICES,
1031                         sizeof(BACKGROUND_RECTANGLE_VERTICES))) {
1032            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "ChangeColor execute draw background failed");
1033            return;
1034        }
1035
1036        // 确定绘制四边形的顶点,使用绘制区域的百分比表示
1037        GLfloat rotateX = 0;
1038        GLfloat rotateY = FIFTY_PERCENT * height_;
1039        GLfloat centerX = 0;
1040        GLfloat centerY = -rotateY * (M_PI / 180 * 54) * (M_PI / 180 * 18);
1041        GLfloat leftX = -rotateY * (M_PI / 180 * 18);
1042        GLfloat leftY = 0;
1043        GLfloat rightX = rotateY * (M_PI / 180 * 18);
1044        GLfloat rightY = 0;
1045
1046        // 确定绘制四边形的顶点,使用绘制区域的百分比表示
1047        const GLfloat shapeVertices[] = {
1048            centerX / width_, centerY / height_,
1049            leftX / width_, leftY / height_,
1050            rotateX / width_, rotateY / height_,
1051            rightX / width_, rightY / height_
1052        };
1053
1054        // 使用新的颜色绘制
1055        if (!ExecuteDrawNewStar(position, CHANGE_COLOR, shapeVertices, sizeof(shapeVertices))) {
1056            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw execute draw star failed");
1057            return;
1058        }
1059
1060        GLfloat rad = M_PI / 180 * 72;
1061        for (int i = 0; i < 4; ++i) {
1062            // 旋转得其他四个四边形的顶点
1063            rotate2d(centerX, centerY, &rotateX, &rotateY,rad);
1064            rotate2d(centerX, centerY, &leftX, &leftY,rad);
1065            rotate2d(centerX, centerY, &rightX, &rightY,rad);
1066
1067            // 确定绘制四边形的顶点,使用绘制区域的百分比表示
1068            const GLfloat shapeVertices[] = {
1069                    centerX / width_, centerY / height_,
1070                    leftX / width_, leftY / height_,
1071                    rotateX / width_, rotateY / height_,
1072                    rightX / width_, rightY / height_
1073            };
1074
1075            // 使用新的颜色绘制
1076            if (!ExecuteDrawNewStar(position, CHANGE_COLOR, shapeVertices, sizeof(shapeVertices))) {
1077                OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw execute draw star failed");
1078                return;
1079            }
1080        }
1081
1082        // 结束绘制
1083        if (!FinishDraw()) {
1084            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "ChangeColor FinishDraw failed");
1085        }
1086    }
1087
1088   bool EGLCore::ExecuteDrawNewStar(
1089       GLint position, const GLfloat* color, const GLfloat shapeVertices[], unsigned long vertSize) {
1090       if ((position > 0) || (color == nullptr) || (vertSize / sizeof(shapeVertices[0])) != SHAPE_VERTICES_SIZE) {
1091           OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "ExecuteDraw: param error");
1092           return false;
1093       }
1094
1095       // The gl function has no return value.
1096       glVertexAttribPointer(position, POINTER_SIZE, GL_FLOAT, GL_FALSE, 0, shapeVertices);
1097       glEnableVertexAttribArray(position);
1098       glVertexAttrib4fv(1, color);
1099       glDrawArrays(GL_TRIANGLE_FAN, 0, TRIANGLE_FAN_SIZE);
1100       glDisableVertexAttribArray(position);
1101
1102       return true;
1103   }
1104    ```
1105
11066. 释放相关资源。
1107
1108    (1) EGLCore类下创建Release()方法,释放初始化环境时申请的资源,包含窗口display、渲染区域surface、环境上下文context等。
1109
1110    ```c++
1111    void EGLCore::Release() {
1112        // 释放surface
1113        if ((eglDisplay_ == nullptr) || (eglSurface_ == nullptr) || (!eglDestroySurface(eglDisplay_, eglSurface_))) {
1114            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Release eglDestroySurface failed");
1115        }
1116        // 释放context
1117        if ((eglDisplay_ == nullptr) || (eglContext_ == nullptr) || (!eglDestroyContext(eglDisplay_, eglContext_))) {
1118            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Release eglDestroyContext failed");
1119        }
1120        // 释放display
1121        if ((eglDisplay_ == nullptr) || (!eglTerminate(eglDisplay_))) {
1122            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Release eglTerminate failed");
1123        }
1124    }
1125    ```
1126
1127    (2) PluginRender类添加Release()方法,释放EGLCore实例及PluginRender实例。
1128
1129    ```c++
1130    void PluginRender::Release(std::string &id) {
1131        PluginRender *render = PluginRender::GetInstance(id);
1132        if (render != nullptr) {
1133            render->eglCore_->Release();
1134            delete render->eglCore_;
1135            render->eglCore_ = nullptr;
1136            instance_.erase(instance_.find(id));
1137        }
1138    }
1139    ```
1140
11417. CMakeLists,使用CMake工具链将C++源代码编译成动态链接库文件。
1142
1143    ```CMake
1144    # 设置CMake最小版本
1145    cmake_minimum_required(VERSION 3.4.1)
1146    # 项目名称
1147    project(XComponent)
1148
1149    set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
1150    add_definitions(-DOHOS_PLATFORM)
1151    # 设置头文件搜索目录
1152    include_directories(
1153        ${NATIVERENDER_ROOT_PATH}
1154        ${NATIVERENDER_ROOT_PATH}/include
1155    )
1156    # 添加名为nativerender的动态库,库文件名为libnativerender.so,添加cpp文件
1157    add_library(nativerender SHARED
1158        render/egl_core.cpp
1159        render/plugin_render.cpp
1160        manager/plugin_manager.cpp
1161        napi_init.cpp
1162    )
1163
1164    find_library(
1165        EGL-lib
1166        EGL
1167    )
1168
1169    find_library(
1170        GLES-lib
1171        GLESv3
1172    )
1173
1174    find_library(
1175        hilog-lib
1176        hilog_ndk.z
1177    )
1178
1179    find_library(
1180        libace-lib
1181        ace_ndk.z
1182    )
1183
1184    find_library(
1185        libnapi-lib
1186        ace_napi.z
1187    )
1188
1189    find_library(
1190        libuv-lib
1191        uv
1192    )
1193    # 添加构建需要链接的库
1194    target_link_libraries(nativerender PUBLIC
1195        ${EGL-lib} ${GLES-lib} ${hilog-lib} ${libace-lib} ${libnapi-lib} ${libuv-lib})
1196    ```
1197
1198## ArkTS XComponent场景
1199
1200与Native XComponent不同,ArkTS XComponent不再需要libraryname参数。通过在ArkTS侧获取SurfaceId,布局信息、生命周期回调、触摸、鼠标、按键等事件回调等均在ArkTS侧触发,按需传递到Native侧进行处理。主要开发场景如下:
1201- 基于ArkTS侧获取的SurfaceId,在Native侧调用OH_NativeWindow_CreateNativeWindowFromSurfaceId接口创建出NativeWindow实例。
1202- 利用NativeWindow和EGL接口开发自定义绘制内容以及申请和提交Buffer到图形队列。
1203- ArkTS侧获取生命周期、事件等信息传递到Native侧处理。
1204
1205**接口说明**
1206
1207ArkTS侧的XComponentController
1208
1209| 接口名                                                       | 描述                                                         |
1210| ------------------------------------------------------------ | ------------------------------------------------------------ |
1211| getXComponentSurfaceId(): string                             | 获取XComponent对应Surface的ID。                                |
1212| onSurfaceCreated(surfaceId: string): void                    | 当XComponent持有的Surface创建后进行该回调。                    |
1213| onSurfaceChanged(surfaceId: string, rect: SurfaceRect): void | 当XComponent持有的Surface大小改变后(包括首次创建时的大小改变)进行该回调。 |
1214| onSurfaceDestroyed(surfaceId: string): void                  | 当XComponent持有的Surface销毁后进行该回调。                    |
1215
1216Native侧
1217
1218| 接口名                                                       | 描述                                                         |
1219| ------------------------------------------------------------ | ------------------------------------------------------------ |
1220| int32_t OH_NativeWindow_CreateNativeWindowFromSurfaceId (uint64_t surfaceId, OHNativeWindow **window ) | 通过surfaceId创建对应的OHNativeWindow。                        |
1221| void OH_NativeWindow_DestroyNativeWindow (OHNativeWindow* window) | 将OHNativeWindow对象的引用计数减1,当引用计数为0的时候,该OHNativeWindow对象会被析构掉。 |
1222
1223**开发步骤**
1224
1225以下步骤以SURFACE类型为例,描述了如何使用`XComponent组件`在ArkTS侧传入SurfaceId,在native侧创建NativeWindow实例,然后创建`EGL/GLES`环境,实现在主页面绘制图形,并可以改变图形的颜色。
1226
12271. 在界面中定义XComponent。
1228
1229    ```javascript
1230    // 函数声明,在cpp/types/libnativerender/Index.d.ts中定义
1231    type XComponentContextStatus = {
1232        hasDraw: boolean,
1233        hasChangeColor: boolean,
1234    };
1235    export const SetSurfaceId: (id: BigInt) => any;
1236    export const ChangeSurface: (id: BigInt, w: number, h: number) =>any;
1237    export const DrawPattern: (id: BigInt) => any;
1238    export const GetXComponentStatus: (id: BigInt) => XComponentContextStatus
1239    export const ChangeColor: (id: BigInt) => any;
1240    export const DestroySurface: (id: BigInt) => any;
1241    ```
1242
1243    ```typescript
1244    import nativeRender from 'libnativerender.so'
1245
1246    // 重写XComponentController,设置生命周期回调
1247    class MyXComponentController extends XComponentController {
1248        onSurfaceCreated(surfaceId: string): void {
1249            console.log(`onSurfaceCreated surfaceId: ${surfaceId}`)
1250            nativeRender.SetSurfaceId(BigInt(surfaceId));
1251        }
1252
1253        onSurfaceChanged(surfaceId: string, rect: SurfaceRect): void {
1254            console.log(`onSurfaceChanged surfaceId: ${surfaceId}, rect: ${JSON.stringify(rect)}}`)
1255            // 在onSurfaceChanged中调用ChangeSurface绘制内容
1256            nativeRender.ChangeSurface(BigInt(surfaceId), rect.surfaceWidth, rect.surfaceHeight)
1257        }
1258
1259        onSurfaceDestroyed(surfaceId: string): void {
1260            console.log(`onSurfaceDestroyed surfaceId: ${surfaceId}`)
1261            nativeRender.DestroySurface(BigInt(surfaceId))
1262        }
1263    }
1264
1265    @Entry
1266    @Component
1267    struct Index {
1268        @State currentStatus: string = "index";
1269        xComponentController: XComponentController = new MyXComponentController();
1270
1271        build() {
1272            Column() {
1273                //...
1274                //在xxx.ets 中定义 XComponent
1275                Column({ space: 10 }) {
1276                    XComponent({
1277                        type: XComponentType.SURFACE,
1278                        controller: this.xComponentController
1279                    })
1280                Text(this.currentStatus)
1281                    .fontSize('24fp')
1282                    .fontWeight(500)
1283                }
1284                .onClick(() => {
1285                    let surfaceId = this.xComponentController.getXComponentSurfaceId()
1286                    nativeRender.ChangeColor(BigInt(surfaceId))
1287                    let hasChangeColor: boolean = false;
1288                    if (nativeRender.GetXComponentStatus(BigInt(surfaceId))) {
1289                        hasChangeColor = nativeRender.GetXComponentStatus(BigInt(surfaceId)).hasChangeColor;
1290                    }
1291                    if (hasChangeColor) {
1292                        this.currentStatus = "change color";
1293                    }
1294                })
1295
1296                //...
1297                Row() {
1298                    Button('Draw Star')
1299                        .fontSize('16fp')
1300                        .fontWeight(500)
1301                        .margin({ bottom: 24 })
1302                        .onClick(() => {
1303                            let surfaceId = this.xComponentController.getXComponentSurfaceId()
1304                            nativeRender.DrawPattern(BigInt(surfaceId))
1305                            let hasDraw: boolean = false;
1306                            if (nativeRender.GetXComponentStatus(BigInt(surfaceId))) {
1307                                hasDraw = nativeRender.GetXComponentStatus(BigInt(surfaceId)).hasDraw;
1308                            }
1309                            if (hasDraw) {
1310                                this.currentStatus = "draw star"
1311                            }
1312                        })
1313                        .width('53.6%')
1314                        .height(40)
1315                }
1316                .width('100%')
1317                .justifyContent(FlexAlign.Center)
1318                .alignItems(VerticalAlign.Bottom)
1319                .layoutWeight(1)
1320            }
1321            .width('100%')
1322            .height('100%')
1323        }
1324    }
1325    ```
1326
13272. Node-API模块注册,具体使用请参考[Native API在应用工程中的使用指导](../napi/napi-guidelines.md)。
1328
1329    ```typescript
1330    #include <hilog/log.h>
1331    #include "common/common.h"
1332    #include "manager/plugin_manager.h"
1333    namespace NativeXComponentSample {
1334    // 在napi_init.cpp文件中,Init方法注册接口函数,从而将封装的C++方法传递出来,供ArkTS侧调用
1335    EXTERN_C_START
1336    static napi_value Init(napi_env env, napi_value exports) {
1337        // ...
1338        // 向ArkTS侧暴露接口SetSurfaceId(),ChangeSurface(),DestroySurface(),
1339        // DrawPattern(),ChangeColor(),GetXComponentStatus()
1340        napi_property_descriptor desc[] = {
1341            {"SetSurfaceId", nullptr, PluginManager::SetSurfaceId, nullptr, nullptr, nullptr, napi_default, nullptr},
1342            {"ChangeSurface", nullptr, PluginManager::ChangeSurface, nullptr, nullptr, nullptr, napi_default, nullptr},
1343            {"DestroySurface", nullptr, PluginManager::DestroySurface, nullptr, nullptr, nullptr, napi_default, nullptr},
1344            {"DrawPattern", nullptr, PluginManager::DrawPattern, nullptr, nullptr, nullptr, napi_default, nullptr},
1345            {"ChangeColor", nullptr, PluginManager::ChangeColor, nullptr, nullptr, nullptr, napi_default, nullptr},
1346            {"GetXComponentStatus", nullptr, PluginManager::GetXComponentStatus, nullptr, nullptr, nullptr, napi_default,
1347             nullptr}};
1348        if (napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc) != napi_ok) {
1349            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Init", "napi_define_properties failed");
1350            return nullptr;
1351        }
1352        return exports;
1353    }
1354    EXTERN_C_END
1355    // 编写接口的描述信息,根据实际需要可以修改对应参数
1356    static napi_module nativerenderModule = {.nm_version = 1,
1357                                             .nm_flags = 0,
1358                                             .nm_filename = nullptr,
1359                                             // 入口函数
1360                                             .nm_register_func = Init,
1361                                             // 模块名称
1362                                             .nm_modname = "nativerender",
1363                                             .nm_priv = ((void *)0),
1364                                             .reserved = {0}};
1365    } // namespace NativeXComponentSample
1366    // __attribute__((constructor))修饰的方法由系统自动调用,使用Node-API接口napi_module_register()传入模块描述信息进行模块注册
1367    extern "C" __attribute__((constructor)) void RegisterModule(void) {
1368        napi_module_register(&NativeXComponentSample::nativerenderModule);
1369    }
1370    ```
1371
13723. 上述注册的六个函数在native侧具体实现。
1373
1374    ```cpp
1375    // PluginManager类定义
1376    class PluginManager {
1377    public:
1378        ~PluginManager();
1379        static PluginRender *GetPluginRender(int64_t &id);
1380        static napi_value ChangeColor(napi_env env, napi_callback_info info);
1381        static napi_value DrawPattern(napi_env env, napi_callback_info info);
1382        static napi_value SetSurfaceId(napi_env env, napi_callback_info info);
1383        static napi_value ChangeSurface(napi_env env, napi_callback_info info);
1384        static napi_value DestroySurface(napi_env env, napi_callback_info info);
1385        static napi_value GetXComponentStatus(napi_env env, napi_callback_info info);
1386
1387    public:
1388        static std::unordered_map<int64_t, PluginRender *> pluginRenderMap_;
1389        static std::unordered_map<int64_t, OHNativeWindow *> windowMap_;
1390    };
1391
1392    // 解析从ArkTS侧传入的surfaceId,此处surfaceId是一个64位int值
1393    int64_t ParseId(napi_env env, napi_callback_info info) {
1394        if ((env == nullptr) || (info == nullptr)) {
1395            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ParseId", "env or info is null");
1396            return -1;
1397        }
1398        size_t argc = 1;
1399        napi_value args[1] = {nullptr};
1400        if (napi_ok != napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)) {
1401            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ParseId", "GetContext napi_get_cb_info failed");
1402            return -1;
1403        }
1404        int64_t value = 0;
1405        bool lossless = true;
1406        if (napi_ok != napi_get_value_bigint_int64(env, args[0], &value, &lossless)) {
1407            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ParseId", "Get value failed");
1408            return -1;
1409        }
1410        return value;
1411    }
1412
1413    // 设置SurfaceId,基于SurfaceId完成对NativeWindow的初始化
1414    napi_value PluginManager::SetSurfaceId(napi_env env, napi_callback_info info) {
1415        int64_t surfaceId = ParseId(env, info);
1416        OHNativeWindow *nativeWindow;
1417        PluginRender *pluginRender;
1418        if (windowMap_.find(surfaceId) == windowMap_.end()) {
1419            OH_NativeWindow_CreateNativeWindowFromSurfaceId(surfaceId, &nativeWindow);
1420            windowMap_[surfaceId] = nativeWindow;
1421        }
1422        if (pluginRenderMap_.find(surfaceId) == pluginRenderMap_.end()) {
1423            pluginRender = new PluginRender(surfaceId);
1424            pluginRenderMap_[surfaceId] = pluginRender;
1425        }
1426        pluginRender->InitNativeWindow(nativeWindow);
1427        return nullptr;
1428    }
1429
1430    void PluginRender::InitNativeWindow(OHNativeWindow *window) {
1431        eglCore_->EglContextInit(window); // 参考Native XComponent场景 EglContextInit的实现
1432    }
1433
1434    // 根据传入的surfaceId、width、height实现surface大小的变动
1435    napi_value PluginManager::ChangeSurface(napi_env env, napi_callback_info info) {
1436        if ((env == nullptr) || (info == nullptr)) {
1437            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager",
1438                         "ChangeSurface: OnLoad env or info is null");
1439            return nullptr;
1440        }
1441        int64_t surfaceId = 0;
1442        size_t argc = 3;
1443        napi_value args[3] = {nullptr};
1444
1445        if (napi_ok != napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)) {
1446            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager",
1447                         "ChangeSurface: GetContext napi_get_cb_info failed");
1448            return nullptr;
1449        }
1450        bool lossless = true;
1451        int index = 0;
1452        if (napi_ok != napi_get_value_bigint_int64(env, args[index++], &surfaceId, &lossless)) {
1453            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "ChangeSurface: Get value failed");
1454            return nullptr;
1455        }
1456        double width;
1457        if (napi_ok != napi_get_value_double(env, args[index++], &width)) {
1458            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "ChangeSurface: Get width failed");
1459            return nullptr;
1460        }
1461        double height;
1462        if (napi_ok != napi_get_value_double(env, args[index++], &height)) {
1463            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "ChangeSurface: Get height failed");
1464            return nullptr;
1465        }
1466        auto pluginRender = GetPluginRender(surfaceId);
1467        if (pluginRender == nullptr) {
1468            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "ChangeSurface: Get pluginRender failed");
1469            return nullptr;
1470        }
1471        pluginRender->UpdateNativeWindowSize(width, height);
1472        return nullptr;
1473    }
1474
1475    void PluginRender::UpdateNativeWindowSize(int width, int height) {
1476        eglCore_->UpdateSize(width, height); // 参考Native XComponent场景 UpdateSize的实现
1477        if (!hasChangeColor_ && !hasDraw_) {
1478            eglCore_->Background(); // 参考Native XComponent场景 Background的实现
1479        }
1480    }
1481
1482    // 销毁surface
1483    napi_value PluginManager::DestroySurface(napi_env env, napi_callback_info info) {
1484        int64_t surfaceId = ParseId(env, info);
1485        auto pluginRenderMapIter = pluginRenderMap_.find(surfaceId);
1486        if (pluginRenderMapIter != pluginRenderMap_.end()) {
1487            delete pluginRenderMapIter->second;
1488            pluginRenderMap_.erase(pluginRenderMapIter);
1489        }
1490        auto windowMapIter = windowMap_.find(surfaceId);
1491        if (windowMapIter != windowMap_.end()) {
1492            OH_NativeWindow_DestroyNativeWindow(windowMapIter->second);
1493            windowMap_.erase(windowMapIter);
1494        }
1495        return nullptr;
1496    }
1497
1498    // 实现EGL绘画逻辑
1499    napi_value PluginManager::DrawPattern(napi_env env, napi_callback_info info) {
1500        int64_t surfaceId = ParseId(env, info);
1501        auto pluginRender = GetPluginRender(surfaceId);
1502        if (pluginRender == nullptr) {
1503            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "DrawPattern: Get pluginRender failed");
1504            return nullptr;
1505        }
1506        pluginRender->DrawPattern();
1507        return nullptr;
1508    }
1509
1510    PluginRender *PluginManager::GetPluginRender(int64_t &id) {
1511        if (pluginRenderMap_.find(id) != pluginRenderMap_.end()) {
1512            return pluginRenderMap_[id];
1513        }
1514        return nullptr;
1515    }
1516
1517    void PluginRender::DrawPattern() {
1518        eglCore_->Draw(hasDraw_); // 参考Native XComponent场景 Draw实现
1519    }
1520
1521    // 实现改变绘制图形颜色的功能
1522    napi_value PluginManager::ChangeColor(napi_env env, napi_callback_info info) {
1523        int64_t surfaceId = ParseId(env, info);
1524        auto pluginRender = GetPluginRender(surfaceId);
1525        if (pluginRender == nullptr) {
1526            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "ChangeColor: Get pluginRender failed");
1527            return nullptr;
1528        }
1529        pluginRender->ChangeColor(); // 参考Native XComponent场景 ChangeColor实现
1530        return nullptr;
1531    }
1532
1533    void PluginRender::ChangeColor() { eglCore_->ChangeColor(hasChangeColor_); }
1534
1535    // 获得xcomponent状态,并返回至ArkTS侧
1536    napi_value PluginManager::GetXComponentStatus(napi_env env, napi_callback_info info) {
1537        int64_t surfaceId = ParseId(env, info);
1538        auto pluginRender = GetPluginRender(surfaceId);
1539        if (pluginRender == nullptr) {
1540            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager",
1541                         "GetXComponentStatus: Get pluginRender failed");
1542            return nullptr;
1543        }
1544        napi_value hasDraw;
1545        napi_value hasChangeColor;
1546        napi_status ret = napi_create_int32(env, pluginRender->HasDraw(), &(hasDraw));
1547        if (ret != napi_ok) {
1548            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager",
1549                         "GetXComponentStatus: napi_create_int32 hasDraw_ error");
1550            return nullptr;
1551        }
1552        ret = napi_create_int32(env, pluginRender->HasChangedColor(), &(hasChangeColor));
1553        if (ret != napi_ok) {
1554            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager",
1555                         "GetXComponentStatus: napi_create_int32 hasChangeColor_ error");
1556            return nullptr;
1557        }
1558        napi_value obj;
1559        ret = napi_create_object(env, &obj);
1560        if (ret != napi_ok) {
1561            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager",
1562                         "GetXComponentStatus: napi_create_object error");
1563            return nullptr;
1564        }
1565        ret = napi_set_named_property(env, obj, "hasDraw", hasDraw);
1566        if (ret != napi_ok) {
1567            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager",
1568                         "GetXComponentStatus: napi_set_named_property hasDraw error");
1569            return nullptr;
1570        }
1571        ret = napi_set_named_property(env, obj, "hasChangeColor", hasChangeColor);
1572        if (ret != napi_ok) {
1573            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager",
1574                         "GetXComponentStatus: napi_set_named_property hasChangeColor error");
1575            return nullptr;
1576        }
1577        return obj;
1578    }
1579
1580    int32_t PluginRender::HasDraw() { return hasDraw_; }
1581
1582    int32_t PluginRender::HasChangedColor() { return hasChangeColor_; }
1583    ```
1584
15854. 配置具体的CMakeLists,使用CMake工具链将C++源代码编译成动态链接库文件。
1586
1587    ```cmake
1588    # 设置CMake最小版本.
1589    cmake_minimum_required(VERSION 3.4.1)
1590    # 项目名称
1591    project(XComponent)
1592
1593    set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
1594    add_definitions(-DOHOS_PLATFORM)
1595    # 设置头文件搜索目录
1596    include_directories(
1597        ${NATIVERENDER_ROOT_PATH}
1598        ${NATIVERENDER_ROOT_PATH}/include
1599    )
1600    # 添加名为nativerender的动态库,库文件名为libnativerender.so,添加cpp文件
1601    add_library(nativerender SHARED
1602        render/egl_core.cpp
1603        render/plugin_render.cpp
1604        manager/plugin_manager.cpp
1605        napi_init.cpp
1606    )
1607
1608    find_library(
1609        # Sets the name of the path variable.
1610        EGL-lib
1611        # Specifies the name of the NDK library that
1612        # you want CMake to locate.
1613        EGL
1614    )
1615
1616    find_library(
1617        # Sets the name of the path variable.
1618        GLES-lib
1619        # Specifies the name of the NDK library that
1620        # you want CMake to locate.
1621        GLESv3
1622    )
1623
1624    find_library(
1625        # Sets the name of the path variable.
1626        hilog-lib
1627        # Specifies the name of the NDK library that
1628        # you want CMake to locate.
1629        hilog_ndk.z
1630    )
1631
1632    find_library(
1633        # Sets the name of the path variable.
1634        libace-lib
1635        # Specifies the name of the NDK library that
1636        # you want CMake to locate.
1637        ace_ndk.z
1638    )
1639
1640    find_library(
1641        # Sets the name of the path variable.
1642        libnapi-lib
1643        # Specifies the name of the NDK library that
1644        # you want CMake to locate.
1645        ace_napi.z
1646    )
1647
1648    find_library(
1649        # Sets the name of the path variable.
1650        libuv-lib
1651        # Specifies the name of the NDK library that
1652        # you want CMake to locate.
1653        uv
1654    )
1655    # 添加构建需要链接的库
1656    target_link_libraries(nativerender PUBLIC
1657        ${EGL-lib} ${GLES-lib} ${hilog-lib} ${libace-lib} ${libnapi-lib} ${libuv-lib} libnative_window.so)
1658    ```
1659
1660## 自绘制原理说明
1661
1662XComponent持有一个surface,开发者能通过调用NativeWindow等接口,申请并提交Buffer至图形队列,以此方式将自绘制内容传送至该surface。XComponent负责将此surface整合进UI界面,其中展示的内容正是开发者传送的自绘制内容。surface的默认位置与大小与XComponent组件一致,开发者可利用[setXComponentSurfaceRect](../reference/apis-arkui/arkui-ts/ts-basic-components-xcomponent.md#setxcomponentsurfacerect12)接口自定义调整surface的位置和大小。
1663
1664> **说明:**
1665>
1666> 当开发者传输的绘制内容包含透明元素时,surface区域的显示效果会与下方内容进行合成展示。例如,若传输的内容完全透明,且XComponent的背景色被设置为黑色,同时Surface保持默认的大小与位置,则最终显示的将是一片黑色区域。
1667
1668## 生命周期说明
1669
1670开发者在ArkTS侧使用如下代码即可用XComponent组件进行利用EGL/OpenGLES渲染的开发。
1671
1672```typescript
1673@Builder
1674function myComponent() {
1675  XComponent({ id: 'xcomponentId1', type: XComponentType.SURFACE, libraryname: 'nativerender' })
1676    .onLoad((context) => {})
1677    .onDestroy(() => {})
1678}
1679```
1680
1681### onLoad事件
1682
1683触发时刻:XComponent准备好surface后触发。
1684
1685参数context:其上面挂载了暴露在模块上的Native方法,使用方法类似于利用 import context from "libnativerender.so" 直接加载模块后获得的context实例。
1686
1687**时序**:
1688
1689​	Native XComponent场景:
1690
1691​	onLoad事件的触发和surface相关,其和Native侧的OnSurfaceCreated的时序如下图:
1692
1693![onLoad](./figures/onLoad.png)
1694
1695​	ArkTS XComponent场景:
1696
1697​	onLoad事件的触发和surface相关,其和ArkTS侧的OnSurfaceCreated的时序如下图:
1698
1699![onLoad](./figures/onLoad1.png)
1700
1701
1702
1703### onDestroy事件
1704
1705触发时刻:XComponent组件被销毁时触发,与一般ArkUI的组件销毁时机一致。
1706
1707**时序:**
1708
1709​	Native XComponent场景:
1710
1711​	onDestroy事件的触发和surface相关,其和Native侧的OnSurfaceDestroyed的时序如下图:
1712
1713![onDestroy](./figures/onDestroy.png)
1714
1715
1716
1717​	ArkTS XComponent场景:
1718
1719​	onDestroy事件的触发和surface相关,其和ArkTS侧的OnSurfaceDestroyed的时序如下图:
1720
1721![onDestroy](./figures/onDestroy1.png)
1722
1723<!--RP1--><!--RP1End-->
1724
1725## 相关实例
1726
1727针对Native XComponent的使用,有以下相关实例可供参考:
1728
1729- [XComponent3D(API10)](https://gitee.com/openharmony/applications_app_samples/tree/master/code/BasicFeature/Native/XComponent3D)
1730- [Native XComponent(API10)](https://gitee.com/openharmony/applications_app_samples/tree/master/code/BasicFeature/Native/XComponent)
1731- [OpenGL三棱椎(API10)](https://gitee.com/openharmony/applications_app_samples/tree/master/code/BasicFeature/Native/NdkOpenGL)
1732
1733针对ArkTS XComponent的使用,有以下相关实例可供参考:
1734
1735- [ArkTSXComponent(API12)](https://gitee.com/openharmony/applications_app_samples/tree/master/code/BasicFeature/Native/ArkTSXComponent)
1736