文档管理中心

自定义渲染 (XComponent)

概述

XComponent组件作为一种渲染组件,可用于EGL/OpenGLES和媒体数据写入,通过使用XComponent持有的“NativeWindow”来渲染画面,通常用于满足开发者较为复杂的自定义渲染需求,例如相机预览流的显示和游戏画面的渲染。其可通过指定type字段来实现不同的渲染方式,分别为XComponentType.SURFACE和XComponentType.TEXTURE。对于SURFACE类型,开发者将定制的绘制内容单独展示到屏幕上。对于TEXTURE类型,开发者将定制的绘制内容和XComponent组件的内容合成后展示到屏幕上。

目前XComponent组件主要有两个应用场景。一个是Native XComponent场景,在native层获取Native XComponent实例,在native侧注册XComponent的生命周期回调,以及触摸、鼠标、按键等事件回调。另一个是ArkTS XComponent场景,在ArkTS侧获取SurfaceId,生命周期回调、触摸、鼠标、按键等事件回调等均在ArkTS侧触发。

Native XComponent场景

在XComponent组件构造函数的libraryname中定义需要加载的动态库,而后应用就可以在Native层获取Native XComponent实例,其是XComponent组件提供在Native层的实例,可作为ArkTS层和Native层XComponent绑定的桥梁。XComponent所提供的NDK接口都依赖于该实例。接口能力包括获取NativeWindow实例、获取XComponent的布局/事件信息、注册XComponent的生命周期回调、注册XComponent的触摸、鼠标、按键等事件回调。针对Native XComponent,主要的开发场景如下:

  • 利用Native XComponent提供的接口注册XComponent的生命周期和事件回调。
  • 在这些回调中进行初始化环境、获取当前状态、响应各类事件的开发。
  • 利用NativeWindow和EGL接口开发自定义绘制内容以及申请和提交Buffer到图形队列。

接口说明

展开
接口名 描述
OH_NativeXComponent_GetXComponentId(OH_NativeXComponent* component, char* id, uint64_t* size) 获取XComponent的id。
OH_NativeXComponent_GetXComponentSize(OH_NativeXComponent* component, const void* window, uint64_t* width, uint64_t* height) 获取XComponent持有的surface的大小。
OH_NativeXComponent_GetXComponentOffset(OH_NativeXComponent* component, const void* window, double* x, double* y) 获取XComponent持有的surface相对其父组件左顶点的偏移量。
OH_NativeXComponent_GetTouchEvent(OH_NativeXComponent* component, const void* window, OH_NativeXComponent_TouchEvent* touchEvent) 获取由XComponent触发的触摸事件。touchEvent内的具体属性值可参考OH_NativeXComponent_TouchEvent
OH_NativeXComponent_GetTouchPointToolType(OH_NativeXComponent* component, uint32_t pointIndex, OH_NativeXComponent_TouchPointToolType* toolType) 获取XComponent触摸点的工具类型。
OH_NativeXComponent_GetTouchPointTiltX(OH_NativeXComponent* component, uint32_t pointIndex, float* tiltX) 获取XComponent触摸点处相对X轴的倾斜角度。
OH_NativeXComponent_GetTouchPointTiltY(OH_NativeXComponent* component, uint32_t pointIndex, float* tiltY) 获取XComponent触摸点处相对Y轴的倾斜角度。
OH_NativeXComponent_GetMouseEvent(OH_NativeXComponent* component, const void* window, OH_NativeXComponent_MouseEvent* mouseEvent) 获取由XComponent触发的鼠标事件。
OH_NativeXComponent_RegisterCallback(OH_NativeXComponent* component, OH_NativeXComponent_Callback* callback) 为此OH_NativeXComponent实例注册生命周期和触摸事件回调。
OH_NativeXComponent_RegisterMouseEventCallback(OH_NativeXComponent* component, OH_NativeXComponent_MouseEvent_Callback* callback) 为此OH_NativeXComponent实例注册鼠标事件回调。
OH_NativeXComponent_RegisterFocusEventCallback(OH_NativeXComponent* component, void (*callback)(OH_NativeXComponent* component, void* window)) 为此OH_NativeXComponent实例注册获得焦点事件回调。
OH_NativeXComponent_RegisterKeyEventCallback(OH_NativeXComponent* component, void (*callback)(OH_NativeXComponent* component, void* window)) 为此OH_NativeXComponent实例注册按键事件回调。
OH_NativeXComponent_RegisterBlurEventCallback(OH_NativeXComponent* component, void (*callback)(OH_NativeXComponent* component, void* window)) 为此OH_NativeXComponent实例注册失去焦点事件回调。
OH_NativeXComponent_GetKeyEvent(OH_NativeXComponent* component, OH_NativeXComponent_KeyEvent** keyEvent) 获取由XComponent触发的按键事件。
OH_NativeXComponent_GetKeyEventAction(OH_NativeXComponent_KeyEvent* keyEvent, OH_NativeXComponent_KeyAction* action) 获取按键事件的动作。
OH_NativeXComponent_GetKeyEventCode(OH_NativeXComponent_KeyEvent* keyEvent, OH_NativeXComponent_KeyCode* code) 获取按键事件的键码值。
OH_NativeXComponent_GetKeyEventSourceType(OH_NativeXComponent_KeyEvent* keyEvent, OH_NativeXComponent_EventSourceType* sourceType) 获取按键事件的输入源类型。
OH_NativeXComponent_GetKeyEventDeviceId(OH_NativeXComponent_KeyEvent* keyEvent, int64_t* deviceId) 获取按键事件的设备ID。
OH_NativeXComponent_GetKeyEventTimestamp(OH_NativeXComponent_KeyEvent* keyEvent, int64_t* timestamp) 获取按键事件的时间戳。
说明

上述接口不支持跨线程访问。

XComponent销毁(onSurfaceDestroyed回调触发后)时会释放上述接口中获取的OH_NativeXComponent和window对象。如果在之后再次使用获取的这些对象有可能会导致出现使用野指针或空指针的崩溃问题,需要开发者避免。

开发步骤

