1# 嵌入ArkTS组件 2<!--Kit: ArkUI--> 3<!--Subsystem: ArkUI--> 4<!--Owner: @xiang-shouxing--> 5<!--Designer: @xiang-shouxing--> 6<!--Tester: @sally__--> 7<!--Adviser: @HelloCrease--> 8 9ArkUI在Native侧提供的能力作为ArkTS的子集,部分能力不会在Native侧提供,如声明式UI语法,自定义struct组件,UI高级组件。 10 11 12针对需要使用ArkTS侧独立能力的场景,ArkUI开发框架提供了Native侧嵌入ArkTS组件的能力,该能力依赖[ComponentContent](../reference/apis-arkui/js-apis-arkui-ComponentContent.md)机制,通过ComponentContent完成对ArkTS组件的封装,然后将封装对象转递到Native侧,通过Native侧的[OH_ArkUI_GetNodeHandleFromNapiValue](../reference/apis-arkui/capi-native-node-napi-h.md#oh_arkui_getnodehandlefromnapivalue)接口转化为ArkUI_NodeHandle对象用于Native侧组件挂载使用。 13 14 15> **说明:** 16> 17> - 通过OH_ArkUI_GetNodeHandleFromNapiValue接口获得的ArkUI_NodeHandle对象只能作为子组件参数使用,如[addChild](../reference/apis-arkui/capi-arkui-nativemodule-arkui-nativenodeapi-1.md#addchild)接口的第二个参数,将该对象使用在其他场景下,如[setAttribute](../reference/apis-arkui/capi-arkui-nativemodule-arkui-nativenodeapi-1.md#setattribute)设置属性将不生效并返回错误码。 18> 19> - 针对Native侧修改ArkTS组件的场景,需要在Native侧通过Node-API方式构建ArkTS侧的更新数据,再通过ComponentContent的[update](../reference/apis-arkui/js-apis-arkui-ComponentContent.md#update)接口更新。 20> 21> - [构建自定义组件](ndk-build-custom-components.md)时,相关函数如measureNode等无法对ArkTS模块内部的组件进行调用。 22 23 24以下示例代码在[接入ArkTS页面](ndk-access-the-arkts-page.md)章节基础上引入ArkTS的Refresh组件。 25 26 27**图1** Refresh组件挂载文本列表 28 29 30 31 321. 注册ArkTS组件创建函数给Native侧,以便Native侧调用,创建函数使用ComponentContent能力进行封装。 33 ```ts 34 // MixedModule.ets 35 // 使用ComponentContent能力创建ArkTS组件 36 37 import { NodeContent, UIContext, RefreshModifier, ComponentContent } from '@kit.ArkUI'; 38 39 // 定义Native侧和ArkTS进行交互的数据对象。 40 interface NativeRefreshAttribute { 41 isRefreshing: boolean; 42 width?: number; 43 height?: number; 44 backgroundColor?: number; 45 refreshOffset?: number; 46 pullToRefresh?: boolean 47 onRefreshing?: () => void; 48 onOffsetChange?: (offset: number) => void; 49 } 50 51 // 定义@Builder函数的入参格式。 52 interface RefreshAttribute { 53 isRefreshing: boolean; 54 // 属性设置通过Modifier优化性能 55 modifier?: RefreshModifier; 56 slot?: NodeContent; 57 onRefreshing?: () => void; 58 onOffsetChange?: (offset: number) => void; 59 } 60 61 // ComponentContent封装ArkTS组件依赖全局@Builder函数,涉及复杂自定义组件场景,可以在@Builder函数中嵌套@Component自定义组件。 62 // @Builder函数提供入参方式,方便后续通过ComponentContent的update接口进行参数更新。 63 @Builder 64 function mixedRefresh(attribute: RefreshAttribute) { 65 Refresh({ refreshing: attribute.isRefreshing }) { 66 // Refresh作为容器组件,需要使用ContentSlot机制预留子组件占位 67 ContentSlot(attribute.slot) 68 }.attributeModifier(attribute.modifier) 69 .onRefreshing(() => { 70 console.info("on onRefreshing"); 71 if (attribute.onRefreshing) { 72 console.info("on native onRefreshing"); 73 attribute.onRefreshing(); 74 } 75 }) 76 .onOffsetChange((value: number) => { 77 console.info("on offset change: " + value); 78 if (attribute.onOffsetChange) { 79 console.info("on native onOffsetChange"); 80 attribute.onOffsetChange(value); 81 } 82 }) 83 } 84 85 // 定义创建函数的返回值,用于ArkTS侧和Native侧的交互。 86 interface MixedModuleResult { 87 // 定义针对Refresh构建函数的封装对象,用于Native侧转化为ArkUI_NodeHandle对象。 88 content?: ComponentContent<RefreshAttribute>; 89 // Refresh作为容器组件,需要使用ContentSlot机制挂载Native侧的子组件。 90 childSlot?: NodeContent; 91 } 92 93 // 提供创建ArkTS组件的入口函数。 94 export function createMixedRefresh(value: NativeRefreshAttribute): MixedModuleResult { 95 console.info("createMixedRefresh"); 96 // 通过AppStorage对象在Ability启动的时候保持UI上下文对象。 97 let uiContent = AppStorage.get<UIContext>("context"); 98 let modifier = new RefreshModifier(); 99 if (value.width) { 100 modifier.width(value.width) 101 } 102 if (value.height) { 103 modifier.height(value.height) 104 } 105 if (value.backgroundColor) { 106 modifier.backgroundColor(value.backgroundColor) 107 } 108 if (value.pullToRefresh) { 109 modifier.pullToRefresh(value.pullToRefresh) 110 } 111 if (value.refreshOffset) { 112 modifier.refreshOffset(value.refreshOffset) 113 } 114 // 创建NodeContent插槽对象用于Refresh子组件挂载。 115 let nodeSlot = new NodeContent(); 116 // 通过ComponentContent创建Refresh组件并将它封装起来。 117 let content = new ComponentContent<RefreshAttribute>(uiContent!, wrapBuilder<[RefreshAttribute]>(mixedRefresh), 118 { 119 isRefreshing: value.isRefreshing, 120 modifier: modifier, 121 slot: nodeSlot, 122 onRefreshing: value.onRefreshing, 123 onOffsetChange: value.onOffsetChange 124 }); 125 // 将Refresh组件的封装对象及其子组件插槽对象传递给Native侧。 126 return { content: content, childSlot: nodeSlot }; 127 } 128 129 // 定义Refresh组件的更新函数,用于Native侧更新。 130 // 在更新场景下,需要将Refresh组件的封装对象及其子组件插槽对象返回,防止组件重新创建。 131 export function updateMixedRefresh(refresh: ComponentContent<RefreshAttribute>, childSlot: NodeContent, 132 value: NativeRefreshAttribute): void { 133 let modifier = new RefreshModifier(); 134 if (value.width) { 135 modifier.width(value.width) 136 } 137 if (value.height) { 138 modifier.height(value.height) 139 } 140 if (value.backgroundColor) { 141 modifier.backgroundColor(value.backgroundColor) 142 } 143 if (value.pullToRefresh) { 144 modifier.pullToRefresh(value.pullToRefresh) 145 } 146 if (value.refreshOffset) { 147 modifier.refreshOffset(value.refreshOffset) 148 } 149 // 调用ComponentContent的update接口进行更新。 150 refresh.update({ 151 isRefreshing: value.isRefreshing, 152 modifier: modifier, 153 slot: childSlot, 154 onRefreshing: value.onRefreshing, 155 onOffsetChange: value.onOffsetChange 156 }) 157 } 158 159 ``` 160 1612. 将创建和更新函数注册给Native侧。 162 ```ts 163 // Index.ets 164 import nativeNode from 'libentry.so'; 165 import { NodeContent } from '@kit.ArkUI'; 166 import { createMixedRefresh, updateMixedRefresh } from './MixedModule' 167 168 @Entry 169 @Component 170 struct Index { 171 private rootSlot = new NodeContent(); 172 @State @Watch('changeNativeFlag') showNative: boolean = false; 173 174 aboutToAppear(): void { 175 // 设置uiContext; 176 AppStorage.setOrCreate<UIContext>("context", this.getUIContext()); 177 // 设置混合模式下的builder函数。 178 nativeNode.registerCreateMixedRefreshNode(createMixedRefresh); 179 nativeNode.registerUpdateMixedRefreshNode(updateMixedRefresh); 180 } 181 182 changeNativeFlag(): void { 183 if (this.showNative) { 184 // 创建NativeModule组件挂载 185 nativeNode.createNativeRoot(this.rootSlot) 186 } else { 187 // 销毁NativeModule组件 188 nativeNode.destroyNativeRoot() 189 } 190 } 191 192 build() { 193 Column() { 194 Button(this.showNative ? "HideNativeUI" : "ShowNativeUI").onClick(() => { 195 this.showNative = !this.showNative 196 }) 197 Row() { 198 // ArkTS插入Native组件。 199 ContentSlot(this.rootSlot) 200 }.layoutWeight(1) 201 } 202 .width('100%') 203 .height('100%') 204 } 205 } 206 ``` 207 208 ```cpp 209 // native_init.cpp 210 #include "napi/native_api.h" 211 #include "ArkUIMixedRefresh.h" 212 #include "NativeEntry.h" 213 214 EXTERN_C_START 215 static napi_value Init(napi_env env, napi_value exports) { 216 napi_property_descriptor desc[] = { 217 // 注册NDK根节点。 218 {"createNativeRoot", nullptr, NativeModule::CreateNativeRoot, nullptr, nullptr, nullptr, napi_default, nullptr}, 219 // 注册混合模式创建和更新方法。 220 {"registerCreateMixedRefreshNode", nullptr, NativeModule::ArkUIMixedRefresh::RegisterCreateAndUpdateRefresh, nullptr, 221 nullptr, nullptr, napi_default, nullptr}, 222 // 销毁NDK根节点。 223 {"destroyNativeRoot", nullptr, NativeModule::DestroyNativeRoot, nullptr, nullptr, nullptr, napi_default, 224 nullptr}}; 225 napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc); 226 return exports; 227 } 228 EXTERN_C_END 229 230 static napi_module demoModule = { 231 .nm_version = 1, 232 .nm_flags = 0, 233 .nm_filename = nullptr, 234 .nm_register_func = Init, 235 .nm_modname = "entry", 236 .nm_priv = ((void *)0), 237 .reserved = {0}, 238 }; 239 240 extern "C" __attribute__((constructor)) void RegisterEntryModule(void) { napi_module_register(&demoModule); } 241 ``` 242 2433. Native侧通过Node-API保存创建和更新函数,用于后续调用。 244 ```c 245 // ArkUIMixedRefresh.h 246 // 混合模式交互类。 247 248 #ifndef MYAPPLICATION_ARKUIMIXEDREFRESH_H 249 #define MYAPPLICATION_ARKUIMIXEDREFRESH_H 250 251 #include "ArkUIMixedNode.h" 252 253 #include <optional> 254 255 #include <arkui/native_node_napi.h> 256 #include <js_native_api_types.h> 257 258 namespace NativeModule { 259 260 class ArkUIMixedRefresh : public ArkUIMixedNode { 261 public: 262 static napi_value RegisterCreateAndUpdateRefresh(napi_env env, napi_callback_info info); 263 }; 264 265 } // namespace NativeModule 266 267 #endif // MYAPPLICATION_ARKUIMIXEDREFRESH_H 268 ``` 269 270 ```cpp 271 // ArkUIMixedRefresh.cpp 272 // 混合模式交互类。 273 274 #include "ArkUIMixedRefresh.h" 275 276 namespace NativeModule { 277 namespace { 278 napi_env g_env; 279 napi_ref g_createRefresh; 280 napi_ref g_updateRefresh; 281 } // namespace 282 283 napi_value ArkUIMixedRefresh::RegisterCreateAndUpdateRefresh(napi_env env, napi_callback_info info) { 284 size_t argc = 1; 285 napi_value args[1] = {nullptr}; 286 287 napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); 288 289 g_env = env; 290 napi_ref refer; 291 // 创建引用之后保存,防止释放。 292 napi_create_reference(env, args[0], 1, &refer); 293 294 g_createRefresh = refer; 295 return nullptr; 296 } 297 298 } // namespace NativeModule 299 ``` 300 301 ```cpp 302 // CMakeLists.txt 303 // optional依赖C++17 304 305 # the minimum version of CMake. 306 cmake_minimum_required(VERSION 3.4.1) 307 project(testndk) 308 309 set(CMAKE_CXX_STANDARD 17) 310 set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR}) 311 312 include_directories(${NATIVERENDER_ROOT_PATH} 313 ${NATIVERENDER_ROOT_PATH}/include) 314 315 add_library(nativeNode SHARED container.cpp manager.cpp init.cpp) 316 # target_link_libraries(entry PUBLIC libace_napi.z.so, libace_ndk.z.so, libhilog_ndk.z.so) 317 318 find_library( 319 # Sets the name of the path variable. 320 hilog-lib 321 # Specifies the name of the NDK library that 322 # you want CMake to locate. 323 hilog_ndk.z 324 ) 325 326 find_library( 327 # Sets the name of the path variable. 328 libace-lib 329 # Specifies the name of the NDK library that 330 # you want CMake to locate. 331 ace_ndk.z 332 ) 333 334 find_library( 335 # Sets the name of the path variable. 336 libnapi-lib 337 # Specifies the name of the NDK library that 338 # you want CMake to locate. 339 ace_napi.z 340 ) 341 342 target_link_libraries(nativeNode PUBLIC 343 ${hilog-lib} ${libace-lib} ${libnapi-lib} ) 344 ``` 345 3464. 抽象混合模式下组件的基类,用于通用逻辑管理。 347 ```c 348 // ArkUIMixedNode.h 349 // 混合模式基类。 350 351 #ifndef MYAPPLICATION_ARKUIMIXEDNODE_H 352 #define MYAPPLICATION_ARKUIMIXEDNODE_H 353 354 #include <js_native_api.h> 355 #include <js_native_api_types.h> 356 357 #include "ArkUIBaseNode.h" 358 #include "NativeModule.h" 359 360 namespace NativeModule { 361 362 // Wrap ArkTS Node 363 class ArkUIMixedNode : public ArkUIBaseNode { 364 public: 365 ArkUIMixedNode(ArkUI_NodeHandle handle, napi_env env, napi_ref componentContent) 366 : ArkUIBaseNode(handle), env_(env), componentContent_(componentContent) {} 367 368 // 在基类析构的时候需要把混合模式在ArkTS侧的对象释放掉。 369 ~ArkUIMixedNode() override { napi_delete_reference(env_, componentContent_); } 370 371 protected: 372 napi_env env_; 373 napi_ref componentContent_; 374 }; 375 376 } // namespace NativeModule 377 378 #endif // MYAPPLICATION_ARKUIMIXEDNODE_H 379 ``` 380 3815. 实现Refresh组件的混合模式封装对象。 382 ```c 383 // ArkUIMixedRefresh.h 384 // Refresh混合模式在Native侧的封装对象。 385 386 #ifndef MYAPPLICATION_ARKUIMIXEDREFRESH_H 387 #define MYAPPLICATION_ARKUIMIXEDREFRESH_H 388 389 #include "ArkUIMixedNode.h" 390 #include "ArkUIBaseNode.h" 391 392 #include <optional> 393 394 #include <arkui/native_node_napi.h> 395 #include <js_native_api_types.h> 396 397 namespace NativeModule { 398 399 // 定义Native侧和ArkTS侧的交互数据结构。 400 struct NativeRefreshAttribute { 401 std::optional<bool> isRefreshing; 402 std::optional<float> width; 403 std::optional<float> height; 404 std::optional<uint32_t> backgroundColor; 405 std::optional<float> refreshOffset; 406 std::optional<bool> pullToRefresh; 407 std::function<void()> onRefreshing; 408 std::function<void(float)> onOffsetChange; 409 }; 410 411 class ArkUIMixedRefresh : public ArkUIMixedNode { 412 public: 413 // 调用ArkTS的方法创建Refresh组件。 414 static const std::shared_ptr<ArkUIMixedRefresh> Create(const NativeRefreshAttribute &attribute); 415 416 ArkUIMixedRefresh(ArkUI_NodeHandle handle, ArkUI_NodeContentHandle contentHandle, napi_env env, 417 napi_ref componentContent, napi_ref nodeContent) 418 : ArkUIMixedNode(handle, env, componentContent), contentHandle_(contentHandle), nodeContent_(nodeContent) {} 419 420 ArkUIMixedRefresh() : ArkUIMixedNode(nullptr, nullptr, nullptr) {} 421 422 ~ArkUIMixedRefresh() override { napi_delete_reference(env_, nodeContent_); } // 释放子节点占位组件插槽对象。 423 424 void SetWidth(float width) { attribute_.width = width; } 425 426 void SetHeight(float height) { attribute_.height = height; } 427 428 void SetBackgroundColor(uint32_t color) { attribute_.backgroundColor = color; } 429 430 void SetRefreshState(bool isRefreshing) { attribute_.isRefreshing = isRefreshing; } 431 432 void SetPullToRefresh(bool pullToRefresh) { attribute_.pullToRefresh = pullToRefresh; } 433 434 void SetRefreshOffset(float offset) { attribute_.refreshOffset = offset; } 435 436 void SetRefreshCallback(const std::function<void()> &callback) { attribute_.onRefreshing = callback; } 437 438 void SetOnOffsetChange(const std::function<void(float)> &callback) { attribute_.onOffsetChange = callback; } 439 440 // 避免频繁跨语言,在Native侧缓存属性事件,批量通知。 441 void FlushMixedModeCmd(); 442 443 static napi_value RegisterCreateAndUpdateRefresh(napi_env env, napi_callback_info info); 444 445 protected: 446 void OnAddChild(const std::shared_ptr<ArkUIBaseNode> &child) override { 447 assert(contentHandle_); 448 // 使用NodeContent挂载组件(可以使用ArkTS在Native侧通过ComponentContent的转化对象,也可以是纯Native组件)到ArkTS组件下面。 449 OH_ArkUI_NodeContent_AddNode(contentHandle_, child->GetHandle()); 450 } 451 452 void OnRemoveChild(const std::shared_ptr<ArkUIBaseNode> &child) override { 453 assert(contentHandle_); 454 // 使用NodeContent卸载组件。 455 OH_ArkUI_NodeContent_RemoveNode(contentHandle_, child->GetHandle()); 456 } 457 458 void OnInsertChild(const std::shared_ptr<ArkUIBaseNode> &child, int32_t index) override { 459 assert(contentHandle_); 460 // 使用NodeContent插入组件。 461 OH_ArkUI_NodeContent_InsertNode(contentHandle_, child->GetHandle(), index); 462 } 463 464 private: 465 // 使用napi接口创建ArkTS侧的数据结构。 466 static napi_value CreateRefreshAttribute(const NativeRefreshAttribute &attribute, void *userData); 467 468 ArkUI_NodeContentHandle contentHandle_; 469 napi_ref nodeContent_; 470 NativeRefreshAttribute attribute_; 471 }; 472 473 } // namespace NativeModule 474 475 #endif // MYAPPLICATION_ARKUIMIXEDREFRESH_H 476 ``` 477 478 相关实现类说明: 479 480 ```c 481 // ArkUIMixedRefresh.cpp 482 483 #include "ArkUIMixedRefresh.h" 484 #include <hilog/log.h> 485 486 namespace NativeModule { 487 namespace { 488 napi_env g_env; 489 napi_ref g_createRefresh; 490 napi_ref g_updateRefresh; 491 } // namespace 492 493 // 使用Napi接口创建与ArkTS侧交互的数据结构,用于Refresh组件的创建和更新。 494 napi_value ArkUIMixedRefresh::CreateRefreshAttribute(const NativeRefreshAttribute &attribute, void *userData) { 495 napi_property_descriptor desc[] = { 496 {"width", nullptr, nullptr, nullptr, nullptr, nullptr, napi_default, userData}, 497 {"height", nullptr, nullptr, nullptr, nullptr, nullptr, napi_default, userData}, 498 {"backgroundColor", nullptr, nullptr, nullptr, nullptr, nullptr, napi_default, userData}, 499 {"pullToRefresh", nullptr, nullptr, nullptr, nullptr, nullptr, napi_default, userData}, 500 {"isRefreshing", nullptr, nullptr, nullptr, nullptr, nullptr, napi_default, userData}, 501 {"refreshOffset", nullptr, nullptr, nullptr, nullptr, nullptr, napi_default, userData}, 502 {"onRefreshing", nullptr, nullptr, nullptr, nullptr, nullptr, napi_default, userData}, 503 {"onOffsetChange", nullptr, nullptr, nullptr, nullptr, nullptr, napi_default, userData}, 504 }; 505 if (attribute.width) { 506 napi_value width; 507 napi_create_double(g_env, attribute.width.value(), &width); 508 desc[0].value = width; 509 } 510 if (attribute.height) { 511 napi_value height; 512 napi_create_double(g_env, attribute.height.value(), &height); 513 desc[1].value = height; 514 } 515 if (attribute.backgroundColor) { 516 napi_value backgroundColor; 517 napi_create_uint32(g_env, attribute.backgroundColor.value(), &backgroundColor); 518 desc[2].value = backgroundColor; 519 } 520 if (attribute.pullToRefresh) { 521 napi_value pullToRefresh; 522 napi_create_int32(g_env, attribute.pullToRefresh.value(), &pullToRefresh); 523 desc[3].value = pullToRefresh; 524 } 525 if (attribute.isRefreshing) { 526 napi_value isRefreshing; 527 napi_create_int32(g_env, attribute.isRefreshing.value(), &isRefreshing); 528 desc[4].value = isRefreshing; 529 } 530 if (attribute.refreshOffset) { 531 napi_value refreshOffset; 532 napi_create_double(g_env, attribute.refreshOffset.value(), &refreshOffset); 533 desc[5].value = refreshOffset; 534 } 535 if (attribute.onRefreshing) { 536 OH_LOG_INFO(LOG_APP, "onRefreshing start"); 537 desc[6].method = [](napi_env env, napi_callback_info info) -> napi_value { 538 OH_LOG_INFO(LOG_APP, "onRefreshing callback"); 539 size_t argc = 0; 540 napi_value args[0]; 541 void *data; 542 napi_get_cb_info(env, info, &argc, args, nullptr, &data); 543 auto refresh = reinterpret_cast<ArkUIMixedRefresh *>(data); 544 if (refresh && refresh->attribute_.onRefreshing) { 545 refresh->attribute_.onRefreshing(); 546 } 547 return nullptr; 548 }; 549 } 550 if (attribute.onOffsetChange) { 551 OH_LOG_INFO(LOG_APP, "onOffsetChange start"); 552 desc[7].method = [](napi_env env, napi_callback_info info) -> napi_value { 553 OH_LOG_INFO(LOG_APP, "onOffsetChange callback"); 554 size_t argc = 1; 555 napi_value args[1] = {nullptr}; 556 void *data; 557 napi_get_cb_info(env, info, &argc, args, nullptr, &data); 558 double offset = 0.0; 559 napi_get_value_double(env, args[0], &offset); 560 auto refresh = reinterpret_cast<ArkUIMixedRefresh *>(data); 561 if (refresh && refresh->attribute_.onOffsetChange) { 562 refresh->attribute_.onOffsetChange(offset); 563 } 564 return nullptr; 565 }; 566 } 567 napi_value refreshAttribute = nullptr; 568 auto result = napi_create_object_with_properties(g_env, &refreshAttribute, sizeof(desc) / sizeof(desc[0]), desc); 569 if (result != napi_ok) { 570 return nullptr; 571 } 572 return refreshAttribute; 573 } 574 575 // 创建ArkTS侧的组件并保存在Native侧的封装对象中。 576 const std::shared_ptr<ArkUIMixedRefresh> ArkUIMixedRefresh::Create(const NativeRefreshAttribute &attribute) { 577 napi_handle_scope scope; 578 napi_open_handle_scope(g_env, &scope); 579 auto refresh = std::make_shared<ArkUIMixedRefresh>(); 580 auto refreshAttribute = CreateRefreshAttribute(attribute, refresh.get()); 581 if (refreshAttribute == nullptr) { 582 napi_close_handle_scope(g_env, scope); 583 return nullptr; 584 } 585 napi_value result = nullptr; 586 napi_value argv[1] = {refreshAttribute}; 587 napi_value createRefresh = nullptr; 588 napi_get_reference_value(g_env, g_createRefresh, &createRefresh); 589 // 调用ArkTS的Create函数创建ArkTS的ComponentContent。 590 napi_call_function(g_env, nullptr, createRefresh, 1, argv, &result); 591 592 // 获取ArkTS的Refresh组件。 593 napi_value componentContent = nullptr; 594 napi_get_named_property(g_env, result, "content", &componentContent); 595 ArkUI_NodeHandle handle; 596 OH_ArkUI_GetNodeHandleFromNapiValue(g_env, componentContent, &handle); 597 assert(handle); 598 // 获取ArkTS的Refresh组件的子组件插槽。 599 napi_value nodeContent = nullptr; 600 napi_get_named_property(g_env, result, "childSlot", &nodeContent); 601 ArkUI_NodeContentHandle contentHandle; 602 OH_ArkUI_GetNodeContentFromNapiValue(g_env, nodeContent, &contentHandle); 603 assert(contentHandle); 604 // 保存ArkTS的ComponentContent用于防止ArkTS侧对象释放以及后续的更新。 605 napi_ref componentContentRef; 606 napi_create_reference(g_env, componentContent, 1, &componentContentRef); 607 // 保存ArkTS的NodeContent用于防止ArkTS侧对象释放以及后续的更新。 608 napi_ref nodeContentRef; 609 napi_create_reference(g_env, nodeContent, 1, &nodeContentRef); 610 // 更新Refresh组件相关参数。 611 refresh->handle_ = handle; 612 refresh->env_ = g_env; 613 refresh->componentContent_ = componentContentRef; 614 refresh->nodeContent_ = nodeContentRef; 615 refresh->contentHandle_ = contentHandle; 616 refresh->attribute_ = attribute; 617 return refresh; 618 } 619 // 更新函数实现。 620 void ArkUIMixedRefresh::FlushMixedModeCmd() { 621 napi_handle_scope scope; 622 napi_open_handle_scope(g_env, &scope); 623 // 创建调用ArkTS接口入参。 624 auto refreshAttribute = CreateRefreshAttribute(attribute_, this); 625 if (refreshAttribute == nullptr) { 626 napi_close_handle_scope(g_env, scope); 627 return; 628 } 629 // 获取更新接口的剩余两个接口参数。 630 napi_value componentContent = nullptr; 631 napi_get_reference_value(g_env, componentContent_, &componentContent); 632 napi_value nodeContent = nullptr; 633 napi_get_reference_value(g_env, nodeContent_, &nodeContent); 634 635 napi_value argv[3] = {componentContent, nodeContent, refreshAttribute}; 636 napi_value updateRefresh = nullptr; 637 napi_get_reference_value(g_env, g_updateRefresh, &updateRefresh); 638 // 调用ArkTS的Update函数进行更新。 639 napi_value result = nullptr; 640 napi_call_function(g_env, nullptr, updateRefresh, 3, argv, &result); 641 } 642 643 napi_value ArkUIMixedRefresh::RegisterCreateAndUpdateRefresh(napi_env env, napi_callback_info info) { 644 size_t argc = 1; 645 napi_value args[1] = {nullptr}; 646 647 napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); 648 649 g_env = env; 650 napi_ref refer; 651 napi_create_reference(env, args[0], 1, &refer); 652 653 g_createRefresh = refer; 654 return nullptr; 655 } 656 657 } // namespace NativeModule 658 659 ``` 660 6616. 定时器模块相关简单实现。 662 ```c 663 // UITimer.h 664 // 定时器模块。 665 666 #ifndef MYAPPLICATION_UITIMER_H 667 #define MYAPPLICATION_UITIMER_H 668 669 #include <hilog/log.h> 670 #include <js_native_api.h> 671 #include <js_native_api_types.h> 672 #include <node_api.h> 673 #include <node_api_types.h> 674 #include <string> 675 #include <thread> 676 #include <uv.h> 677 678 namespace NativeModule { 679 680 struct UIData { 681 void *userData = nullptr; 682 int32_t count = 0; 683 int32_t totalCount = 0; 684 void (*func)(void *userData, int32_t count) = nullptr; 685 }; 686 687 napi_threadsafe_function threadSafeFunction = nullptr; 688 689 void CreateNativeTimer(napi_env env, void *userData, int32_t totalCount, void (*func)(void *userData, int32_t count)) { 690 napi_value name; 691 std::string str = "UICallback"; 692 napi_create_string_utf8(env, str.c_str(), str.size(), &name); 693 // UI主线程回调函数。 694 napi_create_threadsafe_function( 695 env, nullptr, nullptr, name, 0, 1, nullptr, nullptr, nullptr, 696 [](napi_env env, napi_value value, void *context, void *data) { 697 auto userdata = reinterpret_cast<UIData *>(data); 698 userdata->func(userdata->userData, userdata->count); 699 delete userdata; 700 }, 701 &threadSafeFunction); 702 // 启动定时器,模拟数据变化。 703 std::thread timerThread([data = userData, totalCount, func]() { 704 uv_loop_t *loop = uv_loop_new(); 705 uv_timer_t *timer = new uv_timer_t(); 706 uv_timer_init(loop, timer); 707 timer->data = new UIData{data, 0, totalCount, func}; 708 uv_timer_start( 709 timer, 710 [](uv_timer_t *handle) { 711 OH_LOG_INFO(LOG_APP, "on timeout"); 712 napi_acquire_threadsafe_function(threadSafeFunction); 713 auto *customData = reinterpret_cast<UIData *>(handle->data); 714 // 创建回调数据。 715 auto *callbackData = 716 new UIData{customData->userData, customData->count, customData->totalCount, customData->func}; 717 napi_call_threadsafe_function(threadSafeFunction, callbackData, napi_tsfn_blocking); 718 customData->count++; 719 if (customData->count > customData->totalCount) { 720 uv_timer_stop(handle); 721 delete handle; 722 delete customData; 723 } 724 delete callbackData; 725 }, 726 4000, 4000); 727 uv_run(loop, UV_RUN_DEFAULT); 728 uv_loop_delete(loop); 729 }); 730 timerThread.detach(); 731 } 732 } // namespace NativeModule 733 734 #endif // MYAPPLICATION_UITIMER_H 735 ``` 736 7377. 使用[接入ArkTS页面](ndk-access-the-arkts-page.md)章节的页面结构,并沿用[定时器模块相关简单实现](ndk-embed-arkts-components.md),将Refresh组件作为文本列表的父组件。 738 ```c 739 // MixedRefreshExample.h 740 // 混合模式示例代码。 741 742 #ifndef MYAPPLICATION_MIXEDREFRESHEXAMPLE_H 743 #define MYAPPLICATION_MIXEDREFRESHEXAMPLE_H 744 745 #include "ArkUIBaseNode.h" 746 #include "ArkUIMixedRefresh.h" 747 #include "NormalTextListExample.h" 748 #include "UITimer.h" 749 750 #include <js_native_api_types.h> 751 752 namespace NativeModule { 753 754 std::shared_ptr<ArkUIBaseNode> CreateMixedRefreshList(napi_env env) { 755 auto list = CreateTextListExample(); 756 // 混合模式创建Refresh组件并挂载List组件。 757 NativeRefreshAttribute nativeRefreshAttribute{ 758 .backgroundColor = 0xFF89CFF0, .refreshOffset = 64, .pullToRefresh = true}; 759 auto refresh = ArkUIMixedRefresh::Create(nativeRefreshAttribute); 760 refresh->AddChild(list); 761 762 // 设置混合模式下的事件。 763 refresh->SetOnOffsetChange( 764 [](float offset) { OH_LOG_INFO(LOG_APP, "on refresh offset changed: %{public}f", offset); }); 765 refresh->SetRefreshCallback([refreshPtr = refresh.get(), env]() { 766 OH_LOG_INFO(LOG_APP, "on refreshing"); 767 // 启动定时器,模拟数据获取。 768 CreateNativeTimer(env, refreshPtr, 1, [](void *userData, int32_t count) { 769 // 数据获取后关闭刷新。 770 auto refresh = reinterpret_cast<ArkUIMixedRefresh *>(userData); 771 refresh->SetRefreshState(false); 772 refresh->FlushMixedModeCmd(); 773 }); 774 }); 775 776 // 更新事件到ArkTS侧。 777 refresh->FlushMixedModeCmd(); 778 return refresh; 779 } 780 781 } // namespace NativeModule 782 783 #endif // MYAPPLICATION_MIXEDREFRESHEXAMPLE_H 784 ``` 785 786 替换入口组件创建为下拉刷新文本列表。 787 788 ```c 789 // NativeEntry.cpp 790 791 #include "NativeEntry.h" 792 793 #include "ArkUIMixedRefresh.h" 794 #include "MixedRefreshExample.h" 795 #include "NormalTextListExample.h" 796 797 #include <arkui/native_node_napi.h> 798 #include <arkui/native_type.h> 799 #include <js_native_api.h> 800 #include <uv.h> 801 802 namespace NativeModule { 803 804 napi_value CreateNativeRoot(napi_env env, napi_callback_info info) { 805 size_t argc = 1; 806 napi_value args[1] = {nullptr}; 807 808 napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); 809 810 // 获取NodeContent 811 ArkUI_NodeContentHandle contentHandle; 812 OH_ArkUI_GetNodeContentFromNapiValue(env, args[0], &contentHandle); 813 NativeEntry::GetInstance()->SetContentHandle(contentHandle); 814 815 // 创建Refresh文本列表 816 auto refresh = CreateMixedRefreshList(env); 817 818 // 保持Native侧对象到管理类中,维护生命周期。 819 NativeEntry::GetInstance()->SetRootNode(refresh); 820 return nullptr; 821 } 822 823 napi_value DestroyNativeRoot(napi_env env, napi_callback_info info) { 824 // 从管理类中释放Native侧对象。 825 NativeEntry::GetInstance()->DisposeRootNode(); 826 return nullptr; 827 } 828 829 } // namespace NativeModule 830 831 ``` 832 833