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