以下步骤以SURFACE类型为例,描述了如何使用XComponent组件调用Node-API接口来创建EGL/GLES环境,实现在主页面绘制图形,并可以改变图形的颜色。

  1. 在界面中定义XComponent。

    收起
    自动换行
    深色代码主题
    复制
    1. //在ets/interface/XComponentContext.ts中声明native侧接口
    2. export default interface XComponentContext {
    3. drawPattern(): void;
    4. getStatus(): XComponentContextStatus;
    5. };
    6. type XComponentContextStatus = {
    7. hasDraw: boolean,
    8. hasChangeColor: boolean,
    9. };
    收起
    自动换行
    深色代码主题
    复制
    1. import XComponentContext from "../interface/XComponentContext"
    2. @Entry
    3. @Component
    4. struct Index {
    5. xComponentContext: XComponentContext | undefined = undefined;
    6. xComponentAttrs: XComponentAttrs = {
    7. id: 'xcomponentId',
    8. type: XComponentType.SURFACE,
    9. libraryname: 'nativerender'
    10. }
    11. build() {
    12. Row() {
    13. // ...
    14. // 在xxx.ets 中定义 XComponent
    15. XComponent(this.xComponentAttrs)
    16. .focusable(true) // 可响应键盘事件
    17. .onLoad((xComponentContext) => {
    18. console.log("onLoad");
    19. this.xComponentContext = xComponentContext as XComponentContext;
    20. // 调用drawPattern绘制内容
    21. if (this.xComponentContext) {
    22. this.xComponentContext.drawPattern();
    23. if (this.xComponentContext.getStatus()) {
    24. this.xComponentContext.getStatus().hasDraw;
    25. }
    26. }
    27. })
    28. .onDestroy(() => {
    29. console.log("onDestroy");
    30. })
    31. // ...
    32. }
    33. .onClick(() => {
    34. // 调用getStatus改变绘制内容
    35. if (this.xComponentContext && this.xComponentContext.getStatus()) {
    36. this.xComponentContext.getStatus().hasChangeColor;
    37. }
    38. })
    39. .height('100%')
    40. }
    41. }
    42. interface XComponentAttrs {
    43. id: string;
    44. type: number;
    45. libraryname: string;
    46. }
  2. Node-API模块注册,具体使用请参考Native API在应用工程中的使用指导

    收起
    自动换行
    深色代码主题
    复制
    1. #include <hilog/log.h>
    2. #include "common/common.h"
    3. #include "manager/plugin_manager.h"
    4. // 在napi_init.cpp文件中,Init方法注册接口函数,从而将封装的C++方法传递出来,供ArkTS侧调用
    5. EXTERN_C_START
    6. static napi_value Init(napi_env env, napi_value exports) {
    7. // ...
    8. // 向ArkTS侧暴露接口getContext()
    9. napi_property_descriptor desc[] = {
    10. { "getContext", nullptr, PluginManager::GetContext, nullptr, nullptr, nullptr, napi_default, nullptr }
    11. };
    12. if (napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc) != napi_ok) {
    13. OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Init", "napi_define_properties failed");
    14. return nullptr;
    15. }
    16. // 方法内检查环境变量是否包含XComponent组件实例,若实例存在则导出绘制相关接口
    17. PluginManager::GetInstance()->Export(env, exports);
    18. return exports;
    19. }
    20. EXTERN_C_END
    21. // 编写接口的描述信息,根据实际需要可以修改对应参数
    22. static napi_module nativerenderModule = {
    23. .nm_version = 1,
    24. .nm_flags = 0,
    25. .nm_filename = nullptr,
    26. // 入口函数
    27. .nm_register_func = Init,// 指定加载对应模块时的回调函数
    28. // 模块名称
    29. .nm_modname =
    30. "nativerender", // 指定模块名称,对于XComponent相关开发,这个名称必须和ArkTS侧XComponent中libraryname的值保持一致
    31. .nm_priv = ((void *)0),
    32. .reserved = {0}};
    33. // __attribute__((constructor))修饰的方法由系统自动调用,使用Node-API接口napi_module_register()传入模块描述信息进行模块注册
    34. extern "C" __attribute__((constructor)) void RegisterModule(void) { napi_module_register(&nativerenderModule); }
    收起
    自动换行
    深色代码主题
    复制
    1. // 检查环境变量是否包含XComponent组件实例,若实例存在则导出绘制相关接口
    2. void PluginManager::Export(napi_env env, napi_value exports)
    3. {
    4. if ((env == nullptr) || (exports == nullptr)) {
    5. OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "Export: env or exports is null");
    6. return;
    7. }
    8. napi_value exportInstance = nullptr;
    9. if (napi_get_named_property(env, exports, OH_NATIVE_XCOMPONENT_OBJ, &exportInstance) != napi_ok) {
    10. OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "Export: napi_get_named_property fail");
    11. }
    12. // 获取nativeXComponent
    13. OH_NativeXComponent* nativeXComponent = nullptr;
    14. if (napi_unwrap(env, exportInstance, reinterpret_cast<void**>(&nativeXComponent)) != napi_ok) {
    15. OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "Export: napi_unwrap fail");
    16. return;
    17. }
    18. // 获取XComponent的id,即ArkTS侧XComponent组件构造中的id参数
    19. char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {'\0'};
    20. uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
    21. if (OH_NativeXComponent_GetXComponentId(nativeXComponent, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
    22. OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager",
    23. "Export: OH_NativeXComponent_GetXComponentId fail");
    24. return;
    25. }
    26. std::string id(idStr);
    27. auto context = PluginManager::GetInstance();
    28. if ((context != nullptr) && (nativeXComponent != nullptr)) {
    29. context->SetNativeXComponent(id, nativeXComponent);
    30. auto render = context->GetRender(id);
    31. if (render != nullptr) {
    32. // 注册回调函数
    33. render->RegisterCallback(nativeXComponent);
    34. // 方法内使用Node-API,导出绘制相关接口,向ArkTS侧暴露绘制相关方法
    35. render->Export(env, exports);
    36. }
    37. }
    38. }
    收起
    自动换行
    深色代码主题
    复制
    1. // 使用Node-API中的napi_define_properties方法,向ArkTS侧暴露drawPattern()和getStatus()方法,在ArkTS侧调用drawPattern()来绘制内容,
    2. // 调用getStatus()来改变绘制内容
    3. void PluginRender::Export(napi_env env, napi_value exports)
    4. {
    5. // ...
    6. // 将接口函数注册为ArkTS侧接口drawPattern和getStatus
    7. napi_property_descriptor desc[] = {
    8. {"drawPattern", nullptr, PluginRender::NapiDrawPattern, nullptr, nullptr, nullptr, napi_default, nullptr},
    9. {"getStatus", nullptr, PluginRender::TestGetXComponentStatus, nullptr, nullptr, nullptr, napi_default,
    10. nullptr}};
    11. if (napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc) != napi_ok) {
    12. OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginRender", "Export: napi_define_properties failed");
    13. }
    14. }
  3. 注册XComponent事件回调,使用Node-API实现XComponent事件回调函数。

    (1) 定义surface创建成功,发生改变,销毁和XComponent的touch事件回调接口。

    收起
    自动换行
    深色代码主题
    复制
    1. // 在头文件中定义PluginRender类
    2. class PluginRender {
    3. public:
    4. explicit PluginRender(std::string& id);
    5. ~PluginRender() {
    6. if (eglCore_ != nullptr) {
    7. eglCore_->Release();
    8. delete eglCore_;
    9. eglCore_ = nullptr;
    10. }
    11. }
    12. static PluginRender* GetInstance(std::string& id);
    13. static void Release(std::string& id);
    14. static napi_value NapiDrawPattern(napi_env env, napi_callback_info info);
    15. static napi_value TestGetXComponentStatus(napi_value env, napi_callback_info info);
    16. void Export(napi_env env, napi_value exports);
    17. void OnSurfaceChanged(OH_NativeXComponent* component, void* window);
    18. void OnTouchEvent(OH_NativeXComponent* component, void* window);
    19. void OnMouseEvent(OH_NativeXComponent* component, void* window);
    20. void OnHoverEvent(OH_NativeXComponent* component, bool isHover);
    21. void OnFocusEvent(OH_NativeXComponent* component, void* window);
    22. void OnBlurEvent(OH_NativeXComponent* component, void* window);
    23. void OnKeyEvent(OH_NativeXComponent* component, void* window);
    24. void RegisterCallback(OH_NativeXComponent* NativeXComponent);
    25. public:
    26. static std::unordered_map<std::string, PluginRender*> instance_;
    27. EGLCore* eglCore_;
    28. std::string id_;
    29. static int32_t hasDraw_;
    30. static int32_t hasChangeColor_;
    31. private:
    32. OH_NativeXComponent_Callback renderCallback_;
    33. OH_NativeXComponent_MouseEvent_Callback mouseCallback_;
    34. };
    收起
    自动换行
    深色代码主题
    复制
    1. // 在源文件中实现PluginRender类中方法
    2. std::unordered_map<std::string, PluginRender *> PluginRender::instance_;
    3. int32_t PluginRender::hasDraw_ = 0;
    4. int32_t PluginRender::hasChangeColor_ = 0;
    5. PluginRender::PluginRender(std::string& id) {
    6. this->id_ = id;
    7. this->eglCore_ = new EGLCore();
    8. }
    9. PluginRender* PluginRender::GetInstance(std::string& id) {
    10. if (instance_.find(id) == instance_.end()) {
    11. PluginRender* instance = new PluginRender(id);
    12. instance_[id] = instance;
    13. return instance;
    14. } else {
    15. return instance_[id];
    16. }
    17. }
    18. // 定义一个函数OnSurfaceCreatedCB(),封装初始化环境与绘制背景
    19. void OnSurfaceCreatedCB(OH_NativeXComponent *component, void *window) {
    20. // ...
    21. // 获取XComponent的id,即ArkTS侧XComponent组件构造中的id参数
    22. char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {'\0'};
    23. uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
    24. if (OH_NativeXComponent_GetXComponentId(component, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
    25. OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Callback",
    26. "OnSurfaceCreatedCB: Unable to get XComponent id");
    27. return;
    28. }
    29. // 初始化环境与绘制背景
    30. std::string id(idStr);
    31. auto render = PluginRender::GetInstance(id);
    32. uint64_t width;
    33. uint64_t height;
    34. // 获取XComponent拥有的surface的大小
    35. int32_t xSize = OH_NativeXComponent_GetXComponentSize(component, window, &width, &height);
    36. if ((xSize == OH_NATIVEXCOMPONENT_RESULT_SUCCESS) && (render != nullptr)) {
    37. if (render->eglCore_->EglContextInit(window, width, height)) {
    38. render->eglCore_->Background();
    39. }
    40. }
    41. }
    42. // 定义一个函数OnSurfaceChangedCB()
    43. void OnSurfaceChangedCB(OH_NativeXComponent *component, void *window) {
    44. // ...
    45. // 获取XComponent的id
    46. char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {'\0'};
    47. uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
    48. if (OH_NativeXComponent_GetXComponentId(component, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
    49. OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Callback",
    50. "OnSurfaceChangedCB: Unable to get XComponent id");
    51. return;
    52. }
    53. std::string id(idStr);
    54. auto render = PluginRender::GetInstance(id);
    55. if (render != nullptr) {
    56. // 封装OnSurfaceChanged方法
    57. render->OnSurfaceChanged(component, window);
    58. }
    59. }
    60. // 定义一个函数OnSurfaceDestroyedCB(),将PluginRender类内释放资源的方法Release()封装在其中
    61. void OnSurfaceDestroyedCB(OH_NativeXComponent *component, void *window) {
    62. // ...
    63. // 获取XComponent的id
    64. char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {'\0'};
    65. uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
    66. if (OH_NativeXComponent_GetXComponentId(component, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
    67. OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Callback",
    68. "OnSurfaceDestroyedCB: Unable to get XComponent id");
    69. return;
    70. }
    71. std::string id(idStr);
    72. // 释放资源
    73. PluginRender::Release(id);
    74. }
    75. // 定义一个函数DispatchTouchEventCB(),响应触摸事件时触发该回调
    76. void DispatchTouchEventCB(OH_NativeXComponent *component, void *window) {
    77. // ...
    78. // 获取XComponent的id
    79. char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = { '\0' };
    80. uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
    81. if (OH_NativeXComponent_GetXComponentId(component, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
    82. OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Callback",
    83. "DispatchTouchEventCB: Unable to get XComponent id");
    84. return;
    85. }
    86. std::string id(idStr);
    87. PluginRender *render = PluginRender::GetInstance(id);
    88. if (render != nullptr) {
    89. // 封装OnTouchEvent方法
    90. render->OnTouchEvent(component, window);
    91. }
    92. }
    93. // 定义一个函数DispatchMouseEventCB(),响应鼠标事件时触发该回调
    94. void DispatchMouseEventCB(OH_NativeXComponent *component, void *window) {
    95. // ...
    96. int32_t ret;
    97. char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {};
    98. uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
    99. ret = OH_NativeXComponent_GetXComponentId(component, idStr, &idSize);
    100. if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
    101. return;
    102. }
    103. std::string id(idStr);
    104. auto render = PluginRender::GetInstance(id);
    105. if (render) {
    106. // 封装OnMouseEvent方法
    107. render->OnMouseEvent(component, window);
    108. }
    109. }
    110. // 定义一个函数DispatchHoverEventCB(),响应鼠标悬停事件时触发该回调
    111. void DispatchHoverEventCB(OH_NativeXComponent *component, bool isHover) {
    112. // ...
    113. int32_t ret;
    114. char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {};
    115. uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
    116. ret = OH_NativeXComponent_GetXComponentId(component, idStr, &idSize);
    117. if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
    118. return;
    119. }
    120. std::string id(idStr);
    121. auto render = PluginRender::GetInstance(id);
    122. if (render) {
    123. // 封装OnHoverEvent方法
    124. render->OnHoverEvent(component, isHover);
    125. }
    126. }
    127. // 定义一个函数OnFocusEventCB(),响应获焦事件时触发该回调
    128. void OnFocusEventCB(OH_NativeXComponent *component, void *window) {
    129. // ...
    130. int32_t ret;
    131. char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {};
    132. uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
    133. ret = OH_NativeXComponent_GetXComponentId(component, idStr, &idSize);
    134. if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
    135. return;
    136. }
    137. std::string id(idStr);
    138. auto render = PluginRender::GetInstance(id);
    139. if (render) {
    140. // 封装OnFocusEvent方法
    141. render->OnFocusEvent(component, window);
    142. }
    143. }
    144. // 定义一个函数OnBlurEventCB(),响应失去焦点事件时触发该回调
    145. void OnBlurEventCB(OH_NativeXComponent *component, void *window) {
    146. // ...
    147. int32_t ret;
    148. char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {};
    149. uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
    150. ret = OH_NativeXComponent_GetXComponentId(component, idStr, &idSize);
    151. if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
    152. return;
    153. }
    154. std::string id(idStr);
    155. auto render = PluginRender::GetInstance(id);
    156. if (render) {
    157. // 封装OnBlurEvent方法
    158. render->OnBlurEvent(component, window);
    159. }
    160. }
    161. // 定义一个函数OnKeyEventCB(),响应按键事件时触发该回调
    162. void OnKeyEventCB(OH_NativeXComponent *component, void *window) {
    163. // ...
    164. int32_t ret;
    165. char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {};
    166. uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
    167. ret = OH_NativeXComponent_GetXComponentId(component, idStr, &idSize);
    168. if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
    169. return;
    170. }
    171. std::string id(idStr);
    172. auto render = PluginRender::GetInstance(id);
    173. if (render) {
    174. // 封装OnKeyEvent方法
    175. render->OnKeyEvent(component, window);
    176. }
    177. }
    178. // 定义一个OnSurfaceChanged()方法
    179. void PluginRender::OnSurfaceChanged(OH_NativeXComponent* component, void* window) {
    180. char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = { '\0' };
    181. uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
    182. if (OH_NativeXComponent_GetXComponentId(component, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
    183. OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Callback", "OnSurfaceChanged: Unable to get XComponent id");
    184. return;
    185. }
    186. std::string id(idStr);
    187. PluginRender* render = PluginRender::GetInstance(id);
    188. double offsetX;
    189. double offsetY;
    190. // 获取XComponent持有的surface相对其父组件左顶点的偏移量
    191. OH_NativeXComponent_GetXComponentOffset(component, window, &offsetX, &offsetY);
    192. OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "OH_NativeXComponent_GetXComponentOffset",
    193. "offsetX = %{public}lf, offsetY = %{public}lf", offsetX, offsetY);
    194. uint64_t width;
    195. uint64_t height;
    196. OH_NativeXComponent_GetXComponentSize(component, window, &width, &height);
    197. if (render != nullptr) {
    198. render->eglCore_->UpdateSize(width, height);
    199. }
    200. }
    201. // 定义一个OnTouchEvent()方法
    202. void PluginRender::OnTouchEvent(OH_NativeXComponent* component, void* window) {
    203. char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = { '\0' };
    204. uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
    205. if (OH_NativeXComponent_GetXComponentId(component, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
    206. OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Callback",
    207. "OnTouchEvent: Unable to get XComponent id");
    208. return;
    209. }
    210. OH_NativeXComponent_TouchEvent touchEvent;
    211. // 获取由XComponent触发的触摸事件
    212. OH_NativeXComponent_GetTouchEvent(component, window, &touchEvent);
    213. // 获取XComponent触摸点相对于XComponent组件左边缘的坐标x和相对于XComponent组件上边缘的坐标y
    214. OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "OnTouchEvent", "touch info: x = %{public}lf, y = %{public}lf",
    215. touchEvent.x, touchEvent.y);
    216. // 获取XComponent触摸点相对于XComponent所在应用窗口左上角的x坐标和相对于XComponent所在应用窗口左上角的y坐标
    217. OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "OnTouchEvent",
    218. "touch info: screenX = %{public}lf, screenY = %{public}lf", touchEvent.screenX, touchEvent.screenY);
    219. std::string id(idStr);
    220. PluginRender* render = PluginRender::GetInstance(id);
    221. if (render != nullptr && touchEvent.type == OH_NativeXComponent_TouchEventType::OH_NATIVEXCOMPONENT_UP) {
    222. render->eglCore_->ChangeColor();
    223. hasChangeColor_ = 1;
    224. }
    225. float tiltX = 0.0f;
    226. float tiltY = 0.0f;
    227. OH_NativeXComponent_TouchPointToolType toolType =
    228. OH_NativeXComponent_TouchPointToolType::OH_NATIVEXCOMPONENT_TOOL_TYPE_UNKNOWN;
    229. // 获取XComponent触摸点的工具类型
    230. OH_NativeXComponent_GetTouchPointToolType(component, 0, &toolType);
    231. // 获取XComponent触摸点处相对X轴的倾斜角度
    232. OH_NativeXComponent_GetTouchPointTiltX(component, 0, &tiltX);
    233. // 获取XComponent触摸点处相对Y轴的倾斜角度
    234. OH_NativeXComponent_GetTouchPointTiltY(component, 0, &tiltY);
    235. OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "OnTouchEvent",
    236. "touch info: toolType = %{public}d, tiltX = %{public}lf, tiltY = %{public}lf", toolType, tiltX, tiltY);
    237. }
    238. // 定义一个OnMouseEvent()方法
    239. void PluginRender::OnMouseEvent(OH_NativeXComponent *component, void *window) {
    240. OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "PluginRender", "OnMouseEvent");
    241. OH_NativeXComponent_MouseEvent mouseEvent;
    242. // 获取由XComponent触发的鼠标事件
    243. int32_t ret = OH_NativeXComponent_GetMouseEvent(component, window, &mouseEvent);
    244. if (ret == OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
    245. OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "PluginRender",
    246. "MouseEvent Info: x = %{public}f, y = %{public}f, action = %{public}d, button = %{public}d",
    247. mouseEvent.x, mouseEvent.y, mouseEvent.action, mouseEvent.button);
    248. } else {
    249. OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginRender", "GetMouseEvent error");
    250. }
    251. }
    252. // 定义一个OnHoverEvent()方法
    253. void PluginRender::OnHoverEvent(OH_NativeXComponent* component, bool isHover) {
    254. OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "PluginRender", "OnHoverEvent isHover_ = %{public}d", isHover);
    255. }
    256. // 定义一个OnFocusEvent()方法
    257. void PluginRender::OnFocusEvent(OH_NativeXComponent* component, void* window) {
    258. OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "PluginRender", "OnFocusEvent");
    259. }
    260. // 定义一个OnBlurEvent()方法
    261. void PluginRender::OnBlurEvent(OH_NativeXComponent* component, void* window) {
    262. OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "PluginRender", "OnBlurEvent");
    263. }
    264. // 定义一个OnKeyEvent()方法
    265. void PluginRender::OnKeyEvent(OH_NativeXComponent *component, void *window) {
    266. OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "PluginRender", "OnKeyEvent");
    267. OH_NativeXComponent_KeyEvent *keyEvent = nullptr;
    268. // 获取由XComponent触发的按键事件
    269. if (OH_NativeXComponent_GetKeyEvent(component, &keyEvent) >= 0) {
    270. OH_NativeXComponent_KeyAction action;
    271. // 获取按键事件的动作
    272. OH_NativeXComponent_GetKeyEventAction(keyEvent, &action);
    273. OH_NativeXComponent_KeyCode code;
    274. // 获取按键事件的键码值
    275. OH_NativeXComponent_GetKeyEventCode(keyEvent, &code);
    276. OH_NativeXComponent_EventSourceType sourceType;
    277. // 获取按键事件的输入源类型
    278. OH_NativeXComponent_GetKeyEventSourceType(keyEvent, &sourceType);
    279. int64_t deviceId;
    280. // 获取按键事件的设备ID
    281. OH_NativeXComponent_GetKeyEventDeviceId(keyEvent, &deviceId);
    282. int64_t timeStamp;
    283. // 获取按键事件的时间戳
    284. OH_NativeXComponent_GetKeyEventTimestamp(keyEvent, &timeStamp);
    285. OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "PluginRender",
    286. "KeyEvent Info: action=%{public}d, code=%{public}d, sourceType=%{public}d, deviceId=%{public}ld, "
    287. "timeStamp=%{public}ld",
    288. action, code, sourceType, deviceId, timeStamp);
    289. } else {
    290. OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginRender", "GetKeyEvent error");
    291. }
    292. }

    (2) 注册XComponent事件回调函数,在XComponent事件触发时调用3.1步骤中定义的方法。

    收起
    自动换行
    深色代码主题
    复制
    1. void PluginRender::RegisterCallback(OH_NativeXComponent *NativeXComponent) {
    2. // 设置组件创建事件的回调函数,组件创建时触发相关操作,初始化环境与绘制背景
    3. renderCallback_.OnSurfaceCreated = OnSurfaceCreatedCB;
    4. // 设置组件改变事件的回调函数,组件改变时触发相关操作
    5. renderCallback_.OnSurfaceChanged = OnSurfaceChangedCB;
    6. // 设置组件销毁事件的回调函数,组件销毁时触发相关操作,释放申请的资源
    7. renderCallback_.OnSurfaceDestroyed = OnSurfaceDestroyedCB;
    8. // 设置触摸事件的回调函数,在触摸事件触发时调用Node-API接口函数,从而调用原C++方法
    9. renderCallback_.DispatchTouchEvent = DispatchTouchEventCB;
    10. // 将OH_NativeXComponent_Callback注册给NativeXComponent
    11. OH_NativeXComponent_RegisterCallback(NativeXComponent, &renderCallback_);
    12. // 设置鼠标事件的回调函数,在触摸事件触发时调用Node-API接口函数,从而调用原C++方法
    13. mouseCallback_.DispatchMouseEvent = DispatchMouseEventCB;
    14. // 设置鼠标悬停事件的回调函数,在触摸事件触发时调用Node-API接口函数,从而调用原C++方法
    15. mouseCallback_.DispatchHoverEvent = DispatchHoverEventCB;
    16. // 将OH_NativeXComponent_MouseEvent_Callback注册给NativeXComponent
    17. OH_NativeXComponent_RegisterMouseEventCallback(NativeXComponent, &mouseCallback_);
    18. // 将OnFocusEventCB方法注册给NativeXComponent
    19. OH_NativeXComponent_RegisterFocusEventCallback(NativeXComponent, OnFocusEventCB);
    20. // 将OnKeyEventCB方法注册给NativeXComponent
    21. OH_NativeXComponent_RegisterKeyEventCallback(NativeXComponent, OnKeyEventCB);
    22. // 将OnBlurEventCB方法注册给 NativeXComponent
    23. OH_NativeXComponent_RegisterBlurEventCallback(NativeXComponent, OnBlurEventCB);
    24. }

    (3) 定义NapiDrawPattern方法,暴露到ArkTS侧的drawPattern()方法会执行该方法。

    收起
    自动换行
    深色代码主题
    复制
    1. napi_value PluginRender::NapiDrawPattern(napi_env env, napi_callback_info info) {
    2. // ...
    3. // 获取环境变量参数
    4. napi_value thisArg;
    5. if (napi_get_cb_info(env, info, nullptr, nullptr, &thisArg, nullptr) != napi_ok) {
    6. OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginRender", "NapiDrawPattern: napi_get_cb_info fail");
    7. return nullptr;
    8. }
    9. // 获取环境变量中XComponent实例
    10. napi_value exportInstance;
    11. if (napi_get_named_property(env, thisArg, OH_NATIVE_XCOMPONENT_OBJ, &exportInstance) != napi_ok) {
    12. OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginRender",
    13. "NapiDrawPattern: napi_get_named_property fail");
    14. return nullptr;
    15. }
    16. // 通过napi_unwrap接口,获取XComponent的实例指针
    17. OH_NativeXComponent *NativeXComponent = nullptr;
    18. if (napi_unwrap(env, exportInstance, reinterpret_cast<void **>(&NativeXComponent)) != napi_ok) {
    19. OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginRender", "NapiDrawPattern: napi_unwrap fail");
    20. return nullptr;
    21. }
    22. // 获取XComponent实例的id
    23. char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = { '\0' };
    24. uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
    25. if (OH_NativeXComponent_GetXComponentId(NativeXComponent, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
    26. OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginRender",
    27. "NapiDrawPattern: Unable to get XComponent id");
    28. return nullptr;
    29. }
    30. std::string id(idStr);
    31. PluginRender *render = PluginRender::GetInstance(id);
    32. if (render) {
    33. // 调用绘制方法
    34. render->eglCore_->Draw();
    35. OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "PluginRender", "render->eglCore_->Draw() executed");
    36. }
    37. return nullptr;
    38. }

    (4) 定义TestGetXComponentStatus方法,暴露到ArkTS侧的getStatus()方法会执行该方法。

    收起
    自动换行
    深色代码主题
    复制
    1. napi_value PluginRender::TestGetXComponentStatus(napi_env env, napi_callback_info info) {
    2. napi_value hasDraw;
    3. napi_value hasChangeColor;
    4. napi_create_int32(env, hasDraw_, &(hasDraw));
    5. napi_create_int32(env, hasChangeColor_, &(hasChangeColor));
    6. napi_value obj;
    7. napi_create_object(env, &obj);
    8. napi_set_named_property(env, obj, "hasDraw", hasDraw);
    9. napi_set_named_property(env, obj, "hasChangeColor", hasChangeColor);
    10. return obj;
    11. }
  4. 初始化环境,包括初始化可用的EGLDisplay、确定可用的surface配置、创建渲染区域surface、创建并关联上下文等。

    收起
    自动换行
    深色代码主题
    复制
    1. void EGLCore::UpdateSize(int width, int height) {
    2. // width_和height_在头文件中定义
    3. width_ = width;
    4. height_ = height;
    5. }
    6. bool EGLCore::EglContextInit(void *window, int width, int height) {
    7. // ...
    8. UpdateSize(width, height);
    9. eglWindow_ = static_cast<EGLNativeWindowType>(window);
    10. // 初始化display
    11. eglDisplay_ = eglGetDisplay(EGL_DEFAULT_DISPLAY);
    12. if (eglDisplay_ == EGL_NO_DISPLAY) {
    13. OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "eglGetDisplay: unable to get EGL display");
    14. return false;
    15. }
    16. // 初始化EGL
    17. EGLint majorVersion;
    18. EGLint minorVersion;
    19. if (!eglInitialize(eglDisplay_, &majorVersion, &minorVersion)) {
    20. OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore",
    21. "eglInitialize: unable to get initialize EGL display");
    22. return false;
    23. }
    24. // 选择配置
    25. const EGLint maxConfigSize = 1;
    26. EGLint numConfigs;
    27. if (!eglChooseConfig(eglDisplay_, ATTRIB_LIST, &eglConfig_, maxConfigSize, &numConfigs)) {
    28. OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "eglChooseConfig: unable to choose configs");
    29. return false;
    30. }
    31. // 创建环境
    32. return CreateEnvironment();
    33. }
    收起
    自动换行
    深色代码主题
    复制
    1. bool EGLCore::CreateEnvironment() {
    2. // ...
    3. // 创建surface
    4. eglSurface_ = eglCreateWindowSurface(eglDisplay_, eglConfig_, eglWindow_, NULL);
    5. // ...
    6. // 创建context
    7. eglContext_ = eglCreateContext(eglDisplay_, eglConfig_, EGL_NO_CONTEXT, CONTEXT_ATTRIBS);
    8. if (!eglMakeCurrent(eglDisplay_, eglSurface_, eglSurface_, eglContext_)) {
    9. OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "eglMakeCurrent failed");
    10. return false;
    11. }
    12. // 创建program
    13. program_ = CreateProgram(VERTEX_SHADER, FRAGMENT_SHADER);
    14. if (program_ == PROGRAM_ERROR) {
    15. OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "CreateProgram: unable to create program");
    16. return false;
    17. }
    18. return true;
    19. }
    20. GLuint EGLCore::CreateProgram(const char* vertexShader, const char* fragShader) {
    21. if ((vertexShader == nullptr) || (fragShader == nullptr)) {
    22. OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore",
    23. "createProgram: vertexShader or fragShader is null");
    24. return PROGRAM_ERROR;
    25. }
    26. GLuint vertex = LoadShader(GL_VERTEX_SHADER, vertexShader);
    27. if (vertex == PROGRAM_ERROR) {
    28. OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "createProgram vertex error");
    29. return PROGRAM_ERROR;
    30. }
    31. GLuint fragment = LoadShader(GL_FRAGMENT_SHADER, fragShader);
    32. if (fragment == PROGRAM_ERROR) {
    33. OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "createProgram fragment error");
    34. return PROGRAM_ERROR;
    35. }
    36. GLuint program = glCreateProgram();
    37. if (program == PROGRAM_ERROR) {
    38. OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "createProgram program error");
    39. glDeleteShader(vertex);
    40. glDeleteShader(fragment);
    41. return PROGRAM_ERROR;
    42. }
    43. // The gl function has no return value.
    44. glAttachShader(program, vertex);
    45. glAttachShader(program, fragment);
    46. glLinkProgram(program);
    47. GLint linked;
    48. glGetProgramiv(program, GL_LINK_STATUS, &linked);
    49. if (linked != 0) {
    50. glDeleteShader(vertex);
    51. glDeleteShader(fragment);
    52. return program;
    53. }
    54. OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "createProgram linked error");
    55. GLint infoLen = 0;
    56. glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLen);
    57. if (infoLen > 1) {
    58. char* infoLog = (char*)malloc(sizeof(char) * (infoLen + 1));
    59. memset(infoLog, 0, infoLen + 1);
    60. glGetProgramInfoLog(program, infoLen, nullptr, infoLog);
    61. OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "glLinkProgram error = %s", infoLog);
    62. free(infoLog);
    63. infoLog = nullptr;
    64. }
    65. glDeleteShader(vertex);
    66. glDeleteShader(fragment);
    67. glDeleteProgram(program);
    68. return PROGRAM_ERROR;
    69. }
    70. GLuint EGLCore::LoadShader(GLenum type, const char* shaderSrc) {
    71. if ((type <= 0) || (shaderSrc == nullptr)) {
    72. OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "glCreateShader type or shaderSrc error");
    73. return PROGRAM_ERROR;
    74. }
    75. GLuint shader = glCreateShader(type);
    76. if (shader == 0) {
    77. OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "glCreateShader unable to load shader");
    78. return PROGRAM_ERROR;
    79. }
    80. // The gl function has no return value.
    81. glShaderSource(shader, 1, &shaderSrc, nullptr);
    82. glCompileShader(shader);
    83. GLint compiled;
    84. glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
    85. if (compiled != 0) {
    86. return shader;
    87. }
    88. GLint infoLen = 0;
    89. glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
    90. if (infoLen <= 1) {
    91. glDeleteShader(shader);
    92. return PROGRAM_ERROR;
    93. }
    94. char *infoLog = (char*)malloc(sizeof(char) * (infoLen + 1));
    95. if (infoLog != nullptr) {
    96. memset(infoLog, 0, infoLen + 1);
    97. glGetShaderInfoLog(shader, infoLen, nullptr, infoLog);
    98. OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "glCompileShader error = %s", infoLog);
    99. free(infoLog);
    100. infoLog = nullptr;
    101. }
    102. glDeleteShader(shader);
    103. return PROGRAM_ERROR;
    104. }
  5. 渲染功能实现。

    (1) 绘制背景。

    收起
    自动换行
    深色代码主题
    复制
    1. // ...
    2. // 绘制背景颜色 #f4f4f4
    3. const GLfloat BACKGROUND_COLOR[] = { 244.0f / 255, 244.0f / 255, 244.0f / 255, 1.0f };
    4. // 绘制图案颜色
    5. const GLfloat DRAW_COLOR[] = {126.0f / 255, 143.0f / 255, 251.0f / 255, 1.0f};
    6. // 绘制图案改变后的颜色
    7. const GLfloat CHANGE_COLOR[] = {146.0f / 255, 214.0f / 255, 204.0f / 255, 1.0f};
    8. // 绘制背景顶点
    9. const GLfloat BACKGROUND_RECTANGLE_VERTICES[] = {
    10. -1.0f, 1.0f,
    11. 1.0f, 1.0f,
    12. 1.0f, -1.0f,
    13. -1.0f, -1.0f
    14. };
    15. // ...
    收起
    自动换行
    深色代码主题
    复制
    1. // 绘制背景颜色
    2. void EGLCore::Background() {
    3. GLint position = PrepareDraw();
    4. if (position == POSITION_ERROR) {
    5. OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Background get position failed");
    6. return;
    7. }
    8. if (!ExecuteDraw(position, BACKGROUND_COLOR, BACKGROUND_RECTANGLE_VERTICES,
    9. sizeof(BACKGROUND_RECTANGLE_VERTICES))) {
    10. OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Background execute draw failed");
    11. return;
    12. }
    13. if (!FinishDraw()) {
    14. OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Background FinishDraw failed");
    15. return;
    16. }
    17. }
    18. // 绘前准备,获取position,创建成功时position值从0开始
    19. GLint EGLCore::PrepareDraw() {
    20. if ((eglDisplay_ == nullptr) || (eglSurface_ == nullptr) || (eglContext_ == nullptr) ||
    21. (!eglMakeCurrent(eglDisplay_, eglSurface_, eglSurface_, eglContext_))) {
    22. OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "PrepareDraw: param error");
    23. return POSITION_ERROR;
    24. }
    25. glViewport(DEFAULT_X_POSITION, DEFAULT_Y_POSITION, width_, height_);
    26. glClearColor(GL_RED_DEFAULT, GL_GREEN_DEFAULT, GL_BLUE_DEFAULT, GL_ALPHA_DEFAULT);
    27. glClear(GL_COLOR_BUFFER_BIT);
    28. glUseProgram(program_);
    29. return glGetAttribLocation(program_, POSITION_NAME);
    30. }
    31. // 依据传入参数在指定区域绘制指定颜色
    32. bool EGLCore::ExecuteDraw(GLint position, const GLfloat *color, const GLfloat shapeVertices[], unsigned long vertSize) {
    33. if ((position > 0) || (color == nullptr) || (vertSize / sizeof(shapeVertices[0]) != SHAPE_VERTICES_SIZE)) {
    34. OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "ExecuteDraw: param error");
    35. return false;
    36. }
    37. glVertexAttribPointer(position, POINTER_SIZE, GL_FLOAT, GL_FALSE, 0, shapeVertices);
    38. glEnableVertexAttribArray(position);
    39. glVertexAttrib4fv(1, color);
    40. glDrawArrays(GL_TRIANGLE_FAN, 0, TRIANGLE_FAN_SIZE);
    41. glDisableVertexAttribArray(position);
    42. return true;
    43. }
    44. // 结束绘制操作
    45. bool EGLCore::FinishDraw() {
    46. // 强制刷新缓冲
    47. glFlush();
    48. glFinish();
    49. return eglSwapBuffers(eglDisplay_, eglSurface_);
    50. }

    (2) 绘制图形。

    收起
    自动换行
    深色代码主题
    复制
    1. void EGLCore::Draw() {
    2. flag_ = false;
    3. OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "EGLCore", "Draw");
    4. GLint position = PrepareDraw();
    5. if (position == POSITION_ERROR) {
    6. OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw get position failed");
    7. return;
    8. }
    9. // 绘制背景
    10. if (!ExecuteDraw(position, BACKGROUND_COLOR, BACKGROUND_RECTANGLE_VERTICES,
    11. sizeof(BACKGROUND_RECTANGLE_VERTICES))) {
    12. OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw execute draw background failed");
    13. return;
    14. }
    15. // 将五角星分为五个四边形,计算其中一个四边形的四个顶点
    16. GLfloat rotateX = 0;
    17. GLfloat rotateY = FIFTY_PERCENT * height_;
    18. GLfloat centerX = 0;
    19. GLfloat centerY = -rotateY * (M_PI / 180 * 54) * (M_PI / 180 * 18);
    20. GLfloat leftX = -rotateY * (M_PI / 180 * 18);
    21. GLfloat leftY = 0;
    22. GLfloat rightX = rotateY * (M_PI / 180 * 18);
    23. GLfloat rightY = 0;
    24. // 确定绘制四边形的顶点,使用绘制区域的百分比表示
    25. const GLfloat shapeVertices[] = {
    26. centerX / width_, centerY / height_,
    27. leftX / width_, leftY / height_,
    28. rotateX / width_, rotateY / height_,
    29. rightX / width_, rightY / height_
    30. };
    31. if (!ExecuteDrawStar(position, DRAW_COLOR, shapeVertices, sizeof(shapeVertices))) {
    32. OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw execute draw star failed");
    33. return;
    34. }
    35. GLfloat rad = M_PI / 180 * 72;
    36. for (int i = 0; i < 4; ++i)
    37. {
    38. // 旋转得其他四个四边形的顶点
    39. rotate2d(centerX, centerY, &rotateX, &rotateY,rad);
    40. rotate2d(centerX, centerY, &leftX, &leftY,rad);
    41. rotate2d(centerX, centerY, &rightX, &rightY,rad);
    42. // 确定绘制四边形的顶点,使用绘制区域的百分比表示
    43. const GLfloat shapeVertices[] = {
    44. centerX / width_, centerY / height_,
    45. leftX / width_, leftY / height_,
    46. rotateX / width_, rotateY / height_,
    47. rightX / width_, rightY / height_
    48. };
    49. // 绘制图形
    50. if (!ExecuteDrawStar(position, DRAW_COLOR, shapeVertices, sizeof(shapeVertices))) {
    51. OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw execute draw star failed");
    52. return;
    53. }
    54. }
    55. // 结束绘制
    56. if (!FinishDraw()) {
    57. OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw FinishDraw failed");
    58. return;
    59. }
    60. flag_ = true;
    61. }

    (3) 改变颜色,重新画一个大小相同颜色不同的图形,与原图形替换,达到改变颜色的效果。

    收起
    自动换行
    深色代码主题
    复制
    1. void EGLCore::ChangeColor() {
    2. if (!flag_) {
    3. return;
    4. }
    5. OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "EGLCore", "ChangeColor");
    6. GLint position = PrepareDraw();
    7. if (position == POSITION_ERROR) {
    8. OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "ChangeColor get position failed");
    9. return;
    10. }
    11. // 绘制背景
    12. if (!ExecuteDraw(position, BACKGROUND_COLOR, BACKGROUND_RECTANGLE_VERTICES,
    13. sizeof(BACKGROUND_RECTANGLE_VERTICES))) {
    14. OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "ChangeColor execute draw background failed");
    15. return;
    16. }
    17. // 确定绘制四边形的顶点,使用绘制区域的百分比表示
    18. GLfloat rotateX = 0;
    19. GLfloat rotateY = FIFTY_PERCENT * height_;
    20. GLfloat centerX = 0;
    21. GLfloat centerY = -rotateY * (M_PI / 180 * 54) * (M_PI / 180 * 18);
    22. GLfloat leftX = -rotateY * (M_PI / 180 * 18);
    23. GLfloat leftY = 0;
    24. GLfloat rightX = rotateY * (M_PI / 180 * 18);
    25. GLfloat rightY = 0;
    26. // 确定绘制四边形的顶点,使用绘制区域的百分比表示
    27. const GLfloat shapeVertices[] = {
    28. centerX / width_, centerY / height_,
    29. leftX / width_, leftY / height_,
    30. rotateX / width_, rotateY / height_,
    31. rightX / width_, rightY / height_
    32. };
    33. // 使用新的颜色绘制
    34. if (!ExecuteDrawNewStar(position, CHANGE_COLOR, shapeVertices, sizeof(shapeVertices))) {
    35. OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw execute draw star failed");
    36. return;
    37. }
    38. GLfloat rad = M_PI / 180 * 72;
    39. for (int i = 0; i < 4; ++i) {
    40. // 旋转得其他四个四边形的顶点
    41. rotate2d(centerX, centerY, &rotateX, &rotateY,rad);
    42. rotate2d(centerX, centerY, &leftX, &leftY,rad);
    43. rotate2d(centerX, centerY, &rightX, &rightY,rad);
    44. // 确定绘制四边形的顶点,使用绘制区域的百分比表示
    45. const GLfloat shapeVertices[] = {
    46. centerX / width_, centerY / height_,
    47. leftX / width_, leftY / height_,
    48. rotateX / width_, rotateY / height_,
    49. rightX / width_, rightY / height_
    50. };
    51. // 使用新的颜色绘制
    52. if (!ExecuteDrawNewStar(position, CHANGE_COLOR, shapeVertices, sizeof(shapeVertices))) {
    53. OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw execute draw star failed");
    54. return;
    55. }
    56. }
    57. // 结束绘制
    58. if (!FinishDraw()) {
    59. OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "ChangeColor FinishDraw failed");
    60. }
    61. }
    62. bool EGLCore::ExecuteDrawNewStar(
    63. GLint position, const GLfloat* color, const GLfloat shapeVertices[], unsigned long vertSize) {
    64. if ((position > 0) || (color == nullptr) || (vertSize / sizeof(shapeVertices[0])) != SHAPE_VERTICES_SIZE) {
    65. OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "ExecuteDraw: param error");
    66. return false;
    67. }
    68. // The gl function has no return value.
    69. glVertexAttribPointer(position, POINTER_SIZE, GL_FLOAT, GL_FALSE, 0, shapeVertices);
    70. glEnableVertexAttribArray(position);
    71. glVertexAttrib4fv(1, color);
    72. glDrawArrays(GL_TRIANGLE_FAN, 0, TRIANGLE_FAN_SIZE);
    73. glDisableVertexAttribArray(position);
    74. return true;
    75. }
  6. 释放相关资源。

    (1) EGLCore类下创建Release()方法,释放初始化环境时申请的资源,包含窗口display、渲染区域surface、环境上下文context等。

    收起
    自动换行
    深色代码主题
    复制
    1. void EGLCore::Release() {
    2. // 释放surface
    3. if ((eglDisplay_ == nullptr) || (eglSurface_ == nullptr) || (!eglDestroySurface(eglDisplay_, eglSurface_))) {
    4. OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Release eglDestroySurface failed");
    5. }
    6. // 释放context
    7. if ((eglDisplay_ == nullptr) || (eglContext_ == nullptr) || (!eglDestroyContext(eglDisplay_, eglContext_))) {
    8. OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Release eglDestroyContext failed");
    9. }
    10. // 释放display
    11. if ((eglDisplay_ == nullptr) || (!eglTerminate(eglDisplay_))) {
    12. OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Release eglTerminate failed");
    13. }
    14. }

    (2) PluginRender类添加Release()方法,释放EGLCore实例及PluginRender实例。

    收起
    自动换行
    深色代码主题
    复制
    1. void PluginRender::Release(std::string &id) {
    2. PluginRender *render = PluginRender::GetInstance(id);
    3. if (render != nullptr) {
    4. render->eglCore_->Release();
    5. delete render->eglCore_;
    6. render->eglCore_ = nullptr;
    7. instance_.erase(instance_.find(id));
    8. }
    9. }
  7. CMakeLists,使用CMake工具链将C++源代码编译成动态链接库文件。

    收起
    自动换行
    深色代码主题
    复制
    1. # 设置CMake最小版本
    2. cmake_minimum_required(VERSION 3.4.1)
    3. # 项目名称
    4. project(XComponent)
    5. set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
    6. add_definitions(-DOHOS_PLATFORM)
    7. # 设置头文件搜索目录
    8. include_directories(
    9. ${NATIVERENDER_ROOT_PATH}
    10. ${NATIVERENDER_ROOT_PATH}/include
    11. )
    12. # 添加名为nativerender的动态库,库文件名为libnativerender.so,添加cpp文件
    13. add_library(nativerender SHARED
    14. render/egl_core.cpp
    15. render/plugin_render.cpp
    16. manager/plugin_manager.cpp
    17. napi_init.cpp
    18. )
    19. find_library(
    20. EGL-lib
    21. EGL
    22. )
    23. find_library(
    24. GLES-lib
    25. GLESv3
    26. )
    27. find_library(
    28. hilog-lib
    29. hilog_ndk.z
    30. )
    31. find_library(
    32. libace-lib
    33. ace_ndk.z
    34. )
    35. find_library(
    36. libnapi-lib
    37. ace_napi.z
    38. )
    39. find_library(
    40. libuv-lib
    41. uv
    42. )
    43. # 添加构建需要链接的库
    44. target_link_libraries(nativerender PUBLIC
    45. ${EGL-lib} ${GLES-lib} ${hilog-lib} ${libace-lib} ${libnapi-lib} ${libuv-lib})

