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