1# 自定义渲染 (XComponent) 2<!--Kit: ArkUI--> 3<!--Subsystem: ArkUI--> 4<!--Owner: @zjsxstar--> 5<!--Designer: @sunbees--> 6<!--Tester: @liuli0427--> 7<!--Adviser: @HelloCrease--> 8 9## 概述 10 11XComponent组件作为一种渲染组件,可用于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组件的内容合成后展示到屏幕上。 12 13XComponent持有一个Surface,开发者能通过调用[NativeWindow](../graphics/native-window-guidelines.md)等接口,申请并提交Buffer至图形队列,以此方式将自绘制内容传送至该Surface。XComponent负责将此Surface整合进UI界面,其中展示的内容正是开发者传送的自绘制内容。Surface的默认位置与大小与XComponent组件一致,开发者可利用[setXComponentSurfaceRect](../reference/apis-arkui/arkui-ts/ts-basic-components-xcomponent.md#setxcomponentsurfacerect12)接口自定义调整Surface的位置和大小。XComponent组件负责创建Surface,并通过回调将Surface的相关信息告知应用。应用可以通过一系列接口设定Surface的属性。该组件本身不对所绘制的内容进行感知,亦不提供渲染绘制的接口。 14 15目前XComponent组件主要有三个应用场景: 161. [使用XComponentController管理Surface生命周期场景](#使用xcomponentcontroller管理surface生命周期),该场景在ArkTS侧获取SurfaceId,生命周期回调、触摸、鼠标、按键等事件回调等均在ArkTS侧触发。 172. [使用OH_ArkUI_SurfaceHolder管理Surface生命周期场景](#使用oh_arkui_surfaceholder管理surface生命周期),该场景根据XComponent组件对应的ArkUI_NodeHandle中创建OH_ArkUI_SurfaceHolder,生命周期回调、触摸等事件回调、无障碍和可变帧率回调等均在Native侧触发。 183. [使用NativeXComponent管理Surface生命周期场景](#使用nativexcomponent管理surface生命周期),该场景在native层获取Native XComponent实例,在Native侧注册XComponent的生命周期回调,以及触摸、鼠标、按键等事件回调。 19 20## 约束与限制 21 22当开发者传输的绘制内容包含透明元素时,Surface区域的显示效果会与下方内容进行合成展示。例如,若传输的内容完全透明,且XComponent的背景色被设置为黑色,同时Surface保持默认的大小与位置,则最终显示的将是一片黑色区域。 23 24## 使用XComponentController管理Surface生命周期 25 26本场景通过在ArkTS侧获取SurfaceId,布局信息、生命周期回调、触摸、鼠标、按键等事件回调等均在ArkTS侧触发,按需传递到Native侧进行处理。主要开发场景如下: 27- 基于ArkTS侧获取的SurfaceId,在Native侧调用OH_NativeWindow_CreateNativeWindowFromSurfaceId接口创建出NativeWindow实例。 28- 利用NativeWindow和EGL接口开发自定义绘制内容以及申请和提交Buffer到图形队列。 29- ArkTS侧获取生命周期、事件等信息传递到Native侧处理。 30 31> **说明**: 32> 33> 1. Native侧的NativeWindow缓存在字典中,其key需要保证其唯一性,当对应的XComponent销毁后,需要及时从字典里将其删除。 34> 35> 2. 对于使用[typeNode](../reference/apis-arkui/js-apis-arkui-frameNode.md#typenode12)创建的SURFACE或TEXTURE类型的XComponent组件,由于typeNode组件的生命周期与声明式组件存在差异,组件在创建后的缓冲区尺寸为未设置状态,因此在开始绘制内容之前,应调用[OH_NativeWindow_NativeWindowHandleOpt](../reference/apis-arkgraphics2d/capi-external-window-h.md#oh_nativewindow_nativewindowhandleopt)接口进行缓冲区尺寸设置。 36> 37> 3. 多个XComponent开发时,缓存Native侧资源需要保证key是唯一的,key推荐使用id+随机数或者surfaceId。 38> 39> 4. 在onSurfaceCreated回调触发后,才能获取到有效的surfaceId。 40 41**效果预览** 42 43| 主页 | 绘制五角星 | 改变颜色 | 44|--------------------------------------|-----------------------------------------------|-----------------------------------------------------| 45|  |  |  | 46 47>**说明:** 48> 49>1. 安装编译生成的hap包,并打开应用。 50> 51>2. 点击页面底部“Draw Star”按钮,页面将绘制一个五角星。 52> 53>3. 点击XComponent组件区域(页面中灰色区域)改变五角星颜色。 54 55**生命周期**: 56 57- onSurfaceCreated回调 58 59 触发时刻:XComponent创建完成且创建好Surface后触发。 60 61 ArkTS侧onSurfaceCreated的时序如下图: 62 63  64 65- onSurfaceChanged回调 66 67 触发时刻:Surface大小变化触发重新布局之后触发。 68 69 ArkTS侧onSurfaceChanged的时序如下图: 70 71  72 73- onSurfaceDestroyed回调 74 75 触发时刻:XComponent组件被销毁时触发,与一般ArkUI的组件销毁时机一致。 76 77 ArkTS侧onSurfaceDestroyed的时序图: 78 79  80 81**接口说明** 82 83ArkTS侧的XComponentController 84 85| 接口名 | 描述 | 86| ------------------------------------------------------------ | ------------------------------------------------------------ | 87| getXComponentSurfaceId(): string | 获取XComponent对应Surface的ID。 | 88| onSurfaceCreated(surfaceId: string): void | 当XComponent持有的Surface创建后进行该回调。 | 89| onSurfaceChanged(surfaceId: string, rect: SurfaceRect): void | 当XComponent持有的Surface大小改变后(包括首次创建时的大小改变)进行该回调。 | 90| onSurfaceDestroyed(surfaceId: string): void | 当XComponent持有的Surface销毁后进行该回调。 | 91 92Native侧 93 94| 接口名 | 描述 | 95| ------------------------------------------------------------ | ------------------------------------------------------------ | 96| int32_t OH_NativeWindow_CreateNativeWindowFromSurfaceId (uint64_t surfaceId, OHNativeWindow **window ) | 通过surfaceId创建对应的OHNativeWindow。 | 97| void OH_NativeWindow_DestroyNativeWindow (OHNativeWindow* window) | 将OHNativeWindow对象的引用计数减1,当引用计数为0的时候,该OHNativeWindow对象会被析构掉。 | 98 99**开发步骤** 100 101核心开发流程如下图所示: 102 103 104 105以下步骤以SURFACE类型为例,描述了如何使用`XComponent组件`在ArkTS侧传入SurfaceId,在Native侧创建NativeWindow实例,然后创建`EGL/GLES`环境,实现在主页面绘制图形,并可以改变图形的颜色。 106 1071. 在界面中定义XComponent,在cpp/types/libnativerender/Index.d.ts中声明接口,具体实现位于Native侧。 108 109 ```javascript 110 // 函数声明,在cpp/types/libnativerender/Index.d.ts中定义 111 type XComponentContextStatus = { 112 hasDraw: boolean, 113 hasChangeColor: boolean, 114 }; 115 export const SetSurfaceId: (id: BigInt) => any; 116 export const ChangeSurface: (id: BigInt, w: number, h: number) =>any; 117 export const DrawPattern: (id: BigInt) => any; 118 export const GetXComponentStatus: (id: BigInt) => XComponentContextStatus 119 export const ChangeColor: (id: BigInt) => any; 120 export const DestroySurface: (id: BigInt) => any; 121 ``` 122 123 ```typescript 124 import nativeRender from 'libnativerender.so'; 125 126 // 重写XComponentController,设置生命周期回调 127 class MyXComponentController extends XComponentController { 128 onSurfaceCreated(surfaceId: string): void { 129 console.log(`onSurfaceCreated surfaceId: ${surfaceId}`) 130 nativeRender.SetSurfaceId(BigInt(surfaceId)); 131 } 132 133 onSurfaceChanged(surfaceId: string, rect: SurfaceRect): void { 134 console.log(`onSurfaceChanged surfaceId: ${surfaceId}, rect: ${JSON.stringify(rect)}}`) 135 // 在onSurfaceChanged中调用ChangeSurface绘制内容 136 nativeRender.ChangeSurface(BigInt(surfaceId), rect.surfaceWidth, rect.surfaceHeight) 137 } 138 139 onSurfaceDestroyed(surfaceId: string): void { 140 console.log(`onSurfaceDestroyed surfaceId: ${surfaceId}`) 141 nativeRender.DestroySurface(BigInt(surfaceId)) 142 } 143 } 144 145 @Entry 146 @Component 147 struct Index { 148 @State currentStatus: string = "index"; 149 xComponentController: XComponentController = new MyXComponentController(); 150 151 build() { 152 Column() { 153 //... 154 //在xxx.ets 中定义 XComponent 155 Column({ space: 10 }) { 156 XComponent({ 157 type: XComponentType.SURFACE, 158 controller: this.xComponentController 159 }) 160 Text(this.currentStatus) 161 .fontSize('24fp') 162 .fontWeight(500) 163 } 164 .onClick(() => { 165 let surfaceId = this.xComponentController.getXComponentSurfaceId() 166 nativeRender.ChangeColor(BigInt(surfaceId)) 167 let hasChangeColor: boolean = false; 168 if (nativeRender.GetXComponentStatus(BigInt(surfaceId))) { 169 hasChangeColor = nativeRender.GetXComponentStatus(BigInt(surfaceId)).hasChangeColor; 170 } 171 if (hasChangeColor) { 172 this.currentStatus = "change color"; 173 } 174 }) 175 176 //... 177 Row() { 178 Button('Draw Star') 179 .fontSize('16fp') 180 .fontWeight(500) 181 .margin({ bottom: 24 }) 182 .onClick(() => { 183 let surfaceId = this.xComponentController.getXComponentSurfaceId() 184 nativeRender.DrawPattern(BigInt(surfaceId)) 185 let hasDraw: boolean = false; 186 if (nativeRender.GetXComponentStatus(BigInt(surfaceId))) { 187 hasDraw = nativeRender.GetXComponentStatus(BigInt(surfaceId)).hasDraw; 188 } 189 if (hasDraw) { 190 this.currentStatus = "draw star" 191 } 192 }) 193 .width('53.6%') 194 .height(40) 195 } 196 .width('100%') 197 .justifyContent(FlexAlign.Center) 198 .alignItems(VerticalAlign.Bottom) 199 .layoutWeight(1) 200 } 201 .width('100%') 202 .height('100%') 203 } 204 } 205 ``` 206 2072. Node-API模块注册,具体使用请参考[Node-API开发规范](../napi/napi-guidelines.md)。 208 209 ```typescript 210 #include <hilog/log.h> 211 #include "common/common.h" 212 #include "manager/plugin_manager.h" 213 namespace NativeXComponentSample { 214 // 在napi_init.cpp文件中,Init方法注册接口函数,从而将封装的C++方法传递出来,供ArkTS侧调用 215 EXTERN_C_START 216 static napi_value Init(napi_env env, napi_value exports) { 217 // ... 218 // 向ArkTS侧暴露接口SetSurfaceId(),ChangeSurface(),DestroySurface(), 219 // DrawPattern(),ChangeColor(),GetXComponentStatus() 220 napi_property_descriptor desc[] = { 221 {"SetSurfaceId", nullptr, PluginManager::SetSurfaceId, nullptr, nullptr, nullptr, napi_default, nullptr}, 222 {"ChangeSurface", nullptr, PluginManager::ChangeSurface, nullptr, nullptr, nullptr, napi_default, nullptr}, 223 {"DestroySurface", nullptr, PluginManager::DestroySurface, nullptr, nullptr, nullptr, napi_default, nullptr}, 224 {"DrawPattern", nullptr, PluginManager::DrawPattern, nullptr, nullptr, nullptr, napi_default, nullptr}, 225 {"ChangeColor", nullptr, PluginManager::ChangeColor, nullptr, nullptr, nullptr, napi_default, nullptr}, 226 {"GetXComponentStatus", nullptr, PluginManager::GetXComponentStatus, nullptr, nullptr, nullptr, napi_default, 227 nullptr}}; 228 if (napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc) != napi_ok) { 229 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Init", "napi_define_properties failed"); 230 return nullptr; 231 } 232 return exports; 233 } 234 EXTERN_C_END 235 // 编写接口的描述信息,根据实际需要可以修改对应参数 236 static napi_module nativerenderModule = {.nm_version = 1, 237 .nm_flags = 0, 238 .nm_filename = nullptr, 239 // 入口函数 240 .nm_register_func = Init, 241 // 模块名称 242 .nm_modname = "nativerender", 243 .nm_priv = ((void *)0), 244 .reserved = {0}}; 245 } // namespace NativeXComponentSample 246 // __attribute__((constructor))修饰的方法由系统自动调用,使用Node-API接口napi_module_register()传入模块描述信息进行模块注册 247 extern "C" __attribute__((constructor)) void RegisterModule(void) { 248 napi_module_register(&NativeXComponentSample::nativerenderModule); 249 } 250 ``` 251 2523. 上述注册的六个函数在Native侧的具体实现如下:ChangeColor和DrawPattern利用OpenGL(https://developer.huawei.com/consumer/cn/doc/harmonyos-references/opengl)进行五角星的绘制;ChangeSurface根据传入的surfaceId、width、height调整Surface的大小;SetSurfaceId基于SurfaceId完成NativeWindow的初始化;DestroySurface销毁与Surface相关的资源;GetXComponentStatus获取xcomponent状态并返回至ArkTS侧。 253 254 ```cpp 255 // PluginManager类定义 256 class PluginManager { 257 public: 258 ~PluginManager(); 259 static PluginRender *GetPluginRender(int64_t &id); 260 static napi_value ChangeColor(napi_env env, napi_callback_info info); 261 static napi_value DrawPattern(napi_env env, napi_callback_info info); 262 static napi_value SetSurfaceId(napi_env env, napi_callback_info info); 263 static napi_value ChangeSurface(napi_env env, napi_callback_info info); 264 static napi_value DestroySurface(napi_env env, napi_callback_info info); 265 static napi_value GetXComponentStatus(napi_env env, napi_callback_info info); 266 267 public: 268 static std::unordered_map<int64_t, PluginRender *> pluginRenderMap_; 269 static std::unordered_map<int64_t, OHNativeWindow *> windowMap_; 270 }; 271 272 // 解析从ArkTS侧传入的surfaceId,此处surfaceId是一个64位int值 273 int64_t ParseId(napi_env env, napi_callback_info info) { 274 if ((env == nullptr) || (info == nullptr)) { 275 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ParseId", "env or info is null"); 276 return -1; 277 } 278 size_t argc = 1; 279 napi_value args[1] = {nullptr}; 280 if (napi_ok != napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)) { 281 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ParseId", "GetContext napi_get_cb_info failed"); 282 return -1; 283 } 284 int64_t value = 0; 285 bool lossless = true; 286 if (napi_ok != napi_get_value_bigint_int64(env, args[0], &value, &lossless)) { 287 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ParseId", "Get value failed"); 288 return -1; 289 } 290 return value; 291 } 292 293 // 设置SurfaceId,基于SurfaceId完成对NativeWindow的初始化 294 napi_value PluginManager::SetSurfaceId(napi_env env, napi_callback_info info) { 295 int64_t surfaceId = ParseId(env, info); 296 OHNativeWindow *nativeWindow; 297 PluginRender *pluginRender; 298 if (windowMap_.find(surfaceId) == windowMap_.end()) { 299 OH_NativeWindow_CreateNativeWindowFromSurfaceId(surfaceId, &nativeWindow); 300 windowMap_[surfaceId] = nativeWindow; 301 } else { 302 return nullptr; 303 } 304 if (pluginRenderMap_.find(surfaceId) == pluginRenderMap_.end()) { 305 pluginRender = new PluginRender(surfaceId); 306 pluginRenderMap_[surfaceId] = pluginRender; 307 } 308 pluginRender->InitNativeWindow(nativeWindow); 309 return nullptr; 310 } 311 312 void PluginRender::InitNativeWindow(OHNativeWindow *window) { 313 eglCore_->EglContextInit(window); // 参考Native XComponent场景EglContextInit的实现 314 } 315 316 // 根据传入的surfaceId、width、height实现Surface大小的变动 317 napi_value PluginManager::ChangeSurface(napi_env env, napi_callback_info info) { 318 if ((env == nullptr) || (info == nullptr)) { 319 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", 320 "ChangeSurface: OnLoad env or info is null"); 321 return nullptr; 322 } 323 int64_t surfaceId = 0; 324 size_t argc = 3; 325 napi_value args[3] = {nullptr}; 326 327 if (napi_ok != napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)) { 328 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", 329 "ChangeSurface: GetContext napi_get_cb_info failed"); 330 return nullptr; 331 } 332 bool lossless = true; 333 int index = 0; 334 if (napi_ok != napi_get_value_bigint_int64(env, args[index++], &surfaceId, &lossless)) { 335 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "ChangeSurface: Get value failed"); 336 return nullptr; 337 } 338 double width; 339 if (napi_ok != napi_get_value_double(env, args[index++], &width)) { 340 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "ChangeSurface: Get width failed"); 341 return nullptr; 342 } 343 double height; 344 if (napi_ok != napi_get_value_double(env, args[index++], &height)) { 345 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "ChangeSurface: Get height failed"); 346 return nullptr; 347 } 348 auto pluginRender = GetPluginRender(surfaceId); 349 if (pluginRender == nullptr) { 350 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "ChangeSurface: Get pluginRender failed"); 351 return nullptr; 352 } 353 pluginRender->UpdateNativeWindowSize(width, height); 354 return nullptr; 355 } 356 357 void PluginRender::UpdateNativeWindowSize(int width, int height) { 358 eglCore_->UpdateSize(width, height); // 参考Native XComponent场景UpdateSize的实现 359 if (!hasChangeColor_ && !hasDraw_) { 360 eglCore_->Background(); // 参考Native XComponent场景Background的实现 361 } 362 } 363 364 // 销毁Surface 365 napi_value PluginManager::DestroySurface(napi_env env, napi_callback_info info) { 366 int64_t surfaceId = ParseId(env, info); 367 auto pluginRenderMapIter = pluginRenderMap_.find(surfaceId); 368 if (pluginRenderMapIter != pluginRenderMap_.end()) { 369 delete pluginRenderMapIter->second; 370 pluginRenderMap_.erase(pluginRenderMapIter); 371 } 372 auto windowMapIter = windowMap_.find(surfaceId); 373 if (windowMapIter != windowMap_.end()) { 374 OH_NativeWindow_DestroyNativeWindow(windowMapIter->second); 375 windowMap_.erase(windowMapIter); 376 } 377 return nullptr; 378 } 379 380 // 实现EGL绘画逻辑 381 napi_value PluginManager::DrawPattern(napi_env env, napi_callback_info info) { 382 int64_t surfaceId = ParseId(env, info); 383 auto pluginRender = GetPluginRender(surfaceId); 384 if (pluginRender == nullptr) { 385 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "DrawPattern: Get pluginRender failed"); 386 return nullptr; 387 } 388 pluginRender->DrawPattern(); 389 return nullptr; 390 } 391 392 PluginRender *PluginManager::GetPluginRender(int64_t &id) { 393 if (pluginRenderMap_.find(id) != pluginRenderMap_.end()) { 394 return pluginRenderMap_[id]; 395 } 396 return nullptr; 397 } 398 399 void PluginRender::DrawPattern() { 400 eglCore_->Draw(hasDraw_); // 参考Native XComponent场景Draw实现 401 } 402 403 // 实现改变绘制图形颜色的功能 404 napi_value PluginManager::ChangeColor(napi_env env, napi_callback_info info) { 405 int64_t surfaceId = ParseId(env, info); 406 auto pluginRender = GetPluginRender(surfaceId); 407 if (pluginRender == nullptr) { 408 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "ChangeColor: Get pluginRender failed"); 409 return nullptr; 410 } 411 pluginRender->ChangeColor(); // 参考Native XComponent场景ChangeColor实现 412 return nullptr; 413 } 414 415 void PluginRender::ChangeColor() { eglCore_->ChangeColor(hasChangeColor_); } 416 417 // 获得xcomponent状态,并返回至ArkTS侧 418 napi_value PluginManager::GetXComponentStatus(napi_env env, napi_callback_info info) { 419 int64_t surfaceId = ParseId(env, info); 420 auto pluginRender = GetPluginRender(surfaceId); 421 if (pluginRender == nullptr) { 422 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", 423 "GetXComponentStatus: Get pluginRender failed"); 424 return nullptr; 425 } 426 napi_value hasDraw; 427 napi_value hasChangeColor; 428 napi_status ret = napi_create_int32(env, pluginRender->HasDraw(), &(hasDraw)); 429 if (ret != napi_ok) { 430 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", 431 "GetXComponentStatus: napi_create_int32 hasDraw_ error"); 432 return nullptr; 433 } 434 ret = napi_create_int32(env, pluginRender->HasChangedColor(), &(hasChangeColor)); 435 if (ret != napi_ok) { 436 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", 437 "GetXComponentStatus: napi_create_int32 hasChangeColor_ error"); 438 return nullptr; 439 } 440 napi_value obj; 441 ret = napi_create_object(env, &obj); 442 if (ret != napi_ok) { 443 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", 444 "GetXComponentStatus: napi_create_object error"); 445 return nullptr; 446 } 447 ret = napi_set_named_property(env, obj, "hasDraw", hasDraw); 448 if (ret != napi_ok) { 449 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", 450 "GetXComponentStatus: napi_set_named_property hasDraw error"); 451 return nullptr; 452 } 453 ret = napi_set_named_property(env, obj, "hasChangeColor", hasChangeColor); 454 if (ret != napi_ok) { 455 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", 456 "GetXComponentStatus: napi_set_named_property hasChangeColor error"); 457 return nullptr; 458 } 459 return obj; 460 } 461 462 int32_t PluginRender::HasDraw() { return hasDraw_; } 463 464 int32_t PluginRender::HasChangedColor() { return hasChangeColor_; } 465 ``` 466 4674. 配置具体的CMakeLists,使用CMake工具链将C++源代码编译成动态链接库文件。 468 469 ```cmake 470 # 设置CMake最小版本 471 cmake_minimum_required(VERSION 3.4.1) 472 # 项目名称 473 project(XComponent) 474 475 set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR}) 476 add_definitions(-DOHOS_PLATFORM) 477 # 设置头文件搜索目录 478 include_directories( 479 ${NATIVERENDER_ROOT_PATH} 480 ${NATIVERENDER_ROOT_PATH}/include 481 ) 482 # 添加名为nativerender的动态库,库文件名为libnativerender.so,添加cpp文件 483 add_library(nativerender SHARED 484 render/egl_core.cpp 485 render/plugin_render.cpp 486 manager/plugin_manager.cpp 487 napi_init.cpp 488 ) 489 490 find_library( 491 # 设置路径变量的名称。 492 EGL-lib 493 # 指定要让CMake查找的NDK库的名称。 494 EGL 495 ) 496 497 find_library( 498 # 设置路径变量的名称。 499 GLES-lib 500 # 指定要让CMake查找的NDK库的名称。 501 GLESv3 502 ) 503 504 find_library( 505 # 设置路径变量的名称。 506 hilog-lib 507 # 指定要让CMake查找的NDK库的名称。 508 hilog_ndk.z 509 ) 510 511 find_library( 512 # 设置路径变量的名称。 513 libace-lib 514 # 指定要让CMake查找的NDK库的名称。 515 ace_ndk.z 516 ) 517 518 find_library( 519 # 设置路径变量的名称。 520 libnapi-lib 521 # 指定要让CMake查找的NDK库的名称。 522 ace_napi.z 523 ) 524 525 find_library( 526 # 设置路径变量的名称。 527 libuv-lib 528 # 指定要让CMake查找的NDK库的名称。 529 uv 530 ) 531 # 添加构建需要链接的库 532 target_link_libraries(nativerender PUBLIC 533 ${EGL-lib} ${GLES-lib} ${hilog-lib} ${libace-lib} ${libnapi-lib} ${libuv-lib} libnative_window.so) 534 ``` 535 536上述用例具体实现可参考<!--RP2-->[ArkTSXComponent(API12)](https://gitee.com/openharmony/applications_app_samples/tree/master/code/BasicFeature/Native/ArkTSXComponent)<!--RP2nd-->。 537 538## 使用OH_ArkUI_SurfaceHolder管理Surface生命周期 539 540与使用XComponentController管理Surface生命周期场景不同,本场景允许应用根据XComponent组件对应的ArkUI_NodeHandle中创建OH_ArkUI_SurfaceHolder,并通过OH_ArkUI_SurfaceHolder上的相关接口注册Surface生命周期,XComponent组件相关的无障碍、可变帧率等能力也可根据ArkUI_NodeHandle通过相关接口来实现。同时,XComponent组件上的基础/手势事件也可通过ArkUI_NodeHandle对象使用ArkUI NDK接口来监听(具体可参考:[监听组件事件](./ndk-listen-to-component-events.md))。主要开发场景如下: 541- 在ArkTS侧创建的XComponent组件可以将其对应的FrameNode节点传递到Native侧以获取ArkUI_NodeHandle,或者在Native侧直接创建XComponent组件对应的ArkUI_NodeHandle,然后调用OH_ArkUI_SurfaceHolder_Create接口创建OH_ArkUI_SurfaceHolder实例。 542- 基于OH_ArkUI_SurfaceHolder实例注册相应的生命周期回调、事件回调,获取NativeWindow实例。 543- 利用NativeWindow和EGL接口开发自定义绘制内容以及申请和提交Buffer到图形队列。 544 545**生命周期**: 546 547- OnSurfaceCreated回调 548 549 触发时刻:XComponent创建完成且创建好Surface后达成以下两个条件中的一个触发。 550 1. 组件上树且autoInitialize = true。 551 2. 调用OH_ArkUI_XComponent_Initialize。 552 553 Native侧OnSurfaceCreated的时序如下图: 554 555  556- OnSurfaceChanged回调 557 558 触发时刻:OnSurfaceCreated回调成功触发且Surface大小变化触发重新布局之后触发。 559 560 Native侧OnSurfaceChanged的时序如下图: 561 562  563 564- OnSurfaceDestroyed回调 565 566 触发时刻:组件下树且autoInitialize=true 或者调用 OH_ArkUI_XComponent_Finalize后触发。 567 568 Native侧OnSurfaceDestroyed的时序图: 569 570  571 572 573**接口说明** 574 575| 接口名 | 描述 | 576| ------------------------------------------------------------ | ------------------------------------------------------------ | 577| OH_ArkUI_QueryModuleInterfaceByName(ArkUI_NativeAPIVariantKind type, const char* structName) | 获取指定类型的Native模块接口集合。 | 578| OH_ArkUI_XComponent_GetNativeWindow(OH_ArkUI_SurfaceHolder* surfaceHolder) | 获取与OH_ArkUI_SurfaceHolder实例关联的nativeWindow。 | 579| OH_ArkUI_SurfaceHolder_RemoveSurfaceCallback(OH_ArkUI_SurfaceHolder* surfaceHolder, OH_ArkUI_SurfaceCallback* callback) | 从OH_ArkUI_SurfaceHolder实例中移除先前添加的Surface生命周期回调。 | 580| OH_ArkUI_SurfaceCallback_Dispose(OH_ArkUI_SurfaceCallback* callback) | 释放OH_ArkUI_SurfaceCallback对象。 | 581| OH_ArkUI_SurfaceHolder_Dispose(OH_ArkUI_SurfaceHolder* surfaceHolder) | 释放OH_ArkUI_SurfaceHolder对象。 | 582| OH_ArkUI_NodeEvent_GetEventType(ArkUI_NodeEvent* event) | 从组件事件获取事件类型。 | 583| OH_ArkUI_NodeEvent_GetNodeHandle(ArkUI_NodeEvent* event) | 获取触发组件事件的组件对象。 | 584| OH_ArkUI_GetNodeHandleFromNapiValue(napi_env env, napi_value frameNode, ArkUI_NodeHandle* handle) | 获取ArkTS侧创建的FrameNode节点对象映射到Native侧的ArkUI_NodeHandle。 | 585| OH_ArkUI_SurfaceHolder_Create(ArkUI_NodeHandle node) | 从XComponent节点创建一个OH_ArkUI_SurfaceHolder对象。 | 586| OH_ArkUI_SurfaceCallback_Create() | 创建一个OH_ArkUI_SurfaceCallback对象。 | 587| OH_ArkUI_SurfaceCallback_SetSurfaceCreatedEvent(OH_ArkUI_SurfaceCallback* callback, void (\*onSurfaceCreated)(OH_ArkUI_SurfaceHolder* surfaceHolder)) | 往OH_ArkUI_SurfaceCallback对象中注册onSurfaceCreated回调。 | 588| OH_ArkUI_SurfaceCallback_SetSurfaceChangedEvent(OH_ArkUI_SurfaceCallback* callback, void (\*onSurfaceChanged)(OH_ArkUI_SurfaceHolder* surfaceHolder, uint64_t width, uint64_t height)) | 往OH_ArkUI_SurfaceCallback对象中注册onSurfaceChanged回调。 | 589| OH_ArkUI_SurfaceCallback_SetSurfaceDestroyedEvent(OH_ArkUI_SurfaceCallback* callback, void (\*onSurfaceDestroyed)(OH_ArkUI_SurfaceHolder* surfaceHolder)) | 往OH_ArkUI_SurfaceCallback对象中注册onSurfaceDestroyed回调。 | 590| OH_ArkUI_SurfaceCallback_SetSurfaceShowEvent(OH_ArkUI_SurfaceCallback* callback, void (\*onSurfaceShow)(OH_ArkUI_SurfaceHolder* surfaceHolder)) | 往OH_ArkUI_SurfaceCallback对象中注册onSurfaceShow回调。 | 591| OH_ArkUI_SurfaceCallback_SetSurfaceHideEvent(OH_ArkUI_SurfaceCallback* callback, void (\*onSurfaceHide)(OH_ArkUI_SurfaceHolder* surfaceHolder)) | 往OH_ArkUI_SurfaceCallback对象中注册onSurfaceHide回调。 | 592| OH_ArkUI_XComponent_RegisterOnFrameCallback(ArkUI_NodeHandle node, void (*callback)(ArkUI_NodeHandle node, uint64_t timestamp, uint64_t targetTimestamp)) | 为XComponent节点注册onFrame回调。 | 593| OH_ArkUI_SurfaceHolder_AddSurfaceCallback(OH_ArkUI_SurfaceHolder* surfaceHolder, OH_ArkUI_SurfaceCallback* callback) | 往OH_ArkUI_SurfaceHolder实例注册OH_ArkUI_SurfaceCallback对象。 | 594| OH_ArkUI_AccessibilityProvider_Create(ArkUI_NodeHandle node) | 从XComponent节点创建一个ArkUI_AccessibilityProvider对象。 | 595| OH_ArkUI_XComponent_UnregisterOnFrameCallback(ArkUI_NodeHandle node) | 取消注册XComponent节点的onFrame回调。 | 596| OH_ArkUI_AccessibilityProvider_Dispose(ArkUI_AccessibilityProvider* provider) | 释放ArkUI_AccessibilityProvider对象。 | 597| OH_ArkUI_XComponent_SetExpectedFrameRateRange(ArkUI_NodeHandle node, OH_NativeXComponent_ExpectedRateRange range) | 为XComponent节点设置预期的帧率范围。 | 598| OH_ArkUI_XComponent_SetNeedSoftKeyboard(ArkUI_NodeHandle node, bool needSoftKeyboard) | 设置XComponent节点在获得焦点时是否需要显示软键盘。 | 599 600**开发步骤** 601 602以下步骤通过在ArkTS侧创建SURFACE类型的XComponent为例(Native侧如何创建XComponent组件对应的ArkUI_NodeHandle可参考[ArkUI_NativeNodeAPI_1](../reference/apis-arkui/capi-arkui-nativemodule-arkui-nativenodeapi-1.md)),描述了如何使用`XComponent组件`调用OH_ArkUI_SurfaceHolder相关接口管理Surface生命周期,并在Native侧创建`EGL/GLES`环境,实现在主页面绘制图形,以及可以改变图形的颜色。 603 6041. 在界面中定义XComponent。 605 606 ```typescript 607 import native from 'libnativerender.so'; 608 609 @Entry 610 @Component 611 struct Index { 612 xcomponentId: string = 'xcp' + (new Date().getTime()); 613 @State isShow: boolean = true; 614 @State minRate: number = 0; 615 @State maxRate: number = 120; 616 @State expected: number = 60; 617 needSoftKeyboard: boolean = false; 618 @State needSoftKeyboardState: string = 'needSoftKeyboard=' + this.needSoftKeyboard; 619 @State text: string = '单指点击XComponent软件盘消失' 620 controller: TextInputController = new TextInputController() 621 622 build() { 623 Column() { 624 TextInput({ text: this.text, placeholder: 'please input ...', controller: this.controller }) 625 .placeholderColor(Color.Grey) 626 .placeholderFont({ size: 14, weight: 400 }) 627 .caretColor(Color.Blue) 628 .width(400) 629 .height(40) 630 .margin(20) 631 .fontSize(14) 632 .fontColor(Color.Black) 633 .onChange((value: string) => { 634 this.text = value 635 }) 636 Column() { 637 if (this.isShow) { 638 XComponent({ 639 type: XComponentType.SURFACE 640 }) 641 .id(this.xcomponentId) 642 .onAttach(() => { 643 let node = this.getUIContext().getFrameNodeById(this.xcomponentId) 644 native.bindNode(this.xcomponentId, node) 645 }) 646 .onDetach(() => { 647 native.unbindNode(this.xcomponentId) 648 }) 649 .width(200) 650 .height(200) 651 .focusable(true) 652 .focusOnTouch(true) 653 .defaultFocus(true) 654 } 655 }.height(200) 656 657 Button('创建/销毁').onClick(() => { 658 this.isShow = !this.isShow; 659 }) 660 661 Column() { 662 Text('期望帧率设置:') 663 .textAlign(TextAlign.Start) 664 .fontSize(15) 665 .border({ width: 1 }) 666 .padding(10) 667 .width('100%') 668 .margin(5) 669 Text('min: ' + this.minRate) 670 Slider({ 671 value: this.minRate, 672 min: 0, 673 max: 240, 674 step: 1 675 }).onChange((value: number, mode: SliderChangeMode) => { 676 this.minRate = value; 677 native.setFrameRate(this.xcomponentId, this.minRate, this.maxRate, this.expected) 678 }).width('100%') 679 Text('max: ' + this.maxRate) 680 Slider({ 681 value: this.maxRate, 682 min: 0, 683 max: 240, 684 step: 1 685 }).onChange((value: number, mode: SliderChangeMode) => { 686 this.maxRate = value; 687 native.setFrameRate(this.xcomponentId, this.minRate, this.maxRate, this.expected) 688 }).width('100%') 689 Text('expected: ' + this.expected) 690 Slider({ 691 value: this.expected, 692 min: 0, 693 max: 240, 694 step: 1 695 }).onChange((value: number, mode: SliderChangeMode) => { 696 this.expected = value; 697 native.setFrameRate(this.xcomponentId, this.minRate, this.maxRate, this.expected) 698 }).width('100%') 699 }.backgroundColor("#F0FAFF") 700 701 Button(this.needSoftKeyboardState) 702 .onClick(() => { 703 this.needSoftKeyboard = !this.needSoftKeyboard; 704 this.needSoftKeyboardState = 'needSoftKeyboard=' + this.needSoftKeyboard; 705 native.setNeedSoftKeyboard(this.xcomponentId, this.needSoftKeyboard); 706 this.text = this.needSoftKeyboard ? '单指点击XComponent软键盘不消失' : '单指点击XComponent软件盘消失' 707 }) 708 } 709 .width('100%') 710 } 711 } 712 ``` 713 7142. Node-API模块注册,具体使用请参考[Node-API开发规范](../napi/napi-guidelines.md)。 715 716 ```c++ 717 #include "manager/plugin_manager.h" 718 719 namespace NativeXComponentSample { 720 721 // 在napi_init.cpp文件中,Init方法注册接口函数,从而将封装的C++方法传递出来,供ArkTS侧调用 722 EXTERN_C_START 723 static napi_value Init(napi_env env, napi_value exports) 724 { 725 // 向ArkTS侧暴露接口 726 napi_property_descriptor desc[] = { 727 {"bindNode", nullptr, PluginManager::BindNode, 728 nullptr, nullptr, nullptr, napi_default, nullptr}, 729 {"unbindNode", nullptr, PluginManager::UnbindNode, 730 nullptr, nullptr, nullptr, napi_default, nullptr}, 731 {"setFrameRate", nullptr, PluginManager::SetFrameRate, 732 nullptr, nullptr, nullptr, napi_default, nullptr}, 733 {"setNeedSoftKeyboard", nullptr, PluginManager::SetNeedSoftKeyboard, 734 nullptr, nullptr, nullptr, napi_default, nullptr}, 735 }; 736 napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc); 737 return exports; 738 } 739 EXTERN_C_END 740 741 // 编写接口的描述信息,根据实际需要可以修改对应参数 742 static napi_module demoModule = { 743 .nm_version = 1, 744 .nm_flags = 0, 745 .nm_filename = nullptr, 746 // 入口函数 747 .nm_register_func = Init, // 指定加载对应模块时的回调函数 748 // 模块名称 749 .nm_modname = "nativerender", // 指定模块名称,对于XComponent相关开发,这个名称必须和ArkTS侧XComponent中libraryname的值保持一致 750 .nm_priv = ((void*)0), 751 .reserved = { 0 }, 752 }; 753 } 754 755 // __attribute__((constructor))修饰的方法由系统自动调用,使用Node-API接口napi_module_register()传入模块描述信息进行模块注册 756 extern "C" __attribute__((constructor)) void RegisterEntryModule(void) 757 { 758 napi_module_register(&NativeXComponentSample::demoModule); 759 } 760 ``` 761 7623. 注册XComponent生命周期、事件、无障碍和可变帧率回调,使用CAPI实现往XComponent注册回调函数。 763 764 (1) 定义BindNode、UnbindNode、SetFrameRate、SetNeedSoftKeyboard方法,暴露到ArkTS侧的bindNode、unbindNode、setFrameRate、setNeedSoftKeyboard方法会执行该方法。 765 766 ```c++ 767 // plugin_manager.h 768 namespace NativeXComponentSample { 769 class PluginManager { 770 public: 771 ~PluginManager(); 772 static napi_value BindNode(napi_env env, napi_callback_info info); 773 static napi_value UnbindNode(napi_env env, napi_callback_info info); 774 static napi_value SetFrameRate(napi_env env, napi_callback_info info); 775 static napi_value SetNeedSoftKeyboard(napi_env env, napi_callback_info info); 776 777 public: 778 static std::unordered_map<std::string, ArkUI_NodeHandle> nodeHandleMap_; 779 static std::unordered_map<void *, EGLRender *> renderMap_; 780 static std::unordered_map<void *, OH_ArkUI_SurfaceCallback *> callbackMap_; 781 static std::unordered_map<void *, OH_ArkUI_SurfaceHolder *> surfaceHolderMap_; 782 static ArkUI_AccessibilityProvider* provider_; 783 }; 784 } 785 ``` 786 787 ```c++ 788 // plugin_manager.cpp 789 std::unordered_map<std::string, ArkUI_NodeHandle> PluginManager::nodeHandleMap_; 790 std::unordered_map<void *, EGLRender *> PluginManager::renderMap_; 791 std::unordered_map<void *, OH_ArkUI_SurfaceCallback *> PluginManager::callbackMap_; 792 std::unordered_map<void *, OH_ArkUI_SurfaceHolder *> PluginManager::surfaceHolderMap_; 793 ArkUI_NativeNodeAPI_1 *nodeAPI = reinterpret_cast<ArkUI_NativeNodeAPI_1 *>( 794 OH_ArkUI_QueryModuleInterfaceByName(ARKUI_NATIVE_NODE, "ArkUI_NativeNodeAPI_1")); 795 796 std::string value2String(napi_env env, napi_value value) { // 将napi_value转化为string类型的变量 797 size_t stringSize = 0; 798 napi_get_value_string_utf8(env, value, nullptr, 0, &stringSize); 799 std::string valueString; 800 valueString.resize(stringSize); 801 napi_get_value_string_utf8(env, value, &valueString[0], stringSize + 1, &stringSize); 802 return valueString; 803 } 804 805 napi_value PluginManager::BindNode(napi_env env, napi_callback_info info) { 806 size_t argc = 2; 807 napi_value args[2] = {nullptr}; 808 napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); 809 std::string nodeId = value2String(env, args[0]); 810 ArkUI_NodeHandle handle; 811 OH_ArkUI_GetNodeHandleFromNapiValue(env, args[1], &handle); // 获取nodeHandle 812 OH_ArkUI_SurfaceHolder *holder = OH_ArkUI_SurfaceHolder_Create(handle); // 获取SurfaceHolder 813 nodeHandleMap_[nodeId] = handle; 814 surfaceHolderMap_[handle] = holder; 815 auto callback = OH_ArkUI_SurfaceCallback_Create(); // 创建SurfaceCallback 816 callbackMap_[holder] = callback; 817 OH_ArkUI_SurfaceCallback_SetSurfaceCreatedEvent(callback, OnSurfaceCreated); // 注册OnSurfaceCreated回调 818 OH_ArkUI_SurfaceCallback_SetSurfaceChangedEvent(callback, OnSurfaceChanged); // 注册OnSurfaceChanged回调 819 OH_ArkUI_SurfaceCallback_SetSurfaceDestroyedEvent(callback, OnSurfaceDestroyed); // 注册OnSurfaceDestroyed回调 820 OH_ArkUI_SurfaceCallback_SetSurfaceShowEvent(callback, OnSurfaceShow); // 注册OnSurfaceShow回调 821 OH_ArkUI_SurfaceCallback_SetSurfaceHideEvent(callback, OnSurfaceHide); // 注册OnSurfaceHide回调 822 OH_ArkUI_XComponent_RegisterOnFrameCallback(handle, OnFrameCallback); // 注册OnFrameCallback回调 823 OH_ArkUI_SurfaceHolder_AddSurfaceCallback(holder, callback); // 注册SurfaceCallback回调 824 if (!nodeAPI->addNodeEventReceiver(handle, onEvent)) { // 添加事件监听,返回成功码 0 825 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "onBind", "addNodeEventReceiver error"); 826 } 827 if (!nodeAPI->registerNodeEvent(handle, NODE_TOUCH_EVENT, 0, nullptr)) { // 用C接口注册touch事件,返回成功码 0 828 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "onBind", "registerTouchEvent error"); 829 } 830 provider_ = OH_ArkUI_AccessibilityProvider_Create(handle); // 创建一个ArkUI_AccessibilityProvider类型的对象 831 /** 832 * 获取ArkUI_AccessibilityProvider后,如果注册无障碍回调函数请参考: 833 * https://gitcode.com/openharmony/docs/blob/OpenHarmony-5.1.0-Release/zh-cn/application-dev/ui/ndk-accessibility-xcomponent.md 834 * **/ 835 return nullptr; 836 } 837 838 napi_value PluginManager::UnbindNode(napi_env env, napi_callback_info info) 839 { 840 size_t argc = 1; 841 napi_value args[1] = {nullptr}; 842 napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); 843 std::string nodeId = value2String(env, args[0]); 844 auto node = nodeHandleMap_[nodeId]; 845 OH_ArkUI_XComponent_UnregisterOnFrameCallback(node); // 解注册帧回调 846 OH_ArkUI_AccessibilityProvider_Dispose(provider_); // 销毁ArkUI_AccessibilityProvider 847 nodeAPI->disposeNode(node); // 销毁nodeHandle 848 nodeHandleMap_.erase(nodeId); 849 return nullptr; 850 } 851 852 napi_value PluginManager::SetFrameRate(napi_env env, napi_callback_info info) 853 { 854 size_t argc = 4; 855 napi_value args[4] = {nullptr}; 856 napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); 857 std::string nodeId = value2String(env, args[0]); 858 auto node = nodeHandleMap_[nodeId]; 859 860 int32_t min = 0; 861 napi_get_value_int32( env, args[1], &min); 862 863 int32_t max = 0; 864 napi_get_value_int32(env, args[2], &max); 865 866 int32_t expected = 0; 867 napi_get_value_int32(env, args[3], &expected); 868 OH_NativeXComponent_ExpectedRateRange range = { 869 .min = min, 870 .max = max, 871 .expected = expected 872 }; 873 OH_ArkUI_XComponent_SetExpectedFrameRateRange(node, range); // 设置期望帧率 874 return nullptr; 875 } 876 877 napi_value PluginManager::SetNeedSoftKeyboard(napi_env env, napi_callback_info info) 878 { 879 size_t argc = 2; 880 napi_value args[2] = {nullptr}; 881 napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); 882 std::string nodeId = value2String(env, args[0]); 883 auto node = nodeHandleMap_[nodeId]; 884 885 bool needSoftKeyboard = false; 886 napi_get_value_bool( env, args[1], &needSoftKeyboard); 887 OH_ArkUI_XComponent_SetNeedSoftKeyboard(node, needSoftKeyboard); // 设置是否需要软键盘 888 return nullptr; 889 } 890 ``` 891 892 (2) 定义Surface创建成功,发生改变,销毁和事件,可变帧率回调接口。 893 894 ```c++ 895 void OnSurfaceCreated(OH_ArkUI_SurfaceHolder *holder) { 896 auto window = OH_ArkUI_XComponent_GetNativeWindow(holder); // 获取native window 897 auto render = new EGLRender(); 898 PluginManager::renderMap_[holder] = render; 899 render->SetUpEGLContext(window); 900 } 901 902 void OnSurfaceChanged(OH_ArkUI_SurfaceHolder *holder, uint64_t width, uint64_t height) { 903 if (PluginManager::renderMap_.count(holder)) { 904 auto render = PluginManager::renderMap_[holder]; 905 render->SetEGLWindowSize(width, height); // 设置绘制区域大小 906 render->DrawStar(true); // 绘制五角星 907 } 908 } 909 910 void OnSurfaceDestroyed(OH_ArkUI_SurfaceHolder *holder) { 911 OH_LOG_Print(LOG_APP, LOG_ERROR, 0xff00, "onBind", "on destroyed"); 912 if (PluginManager::renderMap_.count(holder)) { // 销毁render对象 913 auto render = PluginManager::renderMap_[holder]; 914 delete render; 915 PluginManager::renderMap_.erase(holder); 916 } 917 if (PluginManager::callbackMap_.count(holder)) { 918 auto callback = PluginManager::callbackMap_[holder]; 919 OH_ArkUI_SurfaceHolder_RemoveSurfaceCallback(holder, callback); // 移除SurfaceCallback 920 OH_ArkUI_SurfaceCallback_Dispose(callback); // 销毁surfaceCallback 921 PluginManager::callbackMap_.erase(holder); 922 } 923 OH_ArkUI_SurfaceHolder_Dispose(holder); // 销毁surfaceHolder 924 } 925 926 void OnSurfaceShow(OH_ArkUI_SurfaceHolder* holder) 927 { 928 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "onBind", "on surface show"); 929 } 930 931 void OnSurfaceHide(OH_ArkUI_SurfaceHolder* holder) 932 { 933 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "onBind", "on surface hide"); 934 } 935 936 void OnFrameCallback(ArkUI_NodeHandle node, uint64_t timestamp, uint64_t targetTimestamp) 937 { 938 if (!PluginManager::surfaceHolderMap_.count(node)) { 939 return; 940 } 941 static uint64_t count = 0; 942 count++; 943 if (count % 50 == 0) { 944 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "onBind", "OnFrameCallback count = %{public}ld", count); 945 } 946 } 947 948 void onEvent(ArkUI_NodeEvent *event) { 949 auto eventType = OH_ArkUI_NodeEvent_GetEventType(event); // 获取组件事件类型 950 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "onBind", "on event"); 951 if (eventType == NODE_TOUCH_EVENT) { 952 ArkUI_NodeHandle handle = OH_ArkUI_NodeEvent_GetNodeHandle(event); // 获取触发该事件的组件对象 953 auto holder = PluginManager::surfaceHolderMap_[handle]; 954 if (PluginManager::renderMap_.count(holder)) { 955 auto render = PluginManager::renderMap_[holder]; 956 render->DrawStar(false); // 绘制五角星 957 } 958 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "onBind", "on touch"); 959 } 960 } 961 ``` 962 9634. 初始化环境,包括初始化可用的EGLDisplay、确定可用的Surface配置、创建渲染区域Surface、创建并关联上下文等。 964 965 ```c++ 966 // EGLConst.h 967 #include <EGL/egl.h> 968 #include <EGL/eglext.h> 969 #include <GLES3/gl3.h> 970 971 const unsigned int LOG_PRINT_DOMAIN = 0xFF00; 972 973 /** 974 * Program 错误。 975 */ 976 const GLuint PROGRAM_ERROR = 0; 977 978 /** 979 * 位置错误。 980 */ 981 const GLint POSITION_ERROR = -1; 982 983 /** 984 * 默认x坐标。 985 */ 986 const int DEFAULT_X_POSITION = 0; 987 988 /** 989 * 默认y坐标。 990 */ 991 const int DEFAULT_Y_POSITION = 0; 992 993 /** 994 * Gl 红色默认值。 995 */ 996 const GLfloat GL_RED_DEFAULT = 0.0; 997 998 /** 999 * Gl 绿色默认值。 1000 */ 1001 const GLfloat GL_GREEN_DEFAULT = 0.0; 1002 1003 /** 1004 * Gl 蓝色默认值。 1005 */ 1006 const GLfloat GL_BLUE_DEFAULT = 0.0; 1007 1008 /** 1009 * Gl 透明度。 1010 */ 1011 const GLfloat GL_ALPHA_DEFAULT = 1.0; 1012 1013 /** 1014 * Pointer 数量。 1015 */ 1016 const GLint POINTER_SIZE = 2; 1017 1018 /** 1019 * Triangle fan 尺寸。 1020 */ 1021 const GLsizei TRIANGLE_FAN_SIZE = 4; 1022 1023 /** 1024 * 50%。 1025 */ 1026 const float FIFTY_PERCENT = 0.5; 1027 1028 /** 1029 * 位置句柄名字。 1030 */ 1031 const char POSITION_NAME[] = "a_position"; 1032 1033 /** 1034 * 背景色 #f4f4f4。 1035 */ 1036 const GLfloat BACKGROUND_COLOR[] = {244.0f / 255, 244.0f / 255, 244.0f / 255, 1.0f}; 1037 1038 /** 1039 * Draw 颜色 #7E8FFB。 1040 */ 1041 const GLfloat DRAW_COLOR[] = {126.0f / 255, 143.0f / 255, 251.0f / 255, 1.0f}; 1042 1043 /** 1044 * Change 颜色 #92D6CC。 1045 */ 1046 const GLfloat CHANGE_COLOR[] = {146.0f / 255, 214.0f / 255, 204.0f / 255, 1.0f}; 1047 1048 /** 1049 * 背景区域。 1050 */ 1051 const GLfloat BACKGROUND_RECTANGLE_VERTICES[] = {-1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, -1.0f, -1.0f}; 1052 1053 const EGLint ATTRIB_LIST[] = { 1054 // 键,值。 1055 EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8, 1056 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, 1057 // 结束。 1058 EGL_NONE}; 1059 1060 const EGLint CONTEXT_ATTRIBS[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE}; 1061 1062 /** 1063 * 顶点着色器。 1064 */ 1065 const char VERTEX_SHADER[] = "#version 300 es\n" 1066 "layout(location = 0) in vec4 a_position;\n" 1067 "layout(location = 1) in vec4 a_color; \n" 1068 "out vec4 v_color; \n" 1069 "void main() \n" 1070 "{ \n" 1071 " gl_Position = a_position; \n" 1072 " v_color = a_color; \n" 1073 "} \n"; 1074 1075 /** 1076 * 片元着色器。 1077 */ 1078 const char FRAGMENT_SHADER[] = "#version 300 es\n" 1079 "precision mediump float; \n" 1080 "in vec4 v_color; \n" 1081 "out vec4 fragColor; \n" 1082 "void main() \n" 1083 "{ \n" 1084 " fragColor = v_color; \n" 1085 "} \n"; 1086 ``` 1087 1088 ```c++ 1089 // EGLRender.h 1090 #include "EGLConst.h" 1091 #include <EGL/egl.h> 1092 #include <EGL/eglext.h> 1093 #include <EGL/eglplatform.h> 1094 #include <GLES3/gl3.h> 1095 #include <string> 1096 1097 class EGLRender { 1098 public: 1099 bool SetUpEGLContext(void *window); 1100 void SetEGLWindowSize(int width, int height); 1101 void DrawStar(bool drawColor); 1102 1103 std::string xcomponentId; 1104 EGLNativeWindowType eglWindow_; 1105 1106 EGLDisplay eglDisplay_ = EGL_NO_DISPLAY; 1107 EGLConfig eglConfig_ = EGL_NO_CONFIG_KHR; 1108 EGLSurface eglSurface_ = EGL_NO_SURFACE; 1109 EGLContext eglContext_ = EGL_NO_CONTEXT; 1110 GLuint program_; 1111 int width_ = 0; 1112 int height_ = 0; 1113 ~EGLRender(); 1114 1115 private: 1116 GLint PrepareDraw(); 1117 bool ExecuteDraw(GLint position, const GLfloat *color, const GLfloat shapeVertices[]); 1118 }; 1119 ``` 1120 1121 ```c++ 1122 // EGLRender.cpp 1123 #include "EGLRender.h" 1124 #include "EGLConst.h" 1125 #include <EGL/egl.h> 1126 #include <EGL/eglext.h> 1127 #include <GLES3/gl3.h> 1128 #include <cmath> 1129 #include <cstdio> 1130 #include <algorithm> 1131 #include <hilog/log.h> 1132 #include <iostream> 1133 1134 namespace { 1135 void Rotate2d(GLfloat centerX, GLfloat centerY, GLfloat *rotateX, GLfloat *rotateY, GLfloat theta) { 1136 GLfloat tempX = cos(theta) * (*rotateX - centerX) - sin(theta) * (*rotateY - centerY); 1137 GLfloat tempY = sin(theta) * (*rotateX - centerX) + cos(theta) * (*rotateY - centerY); 1138 *rotateX = tempX + centerX; 1139 *rotateY = tempY + centerY; 1140 } 1141 1142 GLuint LoadShader(GLenum type, const char *shaderSrc) { 1143 if ((type == 0) || (shaderSrc == nullptr)) { 1144 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLRender", "glCreateShader type or shaderSrc error"); 1145 return PROGRAM_ERROR; 1146 } 1147 1148 GLuint shader = glCreateShader(type); 1149 if (shader == 0) { 1150 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLRender", "glCreateShader unable to load shader"); 1151 return PROGRAM_ERROR; 1152 } 1153 1154 // The gl function has no return value. 1155 glShaderSource(shader, 1, &shaderSrc, nullptr); 1156 glCompileShader(shader); 1157 1158 GLint compiled; 1159 glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled); 1160 if (compiled != 0) { 1161 return shader; 1162 } 1163 1164 GLint infoLen = 0; 1165 glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen); 1166 if (infoLen <= 1) { 1167 glDeleteShader(shader); 1168 return PROGRAM_ERROR; 1169 } 1170 1171 char *infoLog = (char *)malloc(sizeof(char) * (infoLen + 1)); 1172 if (infoLog != nullptr) { 1173 memset(infoLog, 0, infoLen + 1); 1174 glGetShaderInfoLog(shader, infoLen, nullptr, infoLog); 1175 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLRender", "glCompileShader error = %s", infoLog); 1176 free(infoLog); 1177 infoLog = nullptr; 1178 } 1179 glDeleteShader(shader); 1180 return PROGRAM_ERROR; 1181 } 1182 1183 // 创建program 1184 GLuint CreateProgram(const char *vertexShader, const char *fragShader) { 1185 if ((vertexShader == nullptr) || (fragShader == nullptr)) { 1186 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLRender", 1187 "createProgram: vertexShader or fragShader is null"); 1188 return PROGRAM_ERROR; 1189 } 1190 1191 GLuint vertex = LoadShader(GL_VERTEX_SHADER, vertexShader); 1192 if (vertex == PROGRAM_ERROR) { 1193 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLRender", "createProgram vertex error"); 1194 return PROGRAM_ERROR; 1195 } 1196 1197 GLuint fragment = LoadShader(GL_FRAGMENT_SHADER, fragShader); 1198 if (fragment == PROGRAM_ERROR) { 1199 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLRender", "createProgram fragment error"); 1200 return PROGRAM_ERROR; 1201 } 1202 1203 GLuint program = glCreateProgram(); 1204 if (program == PROGRAM_ERROR) { 1205 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLRender", "createProgram program error"); 1206 glDeleteShader(vertex); 1207 glDeleteShader(fragment); 1208 return PROGRAM_ERROR; 1209 } 1210 1211 // 该gl函数没有返回值。 1212 glAttachShader(program, vertex); 1213 glAttachShader(program, fragment); 1214 glLinkProgram(program); 1215 1216 GLint linked; 1217 glGetProgramiv(program, GL_LINK_STATUS, &linked); 1218 if (linked != 0) { 1219 glDeleteShader(vertex); 1220 glDeleteShader(fragment); 1221 return program; 1222 } 1223 1224 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLRender", "createProgram linked error"); 1225 GLint infoLen = 0; 1226 glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLen); 1227 if (infoLen > 1) { 1228 char *infoLog = (char *)malloc(sizeof(char) * (infoLen + 1)); 1229 memset(infoLog, 0, infoLen + 1); 1230 glGetProgramInfoLog(program, infoLen, nullptr, infoLog); 1231 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLRender", "glLinkProgram error = %s", infoLog); 1232 free(infoLog); 1233 infoLog = nullptr; 1234 } 1235 glDeleteShader(vertex); 1236 glDeleteShader(fragment); 1237 glDeleteProgram(program); 1238 return PROGRAM_ERROR; 1239 } 1240 } // namespace 1241 1242 bool EGLRender::SetUpEGLContext(void *window) { 1243 OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "EGLRender", "EglContextInit execute"); 1244 eglWindow_ = (EGLNativeWindowType)(window); 1245 // 初始化display。 1246 eglDisplay_ = eglGetDisplay(EGL_DEFAULT_DISPLAY); 1247 if (eglDisplay_ == EGL_NO_DISPLAY) { 1248 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLRender", "eglGetDisplay: unable to get EGL display"); 1249 return false; 1250 } 1251 EGLint majorVersion; 1252 EGLint minorVersion; 1253 if (!eglInitialize(eglDisplay_, &majorVersion, &minorVersion)) { 1254 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLRender", 1255 "eglInitialize: unable to get initialize EGL display"); 1256 return false; 1257 }; 1258 // 选择配置。 1259 const EGLint maxConfigSize = 1; 1260 EGLint numConfigs; 1261 if (!eglChooseConfig(eglDisplay_, ATTRIB_LIST, &eglConfig_, maxConfigSize, &numConfigs)) { 1262 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLRender", "eglChooseConfig: unable to choose configs"); 1263 return false; 1264 }; 1265 // 创建环境。 1266 // 创建 Surface。 1267 eglSurface_ = eglCreateWindowSurface(eglDisplay_, eglConfig_, eglWindow_, NULL); 1268 if (eglSurface_ == nullptr) { 1269 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLRender", 1270 "eglCreateWindowSurface: unable to create Surface"); 1271 return false; 1272 } 1273 // 创建上下文。 1274 eglContext_ = eglCreateContext(eglDisplay_, eglConfig_, EGL_NO_CONTEXT, CONTEXT_ATTRIBS); 1275 if (!eglMakeCurrent(eglDisplay_, eglSurface_, eglSurface_, eglContext_)) { 1276 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLRender", "eglMakeCurrent failed"); 1277 return false; 1278 } 1279 // 创建program。 1280 program_ = CreateProgram(VERTEX_SHADER, FRAGMENT_SHADER); 1281 if (program_ == PROGRAM_ERROR) { 1282 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLRender", "CreateProgram: unable to create program"); 1283 return false; 1284 } 1285 return true; 1286 } 1287 1288 GLint EGLRender::PrepareDraw() { 1289 if ((eglDisplay_ == nullptr) || (eglSurface_ == nullptr) || (eglContext_ == nullptr) || 1290 (!eglMakeCurrent(eglDisplay_, eglSurface_, eglSurface_, eglContext_))) { 1291 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLRender", "PrepareDraw: param error"); 1292 return POSITION_ERROR; 1293 } 1294 1295 // 该gl函数没有返回值。 1296 glViewport(DEFAULT_X_POSITION, DEFAULT_Y_POSITION, width_, height_); 1297 glClearColor(GL_RED_DEFAULT, GL_GREEN_DEFAULT, GL_BLUE_DEFAULT, GL_ALPHA_DEFAULT); 1298 glClear(GL_COLOR_BUFFER_BIT); 1299 glUseProgram(program_); 1300 1301 return glGetAttribLocation(program_, POSITION_NAME); 1302 } 1303 1304 // 绘制五角星 1305 void EGLRender::DrawStar(bool drawColor) { 1306 OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "EGLRender", "Draw"); 1307 GLint position = PrepareDraw(); 1308 if (position == POSITION_ERROR) { 1309 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLRender", "Draw get position failed"); 1310 return; 1311 } 1312 1313 // 绘制背景 1314 if (!ExecuteDraw(position, BACKGROUND_COLOR, BACKGROUND_RECTANGLE_VERTICES)) { 1315 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLRender", "Draw execute draw background failed"); 1316 return; 1317 } 1318 1319 // 将其划分为五个四边形,并计算其中一个四边形的顶点 1320 GLfloat rotateX = 0; 1321 GLfloat rotateY = FIFTY_PERCENT * height_; 1322 GLfloat centerX = 0; 1323 // 将角度 54° 和 18° 转换为弧度 1324 GLfloat centerY = -rotateY * (M_PI / 180 * 54) * (M_PI / 180 * 18); 1325 // 将角度 18° 转换为弧度 1326 GLfloat leftX = -rotateY * (M_PI / 180 * 18); 1327 GLfloat leftY = 0; 1328 // 将角度 18° 转换为弧度 1329 GLfloat rightX = rotateY * (M_PI / 180 * 18); 1330 GLfloat rightY = 0; 1331 1332 // 确定绘制四边形的顶点,使用绘制区域的百分比表示 1333 const GLfloat shapeVertices[] = {centerX / width_, centerY / height_, leftX / width_, leftY / height_, 1334 rotateX / width_, rotateY / height_, rightX / width_, rightY / height_}; 1335 auto color = drawColor ? DRAW_COLOR : CHANGE_COLOR; 1336 if (!ExecuteDraw(position, color, shapeVertices)) { 1337 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLRender", "Draw execute draw shape failed"); 1338 return; 1339 } 1340 1341 // 将角度 72° 转换为弧度 1342 GLfloat rad = M_PI / 180 * 72; 1343 // 旋转四次。 1344 for (int i = 0; i < 4; ++i) { 1345 // 旋转得其他四个四边形的顶点 1346 Rotate2d(centerX, centerY, &rotateX, &rotateY, rad); 1347 Rotate2d(centerX, centerY, &leftX, &leftY, rad); 1348 Rotate2d(centerX, centerY, &rightX, &rightY, rad); 1349 1350 // 确定绘制四边形的顶点,使用绘制区域的百分比表示 1351 const GLfloat shapeVertices[] = {centerX / width_, centerY / height_, leftX / width_, leftY / height_, 1352 rotateX / width_, rotateY / height_, rightX / width_, rightY / height_}; 1353 1354 // 绘制图形 1355 if (!ExecuteDraw(position, color, shapeVertices)) { 1356 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLRender", "Draw execute draw shape failed"); 1357 return; 1358 } 1359 } 1360 // 将绘制命令提交给GPU,GPU执行完成后将渲染结果显示到屏幕 1361 glFlush(); 1362 glFinish(); 1363 if (!eglSwapBuffers(eglDisplay_, eglSurface_)) { 1364 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLRender", "Draw FinishDraw failed"); 1365 return; 1366 } 1367 } 1368 1369 bool EGLRender::ExecuteDraw(GLint position, const GLfloat *color, const GLfloat shapeVertices[]) { 1370 if ((position > 0) || (color == nullptr)) { 1371 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLRender", "ExecuteDraw: param error"); 1372 return false; 1373 } 1374 1375 // 该gl函数没有返回值。 1376 glVertexAttribPointer(position, POINTER_SIZE, GL_FLOAT, GL_FALSE, 0, shapeVertices); 1377 glEnableVertexAttribArray(position); 1378 glVertexAttrib4fv(1, color); 1379 glDrawArrays(GL_TRIANGLE_FAN, 0, TRIANGLE_FAN_SIZE); 1380 glDisableVertexAttribArray(position); 1381 1382 return true; 1383 } 1384 1385 void EGLRender::SetEGLWindowSize(int width, int height) { 1386 width_ = width; 1387 height_ = height; 1388 } 1389 1390 // 释放相关资源 1391 EGLRender::~EGLRender() { 1392 if ((eglDisplay_ == nullptr) || (eglSurface_ == nullptr) || (!eglDestroySurface(eglDisplay_, eglSurface_))) { 1393 OH_LOG_Print(LOG_APP, LOG_ERROR, 0xff00, "EGLRender", "Release eglDestroySurface failed"); 1394 } 1395 1396 if ((eglDisplay_ == nullptr) || (eglContext_ == nullptr) || (!eglDestroyContext(eglDisplay_, eglContext_))) { 1397 OH_LOG_Print(LOG_APP, LOG_ERROR, 0xff00, "EGLRender", "Release eglDestroySurface failed"); 1398 } 1399 1400 if ((eglDisplay_ == nullptr) || (!eglTerminate(eglDisplay_))) { 1401 OH_LOG_Print(LOG_APP, LOG_ERROR, 0xff00, "EGLRender", "Release eglDestroySurface failed"); 1402 } 1403 } 1404 ``` 1405 14065. CMakeLists,使用CMake工具链将C++源代码编译成动态链接库文件。 1407 1408 ```CMake 1409 # the minimum version of CMake. 1410 cmake_minimum_required(VERSION 3.5.0) 1411 project(LCNXComponent2) 1412 1413 set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR}) 1414 1415 if(DEFINED PACKAGE_FIND_FILE) 1416 include(${PACKAGE_FIND_FILE}) 1417 endif() 1418 1419 include_directories(${NATIVERENDER_ROOT_PATH} 1420 ${NATIVERENDER_ROOT_PATH}/render 1421 ${NATIVERENDER_ROOT_PATH}/manager) 1422 1423 add_library(nativerender SHARED 1424 render/EGLRender.cpp 1425 manager/plugin_manager.cpp 1426 napi_init.cpp) 1427 find_library( 1428 # 设置路径变量的名称。 1429 EGL-lib 1430 # 指定要让CMake查找的NDK库的名称。 1431 EGL 1432 ) 1433 1434 find_library( 1435 # 设置路径变量的名称。 1436 GLES-lib 1437 # 指定要让CMake查找的NDK库的名称。 1438 GLESv3 1439 ) 1440 1441 find_library( 1442 # 设置路径变量的名称。 1443 hilog-lib 1444 # 指定要让CMake查找的NDK库的名称。 1445 hilog_ndk.z 1446 ) 1447 1448 find_library( 1449 # 设置路径变量的名称。 1450 libace-lib 1451 # 指定要让CMake查找的NDK库的名称。 1452 ace_ndk.z 1453 ) 1454 1455 find_library( 1456 # 设置路径变量的名称。 1457 libnapi-lib 1458 # 指定要让CMake查找的NDK库的名称。 1459 ace_napi.z 1460 ) 1461 1462 find_library( 1463 # 设置路径变量的名称。 1464 libuv-lib 1465 # 指定要让CMake查找的NDK库的名称。 1466 uv 1467 ) 1468 1469 target_link_libraries(nativerender PUBLIC ${EGL-lib} ${GLES-lib} ${hilog-lib} ${libace-lib} ${libnapi-lib} ${libuv-lib} libnative_window.so) 1470 ``` 1471 1472<!--RP3-->上述用例具体实现可参考[NativeXComponent](https://gitcode.com/openharmony/applications_app_samples/tree/master/code/BasicFeature/Native/NativeXComponent)。<!--RP3End--> 1473 1474 1475 1476## 使用NativeXComponent管理Surface生命周期 1477 1478与上述两种场景不同,本场景在Native侧使用ArkUI NDK 接口创建XComponent组件进行自定义绘制。具体步骤包括:创建组件,获取NativeXComponent实例,注册XComponent的生命周期回调及触摸、鼠标、按键等事件回调,通过回调获取NativeWindow,使用OpenGL ES/EGL接口在XComponent组件上进行图形绘制,最后在ArkTS层使用ContentSlot占位组件进行挂载显示。针对Native侧创建XComponent的主要开发场景如下: 1479 1480- 利用Native XComponent提供的接口注册XComponent的生命周期和事件回调。 1481- 在这些回调中进行初始化环境、获取当前状态、响应各类事件的开发。 1482- 利用NativeWindow和EGL接口开发自定义绘制内容以及申请和提交Buffer到图形队列。 1483 1484**约束条件**: 1485 1486构造XComponent时需要根据需要的type使用对应的节点类型。 1487 1488> **说明**: 1489> 1490> 1. Native侧的OH_NativeXComponent缓存在字典中,其key需要保证其唯一性,当对应的XComponent销毁后,需要及时从字典里将其删除。 1491> 1492> 2. 多个XComponent开发时,缓存Native侧资源需要保证key是唯一的,key推荐使用id+随机数或者surfaceId。 1493 1494**生命周期**: 1495 1496- OnSurfaceCreated回调 1497 1498 触发时刻:XComponent创建完成且创建好Surface后触发。 1499 1500 Native侧OnSurfaceCreated的时序如下图: 1501 1502  1503 1504- OnSurfaceChanged回调 1505 1506 触发时刻:Surface大小变化触发重新布局后触发。 1507 1508 Native侧OnSurfaceChanged的时序如下图: 1509 1510  1511 1512- OnSurfaceDestroyed回调 1513 1514 触发时刻:XComponent组件被销毁时触发,与一般ArkUI的组件销毁时机一致。 1515 1516 Native侧OnSurfaceDestroyed的时序图: 1517 1518  1519 1520**接口说明** 1521 1522| 接口名 | 描述 | 1523| ------------------------------------------------------------ | ------------------------------------------------------------ | 1524| OH_NativeXComponent_GetXComponentId(OH_NativeXComponent* component, char* id, uint64_t* size) | 获取XComponent的id。 | 1525| OH_NativeXComponent_GetXComponentSize(OH_NativeXComponent* component, const void* window, uint64_t* width, uint64_t* height) | 获取XComponent持有的Surface的大小。 | 1526| OH_NativeXComponent_GetXComponentOffset(OH_NativeXComponent* component, const void* window, double* x, double* y) | 获取XComponent持有的Surface相对其父组件左顶点的偏移量。 | 1527| OH_NativeXComponent_GetTouchEvent(OH_NativeXComponent* component, const void* window, OH_NativeXComponent_TouchEvent* touchEvent) | 获取由XComponent触发的触摸事件。touchEvent内的具体属性值可参考[OH_NativeXComponent_TouchEvent](../reference/apis-arkui/capi-oh-nativexcomponent-native-xcomponent-oh-nativexcomponent-touchevent.md)。 | 1528| OH_NativeXComponent_GetTouchPointToolType(OH_NativeXComponent* component, uint32_t pointIndex, OH_NativeXComponent_TouchPointToolType* toolType) | 获取XComponent触摸点的工具类型。 | 1529| OH_NativeXComponent_GetTouchPointTiltX(OH_NativeXComponent* component, uint32_t pointIndex, float* tiltX) | 获取XComponent触摸点处相对X轴的倾斜角度。 | 1530| OH_NativeXComponent_GetTouchPointTiltY(OH_NativeXComponent* component, uint32_t pointIndex, float* tiltY) | 获取XComponent触摸点处相对Y轴的倾斜角度。 | 1531| OH_NativeXComponent_GetMouseEvent(OH_NativeXComponent* component, const void* window, OH_NativeXComponent_MouseEvent* mouseEvent) | 获取由XComponent触发的鼠标事件。 | 1532| OH_NativeXComponent_RegisterCallback(OH_NativeXComponent* component, OH_NativeXComponent_Callback* callback) | 为此OH_NativeXComponent实例注册生命周期和触摸事件回调。 | 1533| OH_NativeXComponent_RegisterMouseEventCallback(OH_NativeXComponent* component, OH_NativeXComponent_MouseEvent_Callback* callback) | 为此OH_NativeXComponent实例注册鼠标事件回调。 | 1534| OH_NativeXComponent_RegisterFocusEventCallback(OH_NativeXComponent* component, void (\*callback)(OH_NativeXComponent* component, void* window)) | 为此OH_NativeXComponent实例注册获得焦点事件回调。 | 1535| OH_NativeXComponent_RegisterKeyEventCallback(OH_NativeXComponent* component, void (\*callback)(OH_NativeXComponent* component, void* window)) | 为此OH_NativeXComponent实例注册按键事件回调。 | 1536| OH_NativeXComponent_RegisterBlurEventCallback(OH_NativeXComponent* component, void (\*callback)(OH_NativeXComponent* component, void* window)) | 为此OH_NativeXComponent实例注册失去焦点事件回调。 | 1537| OH_NativeXComponent_GetKeyEvent(OH_NativeXComponent* component, OH_NativeXComponent_KeyEvent\** keyEvent) | 获取由XComponent触发的按键事件。 | 1538| OH_NativeXComponent_GetKeyEventAction(OH_NativeXComponent_KeyEvent* keyEvent, OH_NativeXComponent_KeyAction* action) | 获取按键事件的动作。 | 1539| OH_NativeXComponent_GetKeyEventCode(OH_NativeXComponent_KeyEvent* keyEvent, OH_NativeXComponent_KeyCode* code) | 获取按键事件的键码值。 | 1540| OH_NativeXComponent_GetKeyEventSourceType(OH_NativeXComponent_KeyEvent* keyEvent, OH_NativeXComponent_EventSourceType* sourceType) | 获取按键事件的输入源类型。 | 1541| OH_NativeXComponent_GetKeyEventDeviceId(OH_NativeXComponent_KeyEvent* keyEvent, int64_t* deviceId) | 获取按键事件的设备ID。 | 1542| OH_NativeXComponent_GetKeyEventTimestamp(OH_NativeXComponent_KeyEvent* keyEvent, int64_t* timestamp) | 获取按键事件的时间戳。 | 1543| OH_ArkUI_QueryModuleInterfaceByName(ArkUI_NativeAPIVariantKind type, const char* structName) | 获取指定类型的Native模块接口集合。 | 1544| OH_ArkUI_GetNodeContentFromNapiValue(napi_env env, napi_value value, ArkUI_NodeContentHandle* content) | 获取ArkTS侧创建的NodeContent对象映射到Native侧的ArkUI_NodeContentHandle。 | 1545| OH_ArkUI_NodeContent_SetUserData(ArkUI_NodeContentHandle content, void* userData) | 在NodeContent对象上保存自定义数据。 | 1546| OH_ArkUI_NodeContentEvent_GetNodeContentHandle(ArkUI_NodeContentEvent* event) | 获取触发事件的NodeContent对象。 | 1547| OH_ArkUI_NodeContent_GetUserData(ArkUI_NodeContentHandle content) | 获取在NodeContent对象上保存的自定义数据。 | 1548| OH_ArkUI_NodeContentEvent_GetEventType(ArkUI_NodeContentEvent* event) | 获取触发NodeContent事件的事件类型。 | 1549| OH_ArkUI_NodeContent_AddNode(ArkUI_NodeContentHandle content, ArkUI_NodeHandle node) | 将一个ArkUI组件节点添加到对应的NodeContent对象下。 | 1550| OH_ArkUI_NodeContent_RegisterCallback(ArkUI_NodeContentHandle content, ArkUI_NodeContentCallback callback) | 注册NodeContent事件函数。 | 1551| OH_NativeXComponent_GetNativeXComponent(ArkUI_NodeHandle node) | 基于Native接口创建的组件实例获取OH_NativeXComponent类型的指针。 | 1552| OH_NativeXComponent_GetHistoricalPoints(OH_NativeXComponent* component, const void* window, int32_t* size, OH_NativeXComponent_HistoricalPoint** historicalPoints ) | 获取当前XComponent触摸事件的历史点信息。由于部分输入设备上报触点的频率非常高(最高可达每1 ms上报一次),而对输入事件的响应通常是为了使UI界面发生变化以响应用户操作,如果将触摸事件按照上报触点的频率如此高频率上报给应用,大多会造成冗余,因此触摸事件在一帧内只会上报一次给应用。在当前帧内上报的触点均作为历史点保存,如果应用需要直接处理这些数据,可调用该接口获取历史点信息。历史接触点historicalPoints的具体规格可参考[重采样与历史点](arkts-interaction-development-guide-touch-screen.md#重采样与历史点)。 | 1553 1554> **说明 :** 1555> 1556> 上述接口不支持跨线程访问。 1557> 1558> XComponent销毁(onSurfaceDestroyed回调触发后)时会释放上述接口中获取的OH_NativeXComponent和window对象。如果再次使用获取的对象,有可能会导致使用野指针或空指针的崩溃问题。 1559 1560**开发步骤** 1561 1562以下步骤以SURFACE类型为例,描述了如何使用`XComponent组件`调用`Node-API`接口来创建`EGL/GLES`环境,实现在主页面绘制图形,并可以改变图形的颜色。 1563 15641. 在界面中定义XComponent。 1565 1566 ```typescript 1567 import nativeNode from 'libnativenode.so'; 1568 import {NodeContent} from '@kit.ArkUI'; 1569 1570 @Entry 1571 @Component 1572 struct Index { 1573 @State currentStatus: string = "init"; 1574 private nodeContent: NodeContent = new NodeContent(); 1575 aboutToAppear():void{ 1576 // 通过C-API创建节点,并添加到管理器nodeContent上 1577 nativeNode.createNativeNode(this.nodeContent); 1578 } 1579 1580 build() { 1581 Column() { 1582 Row() { 1583 Text('Native XComponent Sample') 1584 .fontSize('24fp') 1585 .fontWeight(500) 1586 .margin({ 1587 left: 24, 1588 top: 12 1589 }) 1590 } 1591 .margin({ top: 24 }) 1592 .width('100%') 1593 .height(56) 1594 1595 Column({ space: 10 }) { 1596 // 显示nodeContent管理器里存放的Native侧的组件 1597 ContentSlot(this.nodeContent); 1598 1599 Text(this.currentStatus) 1600 .fontSize('24fp') 1601 .fontWeight(500) 1602 } 1603 .onClick(() => { 1604 let hasChangeColor: boolean = false; 1605 // 获取当前绘制内容状态 1606 if (nativeNode.getStatus()) { 1607 hasChangeColor = nativeNode.getStatus().hasChangeColor; 1608 } 1609 if (hasChangeColor) { 1610 this.currentStatus = "change color"; 1611 } 1612 }) 1613 .margin({ 1614 top: 27, 1615 left: 12, 1616 right: 12 1617 }) 1618 .height('40%') 1619 .width('90%') 1620 1621 Row() { 1622 Button('Draw Star') 1623 .fontSize('16fp') 1624 .fontWeight(500) 1625 .margin({ bottom: 24 }) 1626 .onClick(() => { 1627 // 调用drawPattern绘制内容 1628 nativeNode.drawPattern(); 1629 let hasDraw: boolean = false; 1630 // 获取当前绘制内容状态 1631 if (nativeNode.getStatus()) { 1632 hasDraw = nativeNode.getStatus().hasDraw; 1633 } 1634 if (hasDraw) { 1635 this.currentStatus = "draw star"; 1636 } 1637 }) 1638 .width('53.6%') 1639 .height(40) 1640 } 1641 .width('100%') 1642 .justifyContent(FlexAlign.Center) 1643 .alignItems(VerticalAlign.Bottom) 1644 .layoutWeight(1) 1645 } 1646 .width('100%') 1647 .height('100%') 1648 } 1649 } 1650 ``` 1651 16522. Node-API模块注册,具体使用请参考[Node-API开发规范](../napi/napi-guidelines.md)。 1653 1654 ```c++ 1655 #include <hilog/log.h> 1656 #include "common/common.h" 1657 #include "manager/plugin_manager.h" 1658 1659 // 在napi_init.cpp文件中,Init方法注册接口函数,从而将封装的C++方法传递出来,供ArkTS侧调用 1660 EXTERN_C_START 1661 static napi_value Init(napi_env env, napi_value exports) { 1662 // ... 1663 // 向ArkTS侧暴露接口 1664 napi_property_descriptor desc[] = { 1665 {"createNativeNode", nullptr, PluginManager::createNativeNode, nullptr, nullptr, nullptr, 1666 napi_default, nullptr }, 1667 {"getStatus", nullptr, PluginManager::GetXComponentStatus, nullptr, nullptr, 1668 nullptr, napi_default, nullptr}, 1669 {"drawPattern", nullptr, PluginManager::NapiDrawPattern, nullptr, nullptr, 1670 nullptr, napi_default, nullptr} 1671 }; 1672 if (napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc) != napi_ok) { 1673 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Init", "napi_define_properties failed"); 1674 return nullptr; 1675 } 1676 return exports; 1677 } 1678 EXTERN_C_END 1679 1680 // 编写接口的描述信息,根据实际需要可以修改对应参数 1681 static napi_module nativerenderModule = { 1682 .nm_version = 1, 1683 .nm_flags = 0, 1684 .nm_filename = nullptr, 1685 // 入口函数 1686 .nm_register_func = Init,// 指定加载对应模块时的回调函数 1687 // 模块名称 1688 .nm_modname = 1689 "nativerender", // 指定模块名称,对于XComponent相关开发,这个名称必须和ArkTS侧XComponent中libraryname的值保持一致 1690 .nm_priv = ((void *)0), 1691 .reserved = {0}}; 1692 1693 // __attribute__((constructor))修饰的方法由系统自动调用,使用Node-API接口napi_module_register()传入模块描述信息进行模块注册 1694 extern "C" __attribute__((constructor)) void RegisterModule(void) { napi_module_register(&nativerenderModule); } 1695 ``` 1696 16973. 注册XComponent事件回调,使用Node-API实现XComponent事件回调函数。 1698 1699 (1) 定义Surface创建成功,发生改变,销毁和XComponent的touch事件回调接口。 1700 1701 ```c++ 1702 // 在头文件中定义PluginManager类 1703 class PluginManager { 1704 public: 1705 static OH_NativeXComponent_Callback callback_; 1706 PluginManager(); 1707 ~PluginManager(); 1708 static PluginManager* GetInstance() 1709 { 1710 return &PluginManager::pluginManager_; 1711 } 1712 1713 static napi_value createNativeNode(napi_env env, napi_callback_info info); 1714 static napi_value GetXComponentStatus(napi_env env, napi_callback_info info); 1715 static napi_value NapiDrawPattern(napi_env env, napi_callback_info info); 1716 1717 // CAPI XComponent 1718 void OnSurfaceChanged(OH_NativeXComponent* component, void* window); 1719 void OnSurfaceDestroyed(OH_NativeXComponent* component, void* window); 1720 void DispatchTouchEvent(OH_NativeXComponent* component, void* window); 1721 void OnSurfaceCreated(OH_NativeXComponent* component, void* window); 1722 1723 public: 1724 EGLCore *eglcore_; 1725 uint64_t width_; 1726 uint64_t height_; 1727 OH_NativeXComponent_TouchEvent touchEvent_; 1728 static int32_t hasDraw_; 1729 static int32_t hasChangeColor_; 1730 1731 private: 1732 static PluginManager pluginManager_; 1733 std::unordered_map<std::string, OH_NativeXComponent*> nativeXComponentMap_; 1734 std::unordered_map<std::string, PluginManager*> pluginManagerMap_; 1735 }; 1736 ``` 1737 1738 ```c++ 1739 // 定义一个函数OnSurfaceCreatedCB(),封装初始化环境与绘制背景 1740 void OnSurfaceCreatedCB(OH_NativeXComponent *component, void *window) { 1741 // ... 1742 // 初始化环境与绘制背景 1743 auto *pluginManager = PluginManager::GetInstance(); 1744 pluginManager->OnSurfaceCreated(component, window); 1745 } 1746 1747 // 定义一个函数OnSurfaceChangedCB() 1748 void OnSurfaceChangedCB(OH_NativeXComponent *component, void *window) { 1749 // ... 1750 auto *pluginManager = PluginManager::GetInstance(); 1751 // 封装OnSurfaceChanged方法 1752 pluginManager->OnSurfaceChanged(component, window); 1753 } 1754 1755 // 定义一个函数OnSurfaceDestroyedCB(),将PluginRender类内释放资源的方法Release()封装在其中 1756 void OnSurfaceDestroyedCB(OH_NativeXComponent *component, void *window) { 1757 // ... 1758 auto *pluginManager = PluginManager::GetInstance(); 1759 pluginManager->OnSurfaceDestroyed(component, window); 1760 } 1761 1762 // 定义一个函数DispatchTouchEventCB(),响应触摸事件时触发该回调 1763 void DispatchTouchEventCB(OH_NativeXComponent *component, void *window) { 1764 // ... 1765 auto *pluginManager = PluginManager::GetInstance(); 1766 pluginManager->DispatchTouchEvent(component, window); 1767 } 1768 ``` 1769 1770 (2) 定义createNativeNode方法,暴露到ArkTS侧的createNativeNode()方法会执行该方法。 1771 1772 ```c++ 1773 napi_value PluginManager::createNativeNode(napi_env env, napi_callback_info info) 1774 { 1775 if ((env == nullptr) || (info == nullptr)) { 1776 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "CreateNativeNode env or info is null"); 1777 return nullptr; 1778 } 1779 size_t argCnt = 2; 1780 napi_value args[2] = { nullptr, nullptr }; 1781 if (napi_get_cb_info(env, info, &argCnt, args, nullptr, nullptr) != napi_ok) { 1782 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "CreateNativeNode napi_get_cb_info failed"); 1783 return nullptr; 1784 } 1785 if (argCnt != ARG_CNT) { 1786 napi_throw_type_error(env, NULL, "Wrong number of arguments"); 1787 return nullptr; 1788 } 1789 ArkUI_NodeContentHandle nodeContentHandle_ = nullptr; 1790 OH_ArkUI_GetNodeContentFromNapiValue(env, args[0], &nodeContentHandle_); 1791 nodeAPI = reinterpret_cast<ArkUI_NativeNodeAPI_1*>( 1792 OH_ArkUI_QueryModuleInterfaceByName(ARKUI_NATIVE_NODE, "ArkUI_NativeNodeAPI_1") 1793 ); 1794 std::string tag = value2String(env, args[1]); 1795 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "tag=%{public}s", tag.c_str()); 1796 int32_t ret = OH_ArkUI_NodeContent_SetUserData(nodeContentHandle_, new std::string(tag)); 1797 if (ret != ARKUI_ERROR_CODE_NO_ERROR) { 1798 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "setUserData failed error=%{public}d", ret); 1799 } 1800 if (nodeAPI != nullptr && nodeAPI->createNode != nullptr && nodeAPI->addChild != nullptr) { 1801 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", 1802 "CreateNativeNode tag=%{public}s", tag.c_str()); 1803 auto nodeContentEvent = [](ArkUI_NodeContentEvent *event) { 1804 ArkUI_NodeContentHandle handle = OH_ArkUI_NodeContentEvent_GetNodeContentHandle(event); 1805 std::string *userDate = reinterpret_cast<std::string*>(OH_ArkUI_NodeContent_GetUserData(handle)); 1806 if (OH_ArkUI_NodeContentEvent_GetEventType(event) == NODE_CONTENT_EVENT_ON_ATTACH_TO_WINDOW) { 1807 ArkUI_NodeHandle testNode; 1808 if (userDate) { 1809 testNode = CreateNodeHandle(*userDate); 1810 delete userDate; 1811 userDate = nullptr; 1812 } else { 1813 testNode = CreateNodeHandle("noUserData"); 1814 } 1815 OH_ArkUI_NodeContent_AddNode(handle, testNode); 1816 } 1817 }; 1818 OH_ArkUI_NodeContent_RegisterCallback(nodeContentHandle_, nodeContentEvent); 1819 } 1820 return nullptr; 1821 } 1822 1823 ArkUI_NodeHandle CreateNodeHandle(const std::string &tag) 1824 { 1825 ArkUI_NodeHandle column = nodeAPI->createNode(ARKUI_NODE_COLUMN); 1826 ArkUI_NumberValue value[] = {480}; 1827 ArkUI_NumberValue value1[] = {{.u32 = 15}, {.f32 = 15}}; 1828 ArkUI_AttributeItem item = {value, 1, "changeSize"}; 1829 ArkUI_AttributeItem item1 = {value1, 2}; 1830 nodeAPI->setAttribute(column, NODE_WIDTH, &item); 1831 value[0].f32 = COLUMN_MARGIN; 1832 nodeAPI->setAttribute(column, NODE_MARGIN, &item); 1833 // 创建XComponent组件 1834 xc = nodeAPI->createNode(ARKUI_NODE_XCOMPONENT); 1835 // 设置XComponent组件属性 1836 value[0].u32 = ARKUI_XCOMPONENT_TYPE_SURFACE; 1837 nodeAPI->setAttribute(xc, NODE_XCOMPONENT_TYPE, &item); 1838 nodeAPI->setAttribute(xc, NODE_XCOMPONENT_ID, &item); 1839 nodeAPI->setAttribute(xc, NODE_XCOMPONENT_SURFACE_SIZE, &item1); 1840 ArkUI_NumberValue focusable[] = {1}; 1841 focusable[0].i32 = 1; 1842 ArkUI_AttributeItem focusableItem = {focusable, 1}; 1843 nodeAPI->setAttribute(xc, NODE_FOCUSABLE, &focusableItem); 1844 ArkUI_NumberValue valueSize[] = {480}; 1845 ArkUI_AttributeItem itemSize = {valueSize, 1}; 1846 valueSize[0].f32 = XC_WIDTH; 1847 nodeAPI->setAttribute(xc, NODE_WIDTH, &itemSize); 1848 valueSize[0].f32 = XC_HEIGHT; 1849 nodeAPI->setAttribute(xc, NODE_HEIGHT, &itemSize); 1850 ArkUI_AttributeItem item2 = {value, 1, "ndkxcomponent"}; 1851 nodeAPI->setAttribute(xc, NODE_ID, &item2); 1852 1853 auto *nativeXComponent = OH_NativeXComponent_GetNativeXComponent(xc); 1854 if (!nativeXComponent) { 1855 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "GetNativeXComponent error"); 1856 return column; 1857 } 1858 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "GetNativeXComponent success"); 1859 // 注册XComponent回调函数 1860 OH_NativeXComponent_RegisterCallback(nativeXComponent, &PluginManager::callback_); 1861 auto typeRet = nodeAPI->getAttribute(xc, NODE_XCOMPONENT_TYPE); 1862 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "xcomponent type: %{public}d", 1863 typeRet->value[0].i32); 1864 auto idRet = nodeAPI->getAttribute(xc, NODE_XCOMPONENT_ID); 1865 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "xcomponent id: %{public}s", 1866 idRet->string); 1867 nodeAPI->addChild(column, xc); 1868 return column; 1869 } 1870 ``` 1871 1872 (3) 定义NapiDrawPattern方法,暴露到ArkTS侧的drawPattern()方法会执行该方法。 1873 1874 ```c++ 1875 napi_value PluginManager::NapiDrawPattern(napi_env env, napi_callback_info info) { 1876 // ... 1877 // 获取环境变量参数 1878 napi_value thisArg; 1879 if (napi_get_cb_info(env, info, nullptr, nullptr, &thisArg, nullptr) != napi_ok) { 1880 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "NapiDrawPattern: napi_get_cb_info fail"); 1881 return nullptr; 1882 } 1883 1884 auto *pluginManager = PluginManager::GetInstance(); 1885 // 调用绘制方法 1886 pluginManager->eglcore_->Draw(hasDraw_); 1887 OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "PluginManager", "render->eglCore_->Draw() executed"); 1888 1889 return nullptr; 1890 } 1891 ``` 1892 18934. 初始化环境,包括初始化可用的EGLDisplay、确定可用的Surface配置、创建渲染区域Surface、创建并关联上下文等。 1894 1895 ```c++ 1896 void EGLCore::UpdateSize(int width, int height) { 1897 // width_和height_在头文件中定义 1898 width_ = width; 1899 height_ = height; 1900 if (width_ > 0) { 1901 widthPercent_ = FIFTY_PERCENT * height_ / width_; 1902 } 1903 } 1904 1905 bool EGLCore::EglContextInit(void *window, int width, int height) { 1906 // ... 1907 UpdateSize(width, height); 1908 eglWindow_ = static_cast<EGLNativeWindowType>(window); 1909 1910 // 初始化display 1911 eglDisplay_ = eglGetDisplay(EGL_DEFAULT_DISPLAY); 1912 if (eglDisplay_ == EGL_NO_DISPLAY) { 1913 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "eglGetDisplay: unable to get EGL display"); 1914 return false; 1915 } 1916 1917 // 初始化EGL 1918 EGLint majorVersion; 1919 EGLint minorVersion; 1920 if (!eglInitialize(eglDisplay_, &majorVersion, &minorVersion)) { 1921 OH_LOG_Print( 1922 LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "eglInitialize: unable to get initialize EGL display"); 1923 return false; 1924 } 1925 1926 // 选择配置 1927 const EGLint maxConfigSize = 1; 1928 EGLint numConfigs; 1929 if (!eglChooseConfig(eglDisplay_, ATTRIB_LIST, &eglConfig_, maxConfigSize, &numConfigs)) { 1930 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "eglChooseConfig: unable to choose configs"); 1931 return false; 1932 } 1933 // 创建环境 1934 return CreateEnvironment(); 1935 } 1936 ``` 1937 1938 ```c++ 1939 bool EGLCore::CreateEnvironment() { 1940 // 创建Surface 1941 if (eglWindow_ == nullptr) { 1942 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "eglWindow_ is null"); 1943 return false; 1944 } 1945 eglSurface_ = eglCreateWindowSurface(eglDisplay_, eglConfig_, eglWindow_, NULL); 1946 if (eglSurface_ == nullptr) { 1947 OH_LOG_Print( 1948 LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "eglCreateWindowSurface: unable to create surface"); 1949 return false; 1950 } 1951 1952 // 创建context 1953 eglContext_ = eglCreateContext(eglDisplay_, eglConfig_, EGL_NO_CONTEXT, CONTEXT_ATTRIBS); 1954 if (!eglMakeCurrent(eglDisplay_, eglSurface_, eglSurface_, eglContext_)) { 1955 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "eglMakeCurrent failed"); 1956 return false; 1957 } 1958 1959 // 创建program 1960 program_ = CreateProgram(VERTEX_SHADER, FRAGMENT_SHADER); 1961 if (program_ == PROGRAM_ERROR) { 1962 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "CreateProgram: unable to create program"); 1963 return false; 1964 } 1965 return true; 1966 } 1967 1968 GLuint EGLCore::CreateProgram(const char* vertexShader, const char* fragShader) { 1969 if ((vertexShader == nullptr) || (fragShader == nullptr)) { 1970 OH_LOG_Print( 1971 LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "createProgram: vertexShader or fragShader is null"); 1972 return PROGRAM_ERROR; 1973 } 1974 1975 GLuint vertex = LoadShader(GL_VERTEX_SHADER, vertexShader); 1976 if (vertex == PROGRAM_ERROR) { 1977 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "createProgram vertex error"); 1978 return PROGRAM_ERROR; 1979 } 1980 1981 GLuint fragment = LoadShader(GL_FRAGMENT_SHADER, fragShader); 1982 if (fragment == PROGRAM_ERROR) { 1983 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "createProgram fragment error"); 1984 return PROGRAM_ERROR; 1985 } 1986 1987 GLuint program = glCreateProgram(); 1988 if (program == PROGRAM_ERROR) { 1989 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "createProgram program error"); 1990 glDeleteShader(vertex); 1991 glDeleteShader(fragment); 1992 return PROGRAM_ERROR; 1993 } 1994 1995 // 该gl函数没有返回值。 1996 glAttachShader(program, vertex); 1997 glAttachShader(program, fragment); 1998 glLinkProgram(program); 1999 2000 GLint linked; 2001 glGetProgramiv(program, GL_LINK_STATUS, &linked); 2002 if (linked != 0) { 2003 glDeleteShader(vertex); 2004 glDeleteShader(fragment); 2005 return program; 2006 } 2007 2008 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "createProgram linked error"); 2009 GLint infoLen = 0; 2010 glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLen); 2011 if (infoLen > 1) { 2012 char* infoLog = (char*)malloc(sizeof(char) * (infoLen + 1)); 2013 memset(infoLog, 0, infoLen + 1); 2014 glGetProgramInfoLog(program, infoLen, nullptr, infoLog); 2015 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "glLinkProgram error = %s", infoLog); 2016 free(infoLog); 2017 infoLog = nullptr; 2018 } 2019 glDeleteShader(vertex); 2020 glDeleteShader(fragment); 2021 glDeleteProgram(program); 2022 return PROGRAM_ERROR; 2023 } 2024 2025 GLuint EGLCore::LoadShader(GLenum type, const char* shaderSrc) { 2026 if ((type == 0) || (shaderSrc == nullptr)) { 2027 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "glCreateShader type or shaderSrc error"); 2028 return PROGRAM_ERROR; 2029 } 2030 2031 GLuint shader = glCreateShader(type); 2032 if (shader == 0) { 2033 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "glCreateShader unable to load shader"); 2034 return PROGRAM_ERROR; 2035 } 2036 2037 // 该gl函数没有返回值。 2038 glShaderSource(shader, 1, &shaderSrc, nullptr); 2039 glCompileShader(shader); 2040 2041 GLint compiled; 2042 glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled); 2043 if (compiled != 0) { 2044 return shader; 2045 } 2046 2047 GLint infoLen = 0; 2048 glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen); 2049 if (infoLen <= 1) { 2050 glDeleteShader(shader); 2051 return PROGRAM_ERROR; 2052 } 2053 2054 char *infoLog = (char*)malloc(sizeof(char) * (infoLen + 1)); 2055 if (infoLog != nullptr) { 2056 memset(infoLog, 0, infoLen + 1); 2057 glGetShaderInfoLog(shader, infoLen, nullptr, infoLog); 2058 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "glCompileShader error = %s", infoLog); 2059 free(infoLog); 2060 infoLog = nullptr; 2061 } 2062 glDeleteShader(shader); 2063 return PROGRAM_ERROR; 2064 } 2065 2066 ``` 2067 20685. 渲染功能实现。 2069 2070 (1) 绘制背景。 2071 2072 ```c++ 2073 // ... 2074 // 绘制背景颜色 #f4f4f4 2075 const GLfloat BACKGROUND_COLOR[] = { 244.0f / 255, 244.0f / 255, 244.0f / 255, 1.0f }; 2076 2077 // 绘制图案颜色 2078 const GLfloat DRAW_COLOR[] = {126.0f / 255, 143.0f / 255, 251.0f / 255, 1.0f}; 2079 2080 // 绘制图案改变后的颜色 2081 const GLfloat CHANGE_COLOR[] = {146.0f / 255, 214.0f / 255, 204.0f / 255, 1.0f}; 2082 2083 // 绘制背景顶点 2084 const GLfloat BACKGROUND_RECTANGLE_VERTICES[] = { 2085 -1.0f, 1.0f, 2086 1.0f, 1.0f, 2087 1.0f, -1.0f, 2088 -1.0f, -1.0f 2089 }; 2090 // ... 2091 ``` 2092 2093 ```c++ 2094 // 绘制背景颜色 2095 void EGLCore::Background() { 2096 GLint position = PrepareDraw(); 2097 if (position == POSITION_ERROR) { 2098 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Background get position failed"); 2099 return; 2100 } 2101 2102 if (!ExecuteDraw(position, BACKGROUND_COLOR, BACKGROUND_RECTANGLE_VERTICES, 2103 sizeof(BACKGROUND_RECTANGLE_VERTICES))) { 2104 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Background execute draw failed"); 2105 return; 2106 } 2107 2108 if (!FinishDraw()) { 2109 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Background FinishDraw failed"); 2110 return; 2111 } 2112 } 2113 2114 // 绘前准备,获取position,创建成功时position值从0开始 2115 GLint EGLCore::PrepareDraw() { 2116 if ((eglDisplay_ == nullptr) || (eglSurface_ == nullptr) || (eglContext_ == nullptr) || 2117 (!eglMakeCurrent(eglDisplay_, eglSurface_, eglSurface_, eglContext_))) { 2118 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "PrepareDraw: param error"); 2119 return POSITION_ERROR; 2120 } 2121 2122 // 该gl函数没有返回值。 2123 glViewport(DEFAULT_X_POSITION, DEFAULT_Y_POSITION, width_, height_); 2124 glClearColor(GL_RED_DEFAULT, GL_GREEN_DEFAULT, GL_BLUE_DEFAULT, GL_ALPHA_DEFAULT); 2125 glClear(GL_COLOR_BUFFER_BIT); 2126 glUseProgram(program_); 2127 2128 return glGetAttribLocation(program_, POSITION_NAME); 2129 } 2130 2131 // 依据传入参数在指定区域绘制指定颜色 2132 bool EGLCore::ExecuteDraw(GLint position, const GLfloat *color, const GLfloat shapeVertices[], unsigned long vertSize) { 2133 if ((position > 0) || (color == nullptr) || (vertSize / sizeof(shapeVertices[0])) != SHAPE_VERTICES_SIZE) { 2134 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "ExecuteDraw: param error"); 2135 return false; 2136 } 2137 2138 // 该gl函数没有返回值。 2139 glVertexAttribPointer(position, POINTER_SIZE, GL_FLOAT, GL_FALSE, 0, shapeVertices); 2140 glEnableVertexAttribArray(position); 2141 glVertexAttrib4fv(1, color); 2142 glDrawArrays(GL_TRIANGLE_FAN, 0, TRIANGLE_FAN_SIZE); 2143 glDisableVertexAttribArray(position); 2144 2145 return true; 2146 } 2147 2148 // 结束绘制操作 2149 bool EGLCore::FinishDraw() { 2150 // 强制刷新缓冲 2151 glFlush(); 2152 glFinish(); 2153 return eglSwapBuffers(eglDisplay_, eglSurface_); 2154 } 2155 ``` 2156 2157 (2) 绘制图形。 2158 2159 ```c++ 2160 void EGLCore::Draw(int& hasDraw) { 2161 flag_ = false; 2162 OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "EGLCore", "Draw"); 2163 GLint position = PrepareDraw(); 2164 if (position == POSITION_ERROR) { 2165 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw get position failed"); 2166 return; 2167 } 2168 2169 // 绘制背景 2170 if (!ExecuteDraw(position, BACKGROUND_COLOR, 2171 BACKGROUND_RECTANGLE_VERTICES, sizeof(BACKGROUND_RECTANGLE_VERTICES))) { 2172 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw execute draw background failed"); 2173 return; 2174 } 2175 2176 // 将五角星分为五个四边形,计算其中一个四边形的四个顶点 2177 GLfloat rotateX = 0; 2178 GLfloat rotateY = FIFTY_PERCENT * height_; 2179 GLfloat centerX = 0; 2180 GLfloat centerY = -rotateY * (M_PI / 180 * 54) * (M_PI / 180 * 18); 2181 GLfloat leftX = -rotateY * (M_PI / 180 * 18); 2182 GLfloat leftY = 0; 2183 GLfloat rightX = rotateY * (M_PI / 180 * 18); 2184 GLfloat rightY = 0; 2185 2186 // 确定绘制四边形的顶点,使用绘制区域的百分比表示 2187 const GLfloat shapeVertices[] = { 2188 centerX / width_, centerY / height_, 2189 leftX / width_, leftY / height_, 2190 rotateX / width_, rotateY / height_, 2191 rightX / width_, rightY / height_ 2192 }; 2193 2194 if (!ExecuteDrawStar(position, DRAW_COLOR, shapeVertices, sizeof(shapeVertices))) { 2195 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw execute draw star failed"); 2196 return; 2197 } 2198 2199 GLfloat rad = M_PI / 180 * 72; 2200 for (int i = 0; i < NUM_4; ++i) { 2201 // 旋转得其他四个四边形的顶点 2202 Rotate2d(centerX, centerY, &rotateX, &rotateY, rad); 2203 Rotate2d(centerX, centerY, &leftX, &leftY, rad); 2204 Rotate2d(centerX, centerY, &rightX, &rightY, rad); 2205 2206 // 确定绘制四边形的顶点,使用绘制区域的百分比表示 2207 const GLfloat shapeVertices[] = { 2208 centerX / width_, centerY / height_, 2209 leftX / width_, leftY / height_, 2210 rotateX / width_, rotateY / height_, 2211 rightX / width_, rightY / height_ 2212 }; 2213 2214 // 绘制图形 2215 if (!ExecuteDrawStar(position, DRAW_COLOR, shapeVertices, sizeof(shapeVertices))) { 2216 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw execute draw shape failed"); 2217 return; 2218 } 2219 } 2220 2221 // 结束绘制 2222 if (!FinishDraw()) { 2223 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw FinishDraw failed"); 2224 return; 2225 } 2226 hasDraw = 1; 2227 2228 flag_ = true; 2229 } 2230 ``` 2231 2232 (3) 改变颜色,重新画一个大小相同颜色不同的图形,与原图形替换,达到改变颜色的效果。 2233 2234 ```c++ 2235 void EGLCore::ChangeColor(int& hasChangeColor) { 2236 if (!flag_) { 2237 return; 2238 } 2239 OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "EGLCore", "ChangeColor"); 2240 GLint position = PrepareDraw(); 2241 if (position == POSITION_ERROR) { 2242 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "ChangeColor get position failed"); 2243 return; 2244 } 2245 2246 // 绘制背景 2247 if (!ExecuteDraw(position, BACKGROUND_COLOR, 2248 BACKGROUND_RECTANGLE_VERTICES, sizeof(BACKGROUND_RECTANGLE_VERTICES))) { 2249 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "ChangeColor execute draw background failed"); 2250 return; 2251 } 2252 2253 // 确定绘制四边形的顶点,使用绘制区域的百分比表示 2254 GLfloat rotateX = 0; 2255 GLfloat rotateY = FIFTY_PERCENT * height_; 2256 GLfloat centerX = 0; 2257 GLfloat centerY = -rotateY * (M_PI / 180 * 54) * (M_PI / 180 * 18); 2258 GLfloat leftX = -rotateY * (M_PI / 180 * 18); 2259 GLfloat leftY = 0; 2260 GLfloat rightX = rotateY * (M_PI / 180 * 18); 2261 GLfloat rightY = 0; 2262 2263 // 确定绘制四边形的顶点,使用绘制区域的百分比表示 2264 const GLfloat shapeVertices[] = { 2265 centerX / width_, centerY / height_, 2266 leftX / width_, leftY / height_, 2267 rotateX / width_, rotateY / height_, 2268 rightX / width_, rightY / height_ 2269 }; 2270 2271 // 使用新的颜色绘制 2272 if (!ExecuteDrawNewStar(position, CHANGE_COLOR, shapeVertices, sizeof(shapeVertices))) { 2273 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw execute draw star failed"); 2274 return; 2275 } 2276 2277 GLfloat rad = M_PI / 180 * 72; 2278 for (int i = 0; i < NUM_4; ++i) { 2279 // 旋转得其他四个四边形的顶点 2280 Rotate2d(centerX, centerY, &rotateX, &rotateY, rad); 2281 Rotate2d(centerX, centerY, &leftX, &leftY, rad); 2282 Rotate2d(centerX, centerY, &rightX, &rightY, rad); 2283 2284 // 确定绘制四边形的顶点,使用绘制区域的百分比表示 2285 const GLfloat shapeVertices[] = { 2286 centerX / width_, centerY / height_, 2287 leftX / width_, leftY / height_, 2288 rotateX / width_, rotateY / height_, 2289 rightX / width_, rightY / height_ 2290 }; 2291 2292 // 使用新的颜色绘制 2293 if (!ExecuteDrawNewStar(position, CHANGE_COLOR, shapeVertices, sizeof(shapeVertices))) { 2294 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw execute draw shape failed"); 2295 return; 2296 } 2297 } 2298 2299 // 结束绘制 2300 if (!FinishDraw()) { 2301 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "ChangeColor FinishDraw failed"); 2302 } 2303 hasChangeColor = 1; 2304 } 2305 2306 bool EGLCore::ExecuteDrawNewStar( 2307 GLint position, const GLfloat* color, const GLfloat shapeVertices[], unsigned long vertSize) { 2308 if ((position > 0) || (color == nullptr) || (vertSize / sizeof(shapeVertices[0])) != SHAPE_VERTICES_SIZE) { 2309 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "ExecuteDraw: param error"); 2310 return false; 2311 } 2312 2313 // 该gl函数没有返回值。 2314 glVertexAttribPointer(position, POINTER_SIZE, GL_FLOAT, GL_FALSE, 0, shapeVertices); 2315 glEnableVertexAttribArray(position); 2316 glVertexAttrib4fv(1, color); 2317 glDrawArrays(GL_TRIANGLE_FAN, 0, TRIANGLE_FAN_SIZE); 2318 glDisableVertexAttribArray(position); 2319 2320 return true; 2321 } 2322 ``` 2323 23246. 释放相关资源。 2325 2326 (1) EGLCore类下创建Release()方法,释放初始化环境时申请的资源,包含窗口display、渲染区域surface、环境上下文context等。 2327 2328 ```c++ 2329 void EGLCore::Release() { 2330 // 释放Surface 2331 if ((eglDisplay_ == nullptr) || (eglSurface_ == nullptr) || (!eglDestroySurface(eglDisplay_, eglSurface_))) { 2332 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Release eglDestroySurface failed"); 2333 } 2334 // 释放context 2335 if ((eglDisplay_ == nullptr) || (eglContext_ == nullptr) || (!eglDestroyContext(eglDisplay_, eglContext_))) { 2336 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Release eglDestroyContext failed"); 2337 } 2338 // 释放display 2339 if ((eglDisplay_ == nullptr) || (!eglTerminate(eglDisplay_))) { 2340 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Release eglTerminate failed"); 2341 } 2342 } 2343 ``` 2344 23457. CMakeLists,使用CMake工具链将C++源代码编译成动态链接库文件。 2346 2347 ```CMake 2348 # 设置CMake最小版本 2349 cmake_minimum_required(VERSION 3.4.1) 2350 # 项目名称 2351 project(XComponent) 2352 2353 set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR}) 2354 add_definitions(-DOHOS_PLATFORM) 2355 # 设置头文件搜索目录 2356 include_directories( 2357 ${NATIVERENDER_ROOT_PATH} 2358 ${NATIVERENDER_ROOT_PATH}/include 2359 ) 2360 # 添加名为nativerender的动态库,库文件名为libnativerender.so,添加cpp文件 2361 add_library(nativerender SHARED 2362 render/egl_core.cpp 2363 render/plugin_render.cpp 2364 manager/plugin_manager.cpp 2365 napi_init.cpp 2366 ) 2367 2368 find_library( 2369 EGL-lib 2370 EGL 2371 ) 2372 2373 find_library( 2374 GLES-lib 2375 GLESv3 2376 ) 2377 2378 find_library( 2379 hilog-lib 2380 hilog_ndk.z 2381 ) 2382 2383 find_library( 2384 libace-lib 2385 ace_ndk.z 2386 ) 2387 2388 find_library( 2389 libnapi-lib 2390 ace_napi.z 2391 ) 2392 2393 find_library( 2394 libuv-lib 2395 uv 2396 ) 2397 # 添加构建需要链接的库 2398 target_link_libraries(nativerender PUBLIC 2399 ${EGL-lib} ${GLES-lib} ${hilog-lib} ${libace-lib} ${libnapi-lib} ${libuv-lib}) 2400 ``` 2401 2402## 相关实例 2403 2404针对Native XComponent的使用,有以下相关实例可供参考: 2405 2406- [XComponent3D(API10)](https://gitcode.com/openharmony/applications_app_samples/tree/master/code/BasicFeature/Native/XComponent3D) 2407- [XComponent(API10)](https://gitcode.com/openharmony/applications_app_samples/tree/master/code/BasicFeature/Native/XComponent) 2408- [Native XComponent(API12)](https://gitcode.com/openharmony/applications_app_samples/tree/master/code/BasicFeature/Native/NdkXComponent) 2409- [OpenGL三棱椎(API10)](https://gitcode.com/openharmony/applications_app_samples/tree/master/code/BasicFeature/Native/NdkOpenGL) 2410- [NativeXComponent(api19)](https://gitcode.com/openharmony/applications_app_samples/tree/master/code/BasicFeature/Native/NativeXComponent) 2411 2412针对ArkTS XComponent的使用,有以下相关实例可供参考: 2413 2414- [ArkTSXComponent(API12)](https://gitcode.com/openharmony/applications_app_samples/tree/master/code/BasicFeature/Native/ArkTSXComponent) 2415 2416<!--RP1--><!--RP1End--> 2417