ArkTS XComponent场景

与Native XComponent不同,ArkTS XComponent不再需要libraryname参数。通过在ArkTS侧获取SurfaceId,布局信息、生命周期回调、触摸、鼠标、按键等事件回调等均在ArkTS侧触发,按需传递到Native侧进行处理。主要开发场景如下:

  • 基于ArkTS侧获取的SurfaceId,在Native侧调用OH_NativeWindow_CreateNativeWindowFromSurfaceId接口创建出NativeWindow实例。
  • 利用NativeWindow和EGL接口开发自定义绘制内容以及申请和提交Buffer到图形队列。
  • ArkTS侧获取生命周期、事件等信息传递到Native侧处理。

接口说明

ArkTS侧的XComponentController

展开
接口名 描述
getXComponentSurfaceId(): string 获取XComponent对应Surface的ID。
onSurfaceCreated(surfaceId: string): void 当XComponent持有的Surface创建后进行该回调。
onSurfaceChanged(surfaceId: string, rect: SurfaceRect): void 当XComponent持有的Surface大小改变后(包括首次创建时的大小改变)进行该回调。
onSurfaceDestroyed(surfaceId: string): void 当XComponent持有的Surface销毁后进行该回调。

Native侧

展开
接口名 描述
int32_t OH_NativeWindow_CreateNativeWindowFromSurfaceId (uint64_t surfaceId, OHNativeWindow **window ) 通过surfaceId创建对应的OHNativeWindow。
void OH_NativeWindow_DestroyNativeWindow (OHNativeWindow* window) 将OHNativeWindow对象的引用计数减1,当引用计数为0的时候,该OHNativeWindow对象会被析构掉。

