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