开发步骤

以下步骤以SURFACE类型为例,描述了如何使用XComponent组件在ArkTS侧传入SurfaceId,在native侧创建NativeWindow实例,然后创建EGL/GLES环境,实现在主页面绘制图形,并可以改变图形的颜色。

  1. 在界面中定义XComponent。

    收起
    自动换行
    深色代码主题
    复制
    1. // 函数声明,在cpp/types/libnativerender/Index.d.ts中定义
    2. type XComponentContextStatus = {
    3. hasDraw: boolean,
    4. hasChangeColor: boolean,
    5. };
    6. export const SetSurfaceId: (id: BigInt) => any;
    7. export const ChangeSurface: (id: BigInt, w: number, h: number) =>any;
    8. export const DrawPattern: (id: BigInt) => any;
    9. export const GetXComponentStatus: (id: BigInt) => XComponentContextStatus
    10. export const ChangeColor: (id: BigInt) => any;
    11. export const DestroySurface: (id: BigInt) => any;
    收起
    自动换行
    深色代码主题
    复制
    1. import nativeRender from 'libnativerender.so'
    2. // 重写XComponentController,设置生命周期回调
    3. class MyXComponentController extends XComponentController {
    4. onSurfaceCreated(surfaceId: string): void {
    5. console.log(`onSurfaceCreated surfaceId: ${surfaceId}`)
    6. nativeRender.SetSurfaceId(BigInt(surfaceId));
    7. }
    8. onSurfaceChanged(surfaceId: string, rect: SurfaceRect): void {
    9. console.log(`onSurfaceChanged surfaceId: ${surfaceId}, rect: ${JSON.stringify(rect)}}`)
    10. // 在onSurfaceChanged中调用ChangeSurface绘制内容
    11. nativeRender.ChangeSurface(BigInt(surfaceId), rect.surfaceWidth, rect.surfaceHeight)
    12. }
    13. onSurfaceDestroyed(surfaceId: string): void {
    14. console.log(`onSurfaceDestroyed surfaceId: ${surfaceId}`)
    15. nativeRender.DestroySurface(BigInt(surfaceId))
    16. }
    17. }
    18. @Entry
    19. @Component
    20. struct Index {
    21. @State currentStatus: string = "index";
    22. xComponentController: XComponentController = new MyXComponentController();
    23. build() {
    24. Column() {
    25. //...
    26. //在xxx.ets 中定义 XComponent
    27. Column({ space: 10 }) {
    28. XComponent({
    29. type: XComponentType.SURFACE,
    30. controller: this.xComponentController
    31. })
    32. Text(this.currentStatus)
    33. .fontSize('24fp')
    34. .fontWeight(500)
    35. }
    36. .onClick(() => {
    37. let surfaceId = this.xComponentController.getXComponentSurfaceId()
    38. nativeRender.ChangeColor(BigInt(surfaceId))
    39. let hasChangeColor: boolean = false;
    40. if (nativeRender.GetXComponentStatus(BigInt(surfaceId))) {
    41. hasChangeColor = nativeRender.GetXComponentStatus(BigInt(surfaceId)).hasChangeColor;
    42. }
    43. if (hasChangeColor) {
    44. this.currentStatus = "change color";
    45. }
    46. })
    47. //...
    48. Row() {
    49. Button('Draw Star')
    50. .fontSize('16fp')
    51. .fontWeight(500)
    52. .margin({ bottom: 24 })
    53. .onClick(() => {
    54. let surfaceId = this.xComponentController.getXComponentSurfaceId()
    55. nativeRender.DrawPattern(BigInt(surfaceId))
    56. let hasDraw: boolean = false;
    57. if (nativeRender.GetXComponentStatus(BigInt(surfaceId))) {
    58. hasDraw = nativeRender.GetXComponentStatus(BigInt(surfaceId)).hasDraw;
    59. }
    60. if (hasDraw) {
    61. this.currentStatus = "draw star"
    62. }
    63. })
    64. .width('53.6%')
    65. .height(40)
    66. }
    67. .width('100%')
    68. .justifyContent(FlexAlign.Center)
    69. .alignItems(VerticalAlign.Bottom)
    70. .layoutWeight(1)
    71. }
    72. .width('100%')
    73. .height('100%')
    74. }
    75. }
  2. Node-API模块注册,具体使用请参考Native API在应用工程中的使用指导

    收起
    自动换行
    深色代码主题
    复制
    1. #include <hilog/log.h>
    2. #include "common/common.h"
    3. #include "manager/plugin_manager.h"
    4. namespace NativeXComponentSample {
    5. // 在napi_init.cpp文件中,Init方法注册接口函数,从而将封装的C++方法传递出来,供ArkTS侧调用
    6. EXTERN_C_START
    7. static napi_value Init(napi_env env, napi_value exports) {
    8. // ...
    9. // 向ArkTS侧暴露接口SetSurfaceId(),ChangeSurface(),DestroySurface(),
    10. // DrawPattern(),ChangeColor(),GetXComponentStatus()
    11. napi_property_descriptor desc[] = {
    12. {"SetSurfaceId", nullptr, PluginManager::SetSurfaceId, nullptr, nullptr, nullptr, napi_default, nullptr},
    13. {"ChangeSurface", nullptr, PluginManager::ChangeSurface, nullptr, nullptr, nullptr, napi_default, nullptr},
    14. {"DestroySurface", nullptr, PluginManager::DestroySurface, nullptr, nullptr, nullptr, napi_default, nullptr},
    15. {"DrawPattern", nullptr, PluginManager::DrawPattern, nullptr, nullptr, nullptr, napi_default, nullptr},
    16. {"ChangeColor", nullptr, PluginManager::ChangeColor, nullptr, nullptr, nullptr, napi_default, nullptr},
    17. {"GetXComponentStatus", nullptr, PluginManager::GetXComponentStatus, nullptr, nullptr, nullptr, napi_default,
    18. nullptr}};
    19. if (napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc) != napi_ok) {
    20. OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Init", "napi_define_properties failed");
    21. return nullptr;
    22. }
    23. return exports;
    24. }
    25. EXTERN_C_END
    26. // 编写接口的描述信息,根据实际需要可以修改对应参数
    27. static napi_module nativerenderModule = {.nm_version = 1,
    28. .nm_flags = 0,
    29. .nm_filename = nullptr,
    30. // 入口函数
    31. .nm_register_func = Init,
    32. // 模块名称
    33. .nm_modname = "nativerender",
    34. .nm_priv = ((void *)0),
    35. .reserved = {0}};
    36. } // namespace NativeXComponentSample
    37. // __attribute__((constructor))修饰的方法由系统自动调用,使用Node-API接口napi_module_register()传入模块描述信息进行模块注册
    38. extern "C" __attribute__((constructor)) void RegisterModule(void) {
    39. napi_module_register(&NativeXComponentSample::nativerenderModule);
    40. }
  3. 上述注册的六个函数在native侧具体实现。

    收起
    自动换行
    深色代码主题
    复制
    1. // PluginManager类定义
    2. class PluginManager {
    3. public:
    4. ~PluginManager();
    5. static PluginRender *GetPluginRender(int64_t &id);
    6. static napi_value ChangeColor(napi_env env, napi_callback_info info);
    7. static napi_value DrawPattern(napi_env env, napi_callback_info info);
    8. static napi_value SetSurfaceId(napi_env env, napi_callback_info info);
    9. static napi_value ChangeSurface(napi_env env, napi_callback_info info);
    10. static napi_value DestroySurface(napi_env env, napi_callback_info info);
    11. static napi_value GetXComponentStatus(napi_env env, napi_callback_info info);
    12. public:
    13. static std::unordered_map<int64_t, PluginRender *> pluginRenderMap_;
    14. static std::unordered_map<int64_t, OHNativeWindow *> windowMap_;
    15. };
    16. // 解析从ArkTS侧传入的surfaceId,此处surfaceId是一个64位int值
    17. int64_t ParseId(napi_env env, napi_callback_info info) {
    18. if ((env == nullptr) || (info == nullptr)) {
    19. OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ParseId", "env or info is null");
    20. return -1;
    21. }
    22. size_t argc = 1;
    23. napi_value args[1] = {nullptr};
    24. if (napi_ok != napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)) {
    25. OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ParseId", "GetContext napi_get_cb_info failed");
    26. return -1;
    27. }
    28. int64_t value = 0;
    29. bool lossless = true;
    30. if (napi_ok != napi_get_value_bigint_int64(env, args[0], &value, &lossless)) {
    31. OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ParseId", "Get value failed");
    32. return -1;
    33. }
    34. return value;
    35. }
    36. // 设置SurfaceId,基于SurfaceId完成对NativeWindow的初始化
    37. napi_value PluginManager::SetSurfaceId(napi_env env, napi_callback_info info) {
    38. int64_t surfaceId = ParseId(env, info);
    39. OHNativeWindow *nativeWindow;
    40. PluginRender *pluginRender;
    41. if (windowMap_.find(surfaceId) == windowMap_.end()) {
    42. OH_NativeWindow_CreateNativeWindowFromSurfaceId(surfaceId, &nativeWindow);
    43. windowMap_[surfaceId] = nativeWindow;
    44. }
    45. if (pluginRenderMap_.find(surfaceId) == pluginRenderMap_.end()) {
    46. pluginRender = new PluginRender(surfaceId);
    47. pluginRenderMap_[surfaceId] = pluginRender;
    48. }
    49. pluginRender->InitNativeWindow(nativeWindow);
    50. return nullptr;
    51. }
    52. void PluginRender::InitNativeWindow(OHNativeWindow *window) {
    53. eglCore_->EglContextInit(window); // 参考Native XComponent场景 EglContextInit的实现
    54. }
    55. // 根据传入的surfaceId、width、height实现surface大小的变动
    56. napi_value PluginManager::ChangeSurface(napi_env env, napi_callback_info info) {
    57. if ((env == nullptr) || (info == nullptr)) {
    58. OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager",
    59. "ChangeSurface: OnLoad env or info is null");
    60. return nullptr;
    61. }
    62. int64_t surfaceId = 0;
    63. size_t argc = 3;
    64. napi_value args[3] = {nullptr};
    65. if (napi_ok != napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)) {
    66. OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager",
    67. "ChangeSurface: GetContext napi_get_cb_info failed");
    68. return nullptr;
    69. }
    70. bool lossless = true;
    71. int index = 0;
    72. if (napi_ok != napi_get_value_bigint_int64(env, args[index++], &surfaceId, &lossless)) {
    73. OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "ChangeSurface: Get value failed");
    74. return nullptr;
    75. }
    76. double width;
    77. if (napi_ok != napi_get_value_double(env, args[index++], &width)) {
    78. OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "ChangeSurface: Get width failed");
    79. return nullptr;
    80. }
    81. double height;
    82. if (napi_ok != napi_get_value_double(env, args[index++], &height)) {
    83. OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "ChangeSurface: Get height failed");
    84. return nullptr;
    85. }
    86. auto pluginRender = GetPluginRender(surfaceId);
    87. if (pluginRender == nullptr) {
    88. OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "ChangeSurface: Get pluginRender failed");
    89. return nullptr;
    90. }
    91. pluginRender->UpdateNativeWindowSize(width, height);
    92. return nullptr;
    93. }
    94. void PluginRender::UpdateNativeWindowSize(int width, int height) {
    95. eglCore_->UpdateSize(width, height); // 参考Native XComponent场景 UpdateSize的实现
    96. if (!hasChangeColor_ && !hasDraw_) {
    97. eglCore_->Background(); // 参考Native XComponent场景 Background的实现
    98. }
    99. }
    100. // 销毁surface
    101. napi_value PluginManager::DestroySurface(napi_env env, napi_callback_info info) {
    102. int64_t surfaceId = ParseId(env, info);
    103. auto pluginRenderMapIter = pluginRenderMap_.find(surfaceId);
    104. if (pluginRenderMapIter != pluginRenderMap_.end()) {
    105. delete pluginRenderMapIter->second;
    106. pluginRenderMap_.erase(pluginRenderMapIter);
    107. }
    108. auto windowMapIter = windowMap_.find(surfaceId);
    109. if (windowMapIter != windowMap_.end()) {
    110. OH_NativeWindow_DestroyNativeWindow(windowMapIter->second);
    111. windowMap_.erase(windowMapIter);
    112. }
    113. return nullptr;
    114. }
    115. // 实现EGL绘画逻辑
    116. napi_value PluginManager::DrawPattern(napi_env env, napi_callback_info info) {
    117. int64_t surfaceId = ParseId(env, info);
    118. auto pluginRender = GetPluginRender(surfaceId);
    119. if (pluginRender == nullptr) {
    120. OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "DrawPattern: Get pluginRender failed");
    121. return nullptr;
    122. }
    123. pluginRender->DrawPattern();
    124. return nullptr;
    125. }
    126. PluginRender *PluginManager::GetPluginRender(int64_t &id) {
    127. if (pluginRenderMap_.find(id) != pluginRenderMap_.end()) {
    128. return pluginRenderMap_[id];
    129. }
    130. return nullptr;
    131. }
    132. void PluginRender::DrawPattern() {
    133. eglCore_->Draw(hasDraw_); // 参考Native XComponent场景 Draw实现
    134. }
    135. // 实现改变绘制图形颜色的功能
    136. napi_value PluginManager::ChangeColor(napi_env env, napi_callback_info info) {
    137. int64_t surfaceId = ParseId(env, info);
    138. auto pluginRender = GetPluginRender(surfaceId);
    139. if (pluginRender == nullptr) {
    140. OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "ChangeColor: Get pluginRender failed");
    141. return nullptr;
    142. }
    143. pluginRender->ChangeColor(); // 参考Native XComponent场景 ChangeColor实现
    144. return nullptr;
    145. }
    146. void PluginRender::ChangeColor() { eglCore_->ChangeColor(hasChangeColor_); }
    147. // 获得xcomponent状态,并返回至ArkTS侧
    148. napi_value PluginManager::GetXComponentStatus(napi_env env, napi_callback_info info) {
    149. int64_t surfaceId = ParseId(env, info);
    150. auto pluginRender = GetPluginRender(surfaceId);
    151. if (pluginRender == nullptr) {
    152. OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager",
    153. "GetXComponentStatus: Get pluginRender failed");
    154. return nullptr;
    155. }
    156. napi_value hasDraw;
    157. napi_value hasChangeColor;
    158. napi_status ret = napi_create_int32(env, pluginRender->HasDraw(), &(hasDraw));
    159. if (ret != napi_ok) {
    160. OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager",
    161. "GetXComponentStatus: napi_create_int32 hasDraw_ error");
    162. return nullptr;
    163. }
    164. ret = napi_create_int32(env, pluginRender->HasChangedColor(), &(hasChangeColor));
    165. if (ret != napi_ok) {
    166. OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager",
    167. "GetXComponentStatus: napi_create_int32 hasChangeColor_ error");
    168. return nullptr;
    169. }
    170. napi_value obj;
    171. ret = napi_create_object(env, &obj);
    172. if (ret != napi_ok) {
    173. OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager",
    174. "GetXComponentStatus: napi_create_object error");
    175. return nullptr;
    176. }
    177. ret = napi_set_named_property(env, obj, "hasDraw", hasDraw);
    178. if (ret != napi_ok) {
    179. OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager",
    180. "GetXComponentStatus: napi_set_named_property hasDraw error");
    181. return nullptr;
    182. }
    183. ret = napi_set_named_property(env, obj, "hasChangeColor", hasChangeColor);
    184. if (ret != napi_ok) {
    185. OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager",
    186. "GetXComponentStatus: napi_set_named_property hasChangeColor error");
    187. return nullptr;
    188. }
    189. return obj;
    190. }
    191. int32_t PluginRender::HasDraw() { return hasDraw_; }
    192. int32_t PluginRender::HasChangedColor() { return hasChangeColor_; }
  4. 配置具体的CMakeLists,使用CMake工具链将C++源代码编译成动态链接库文件。

    收起
    自动换行
    深色代码主题
    复制
    1. # 设置CMake最小版本.
    2. cmake_minimum_required(VERSION 3.4.1)
    3. # 项目名称
    4. project(XComponent)
    5. set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
    6. add_definitions(-DOHOS_PLATFORM)
    7. # 设置头文件搜索目录
    8. include_directories(
    9. ${NATIVERENDER_ROOT_PATH}
    10. ${NATIVERENDER_ROOT_PATH}/include
    11. )
    12. # 添加名为nativerender的动态库,库文件名为libnativerender.so,添加cpp文件
    13. add_library(nativerender SHARED
    14. render/egl_core.cpp
    15. render/plugin_render.cpp
    16. manager/plugin_manager.cpp
    17. napi_init.cpp
    18. )
    19. find_library(
    20. # Sets the name of the path variable.
    21. EGL-lib
    22. # Specifies the name of the NDK library that
    23. # you want CMake to locate.
    24. EGL
    25. )
    26. find_library(
    27. # Sets the name of the path variable.
    28. GLES-lib
    29. # Specifies the name of the NDK library that
    30. # you want CMake to locate.
    31. GLESv3
    32. )
    33. find_library(
    34. # Sets the name of the path variable.
    35. hilog-lib
    36. # Specifies the name of the NDK library that
    37. # you want CMake to locate.
    38. hilog_ndk.z
    39. )
    40. find_library(
    41. # Sets the name of the path variable.
    42. libace-lib
    43. # Specifies the name of the NDK library that
    44. # you want CMake to locate.
    45. ace_ndk.z
    46. )
    47. find_library(
    48. # Sets the name of the path variable.
    49. libnapi-lib
    50. # Specifies the name of the NDK library that
    51. # you want CMake to locate.
    52. ace_napi.z
    53. )
    54. find_library(
    55. # Sets the name of the path variable.
    56. libuv-lib
    57. # Specifies the name of the NDK library that
    58. # you want CMake to locate.
    59. uv
    60. )
    61. # 添加构建需要链接的库
    62. target_link_libraries(nativerender PUBLIC
    63. ${EGL-lib} ${GLES-lib} ${hilog-lib} ${libace-lib} ${libnapi-lib} ${libuv-lib} libnative_window.so)

自绘制原理说明

XComponent持有一个surface,开发者能通过调用NativeWindow等接口,申请并提交Buffer至图形队列,以此方式将自绘制内容传送至该surface。XComponent负责将此surface整合进UI界面,其中展示的内容正是开发者传送的自绘制内容。surface的默认位置与大小与XComponent组件一致,开发者可利用setXComponentSurfaceRect接口自定义调整surface的位置和大小。

说明

当开发者传输的绘制内容包含透明元素时,surface区域的显示效果会与下方内容进行合成展示。例如,若传输的内容完全透明,且XComponent的背景色被设置为黑色,同时Surface保持默认的大小与位置,则最终显示的将是一片黑色区域。

生命周期说明

开发者在ArkTS侧使用如下代码即可用XComponent组件进行利用EGL/OpenGLES渲染的开发。

收起
自动换行
深色代码主题
复制
  1. @Builder
  2. function myComponent() {
  3. XComponent({ id: 'xcomponentId1', type: XComponentType.SURFACE, libraryname: 'nativerender' })
  4. .onLoad((context) => {})
  5. .onDestroy(() => {})
  6. }

onLoad事件

触发时刻:XComponent准备好surface后触发。

参数context:其上面挂载了暴露在模块上的Native方法,使用方法类似于利用 import context from "libnativerender.so" 直接加载模块后获得的context实例。

时序

​ Native XComponent场景:

​ onLoad事件的触发和surface相关,其和Native侧的OnSurfaceCreated的时序如下图:

​ ArkTS XComponent场景:

​ onLoad事件的触发和surface相关,其和ArkTS侧的OnSurfaceCreated的时序如下图:

onDestroy事件

触发时刻:XComponent组件被销毁时触发,与一般ArkUI的组件销毁时机一致。

时序:

​ Native XComponent场景:

​ onDestroy事件的触发和surface相关,其和Native侧的OnSurfaceDestroyed的时序如下图:

​ ArkTS XComponent场景:

​ onDestroy事件的触发和surface相关,其和ArkTS侧的OnSurfaceDestroyed的时序如下图:

示例代码

在 指南 中进行搜索
请输入您想要搜索的关键词