1# 接入ArkTS页面 2 3 4## 占位组件 5 6使用NDK接口构建UI界面时,需要在ArkTS页面创建用于挂载NDK接口创建组件的占位组件。占位组件类型为[ContentSlot](../reference/apis-arkui/arkui-ts/ts-components-contentSlot.md),ContentSlot能够绑定一个[NodeContent](../reference/apis-arkui/js-apis-arkui-NodeContent.md)对象,该对象可通过Node-API传递到Native侧挂载显示Native组件。 7 8- 占位组件和其他ArkTS系统组件使用方法相同。详细代码请参考[示例](#示例)。 9 ```ts 10 import { NodeContent } from '@kit.ArkUI'; 11 import nativeNode from 'libentry.so'; 12 13 @Entry 14 @Component 15 struct Index { 16 // 初始化NodeContent对象。 17 private rootSlot = new NodeContent(); 18 @State @Watch('changeNativeFlag') showNative: boolean = false; 19 20 changeNativeFlag(): void { 21 if (this.showNative) { 22 // 传递NodeContent对象用于Native创建组件的挂载显示 23 nativeNode.createNativeRoot(this.rootSlot) 24 } else { 25 // 销毁NativeModule组件 26 nativeNode.destroyNativeRoot() 27 } 28 } 29 30 build() { 31 Column() { 32 Button(this.showNative ? "HideNativeUI" : "ShowNativeUI").onClick(() => { 33 this.showNative = !this.showNative 34 }) 35 Row() { 36 // 将NodeContent和ContentSlot占位组件绑定。 37 ContentSlot(this.rootSlot) 38 }.layoutWeight(1) 39 } 40 .width('100%') 41 .height('100%') 42 } 43 } 44 ``` 45 46- 占位组件可以通过相关接口在Native侧转化为挂载对象。 47 ``` 48 ArkUI_NodeContentHandle contentHandle; 49 OH_ArkUI_GetNodeContentFromNapiValue(env, args[0], &contentHandle); 50 ``` 51 52- 挂载对象提供了相关挂载和卸载组件接口。 53 ``` 54 OH_ArkUI_NodeContent_AddNode(handle_, myNativeNode); 55 OH_ArkUI_NodeContent_RemoveNode(handle_, myNativeNode); 56 ``` 57 58 59## NDK组件模块 60 61NDK提供的UI组件能力如组件创建、树操作、属性设置、事件注册等是通过函数指针结构体(如[ArkUI_NativeNodeAPI_1](../reference/apis-arkui/_ark_u_i___native_node_a_p_i__1.md))进行暴露,该函数指针结构体可以通过[模块查询接口](../reference/apis-arkui/_ark_u_i___native_module.md#oh_arkui_getmoduleinterface)获取。 62 63``` 64ArkUI_NativeNodeAPI_1* arkUINativeNodeApi = nullptr; 65OH_ArkUI_GetModuleInterface(ARKUI_NATIVE_NODE, ArkUI_NativeNodeAPI_1, arkUINativeNodeApi); 66``` 67 68 69在获取到函数指针结构体后,可以使用该结构体内的函数实现相关UI组件操作。 70 71 72- 组件创建和销毁。 73 ``` 74 auto listNode = arkUINativeNodeApi->createNode(ARKUI_NODE_LIST); 75 arkUINativeNodeApi->disposeNode(listNode); 76 ``` 77 78 获取NDK接口支持的组件范围可以通过查询[ArkUI_NodeType](../reference/apis-arkui/_ark_u_i___native_module.md#arkui_nodetype)枚举值。 79 80- 组件树操作。 81 ``` 82 auto parent = arkUINativeNodeApi->createNode(ARKUI_NODE_STACK); 83 auto child = arkUINativeNodeApi->createNode(ARKUI_NODE_STACK); 84 arkUINativeNodeApi->addChild(parent, child); 85 arkUINativeNodeApi->removeChild(parent, child); 86 ``` 87 88- 属性设置。 89 ``` 90 auto stack = arkUINativeNodeApi->createNode(ARKUI_NODE_STACK); 91 ArkUI_NumberValue value[] = {{.f32 = 100}}; 92 ArkUI_AttributeItem item = {value, 1}; 93 arkUINativeNodeApi->setAttribute(stack, NODE_WIDTH, &item); 94 ArkUI_NumberValue value[] = {{.u32 = 0xff112233}}; 95 ArkUI_AttributeItem item = {value, 1}; 96 arkUINativeNodeApi->setAttribute(stack, NODE_BACKGROUND_COLOR, &item); 97 ``` 98 99 获取NDK接口支持的属性范围可以通过查询[ArkUI_NodeAttributeType](../reference/apis-arkui/_ark_u_i___native_module.md#arkui_nodeattributetype)枚举值。 100 101- 事件注册。 102 ``` 103 auto stack = arkUINativeNodeApi->createNode(ARKUI_NODE_STACK); 104 arkUINativeNodeApi->addNodeEventReceiver(stack, [](ArkUI_NodeEvent* event){ 105 // process event 106 }); 107 arkUINativeNodeApi->registerNodeEvent(stack, NODE_ON_CLICK, 0, nullptr); 108 ``` 109 110 获取NDK接口支持的事件范围可以通过查询[ArkUI_NodeEventType](../reference/apis-arkui/_ark_u_i___native_module.md#arkui_nodeeventtype)枚举值。 111 112 113## 示例 114 115下面的示例展示了如何使用ContentSlot挂载Native侧的文本列表。 116 117**图1** Native文本列表 118 119 120 1211. 在ArkTS页面上声明用于Native页面挂载的占位组件,并在页面创建时通知Native侧创建文本列表。 122 ```ts 123 import nativeNode from 'libentry.so'; 124 import { NodeContent } from '@kit.ArkUI'; 125 126 @Entry 127 @Component 128 struct Index { 129 // 初始化NodeContent对象。 130 private rootSlot = new NodeContent(); 131 @State @Watch('changeNativeFlag') showNative: boolean = false; 132 133 changeNativeFlag(): void { 134 if (this.showNative) { 135 // 传递NodeContent对象用于Native创建组件的挂载显示 136 nativeNode.createNativeRoot(this.rootSlot) 137 } else { 138 // 销毁NativeModule组件 139 nativeNode.destroyNativeRoot() 140 } 141 } 142 143 build() { 144 Column() { 145 Button(this.showNative ? "HideNativeUI" : "ShowNativeUI").onClick(() => { 146 this.showNative = !this.showNative 147 }) 148 Row() { 149 // 将NodeContent和ContentSlot占位组件绑定。 150 ContentSlot(this.rootSlot) 151 }.layoutWeight(1) 152 } 153 .width('100%') 154 .height('100%') 155 } 156 } 157 ``` 158 1592. 使用Native模板创建工程,并在Native侧提供Node-API的桥接方法,实现ArkTS侧的NativeNode模块接口。 160 接口声明。 161 ```ts 162 // entry/src/main/cpp/types/libentry/Index.d.ts 163 164 export const createNativeRoot: (content: Object) => void; 165 export const destroyNativeRoot: () => void; 166 ``` 167 168 Native实现。 169 ```cpp 170 // entry/src/main/cpp/napi_init.cpp 171 #include "napi/native_api.h" 172 #include "NativeEntry.h" 173 174 EXTERN_C_START 175 static napi_value Init(napi_env env, napi_value exports) { 176 // 绑定Native侧的创建组件和销毁组件。 177 napi_property_descriptor desc[] = { 178 {"createNativeRoot", nullptr, NativeModule::CreateNativeRoot, nullptr, nullptr, nullptr, napi_default, nullptr}, 179 {"destroyNativeRoot", nullptr, NativeModule::DestroyNativeRoot, nullptr, nullptr, nullptr, napi_default, nullptr}}; 180 napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc); 181 return exports; 182 } 183 EXTERN_C_END 184 185 static napi_module demoModule = { 186 .nm_version = 1, 187 .nm_flags = 0, 188 .nm_filename = nullptr, 189 .nm_register_func = Init, 190 .nm_modname = "entry", 191 .nm_priv = ((void *)0), 192 .reserved = {0}, 193 }; 194 195 extern "C" __attribute__((constructor)) void RegisterEntryModule(void) { napi_module_register(&demoModule); } 196 ``` 197 1983. 在NativeEntry.h文件中创建Native界面。 199 ```c 200 // NativeEntry.h 201 202 #ifndef MYAPPLICATION_NATIVEENTRY_H 203 #define MYAPPLICATION_NATIVEENTRY_H 204 205 #include <ArkUIBaseNode.h> 206 #include <arkui/native_type.h> 207 #include <js_native_api_types.h> 208 #include <memory.h> 209 210 namespace NativeModule { 211 212 napi_value CreateNativeRoot(napi_env env, napi_callback_info info); 213 214 napi_value DestroyNativeRoot(napi_env env, napi_callback_info info); 215 216 // 管理Native组件的生命周期和内存。 217 class NativeEntry { 218 public: 219 static NativeEntry *GetInstance() { 220 static NativeEntry nativeEntry; 221 return &nativeEntry; 222 } 223 224 void SetContentHandle(ArkUI_NodeContentHandle handle) { 225 handle_ = handle; 226 } 227 228 void SetRootNode(const std::shared_ptr<ArkUIBaseNode> &baseNode) { 229 root_ = baseNode; 230 // 添加Native组件到NodeContent上用于挂载显示。 231 OH_ArkUI_NodeContent_AddNode(handle_, root_->GetHandle()); 232 } 233 void DisposeRootNode() { 234 // 从NodeContent上卸载组件并销毁Native组件。 235 OH_ArkUI_NodeContent_RemoveNode(handle_, root_->GetHandle()); 236 root_.reset(); 237 } 238 239 private: 240 std::shared_ptr<ArkUIBaseNode> root_; 241 ArkUI_NodeContentHandle handle_; 242 }; 243 244 } // namespace NativeModule 245 246 #endif // MYAPPLICATION_NATIVEENTRY_H 247 ``` 248 249 对应实现文件。 250 ```cpp 251 // NativeEntry.cpp 252 253 #include <arkui/native_node_napi.h> 254 #include <hilog/log.h> 255 #include <js_native_api.h> 256 #include "NativeEntry.h" 257 258 namespace NativeModule { 259 260 napi_value CreateNativeRoot(napi_env env, napi_callback_info info) { 261 size_t argc = 1; 262 napi_value args[1] = {nullptr}; 263 264 napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); 265 266 // 获取NodeContent 267 ArkUI_NodeContentHandle contentHandle; 268 OH_ArkUI_GetNodeContentFromNapiValue(env, args[0], &contentHandle); 269 NativeEntry::GetInstance()->SetContentHandle(contentHandle); 270 271 // 创建文本列表 272 auto list = CreateTextListExample(); 273 274 // 保持Native侧对象到管理类中,维护生命周期。 275 NativeEntry::GetInstance()->SetRootNode(list); 276 return nullptr; 277 } 278 279 napi_value DestroyNativeRoot(napi_env env, napi_callback_info info) { 280 // 从管理类中释放Native侧对象。 281 NativeEntry::GetInstance()->DisposeRootNode(); 282 return nullptr; 283 } 284 285 } // namespace NativeModule 286 ``` 287 288 289 使用NDK 提供的C接口需要在CMakeLists.txt 中增加libace_ndk.z.so 的引用,如下所示,其中entry为工程导出的动态库名称,如当前示例使用的是默认的名称 libentry.so。 290 291 ``` 292 target_link_libraries(entry PUBLIC libace_napi.z.so libace_ndk.z.so) 293 ``` 294 2954. 由于NDK接口提供的是C接口,为了使用面向对象的方式简化编程和工程管理,这里建议使用C++进行二次封装,下面示例代码展示了示例界面中所需的列表,文本组件封装类。 296 297 1)获取ArkUI在NDK接口的入口模块[ArkUI_NativeNodeAPI_1](../reference/apis-arkui/_ark_u_i___native_node_a_p_i__1.md),该结构体模块提供了一系列组件创建、树构建、属性设置和事件注册等函数指针。 298 299 ```c 300 // NativeModule.h 301 // 提供获取ArkUI在Native侧模块的封装接口 302 303 #ifndef MYAPPLICATION_NATIVEMODULE_H 304 #define MYAPPLICATION_NATIVEMODULE_H 305 306 #include <arkui/native_node.h> 307 #include <functional> 308 #include <cassert> 309 310 #include <arkui/native_interface.h> 311 312 namespace NativeModule { 313 314 class NativeModuleInstance { 315 public: 316 static NativeModuleInstance *GetInstance() { 317 static NativeModuleInstance instance; 318 return &instance; 319 } 320 321 NativeModuleInstance() { 322 // 获取NDK接口的函数指针结构体对象,用于后续操作。 323 OH_ArkUI_GetModuleInterface(ARKUI_NATIVE_NODE, ArkUI_NativeNodeAPI_1, arkUINativeNodeApi_); 324 assert(arkUINativeNodeApi_); 325 } 326 // 暴露给其他模块使用。 327 ArkUI_NativeNodeAPI_1 *GetNativeNodeAPI() { return arkUINativeNodeApi_; } 328 329 private: 330 ArkUI_NativeNodeAPI_1 *arkUINativeNodeApi_ = nullptr; 331 }; 332 333 } // namespace NativeModule 334 335 #endif // MYAPPLICATION_NATIVEMODULE_H 336 ``` 337 338 2)提供列表,文本组件的基类对象,用于封装通用属性和事件。 339 340 ```c 341 // ArkUIBaseNode.h 342 // 提供组件树操作的基类。 343 344 #ifndef MYAPPLICATION_ARKUIBASENODE_H 345 #define MYAPPLICATION_ARKUIBASENODE_H 346 347 #include <arkui/native_type.h> 348 #include <list> 349 #include <memory> 350 351 #include "NativeModule.h" 352 353 namespace NativeModule { 354 355 class ArkUIBaseNode { 356 public: 357 explicit ArkUIBaseNode(ArkUI_NodeHandle handle) 358 : handle_(handle), nativeModule_(NativeModuleInstance::GetInstance()->GetNativeNodeAPI()) {} 359 360 virtual ~ArkUIBaseNode() { 361 // 封装析构函数,实现子节点移除功能。 362 if (!children_.empty()) { 363 for (const auto& child : children_) { 364 nativeModule_->removeChild(handle_, child->GetHandle()); 365 } 366 children_.clear(); 367 } 368 // 封装析构函数,统一回收节点资源。 369 nativeModule_->disposeNode(handle_); 370 } 371 372 void AddChild(const std::shared_ptr<ArkUIBaseNode> &child) { 373 children_.emplace_back(child); 374 OnAddChild(child); 375 } 376 377 void RemoveChild(const std::shared_ptr<ArkUIBaseNode> &child) { 378 children_.remove(child); 379 OnRemoveChild(child); 380 } 381 382 void InsertChild(const std::shared_ptr<ArkUIBaseNode> &child, int32_t index) { 383 if (index >= children_.size()) { 384 AddChild(child); 385 } else { 386 auto iter = children_.begin(); 387 std::advance(iter, index); 388 children_.insert(iter, child); 389 OnInsertChild(child, index); 390 } 391 } 392 393 ArkUI_NodeHandle GetHandle() const { return handle_; } 394 395 protected: 396 // 针对父容器子类需要重载下面的函数,实现组件挂载和卸载。 397 virtual void OnAddChild(const std::shared_ptr<ArkUIBaseNode> &child) {} 398 virtual void OnRemoveChild(const std::shared_ptr<ArkUIBaseNode> &child) {} 399 virtual void OnInsertChild(const std::shared_ptr<ArkUIBaseNode> &child, int32_t index) {} 400 401 ArkUI_NodeHandle handle_; 402 ArkUI_NativeNodeAPI_1 *nativeModule_ = nullptr; 403 404 private: 405 std::list<std::shared_ptr<ArkUIBaseNode>> children_; 406 }; 407 } // namespace NativeModule 408 409 #endif // MYAPPLICATION_ARKUIBASENODE_H 410 ``` 411 412 ```c 413 // ArkUINode.h 414 // 提供通用属性和事件的封装。 415 416 #ifndef MYAPPLICATION_ARKUINODE_H 417 #define MYAPPLICATION_ARKUINODE_H 418 419 #include "ArkUIBaseNode.h" 420 #include "NativeModule.h" 421 #include <arkui/native_node.h> 422 #include <arkui/native_type.h> 423 424 namespace NativeModule { 425 426 class ArkUINode : public ArkUIBaseNode { 427 public: 428 explicit ArkUINode(ArkUI_NodeHandle handle) : ArkUIBaseNode(handle) {} 429 430 ~ArkUINode() override {} 431 432 // NDK相关通用属性调用封装 433 void SetWidth(float width) { 434 assert(handle_); 435 ArkUI_NumberValue value[] = {{.f32 = width}}; 436 ArkUI_AttributeItem item = {value, 1}; 437 nativeModule_->setAttribute(handle_, NODE_WIDTH, &item); 438 } 439 void SetPercentWidth(float percent) { 440 assert(handle_); 441 ArkUI_NumberValue value[] = {{.f32 = percent}}; 442 ArkUI_AttributeItem item = {value, 1}; 443 nativeModule_->setAttribute(handle_, NODE_WIDTH_PERCENT, &item); 444 } 445 void SetHeight(float height) { 446 assert(handle_); 447 ArkUI_NumberValue value[] = {{.f32 = height}}; 448 ArkUI_AttributeItem item = {value, 1}; 449 nativeModule_->setAttribute(handle_, NODE_HEIGHT, &item); 450 } 451 void SetPercentHeight(float percent) { 452 assert(handle_); 453 ArkUI_NumberValue value[] = {{.f32 = percent}}; 454 ArkUI_AttributeItem item = {value, 1}; 455 nativeModule_->setAttribute(handle_, NODE_HEIGHT_PERCENT, &item); 456 } 457 void SetBackgroundColor(uint32_t color) { 458 assert(handle_); 459 ArkUI_NumberValue value[] = {{.u32 = color}}; 460 ArkUI_AttributeItem item = {value, 1}; 461 nativeModule_->setAttribute(handle_, NODE_BACKGROUND_COLOR, &item); 462 } 463 464 protected: 465 // 组件树操作的实现类对接。 466 void OnAddChild(const std::shared_ptr<ArkUIBaseNode> &child) override { 467 nativeModule_->addChild(handle_, child->GetHandle()); 468 } 469 void OnRemoveChild(const std::shared_ptr<ArkUIBaseNode> &child) override { 470 nativeModule_->removeChild(handle_, child->GetHandle()); 471 } 472 void OnInsertChild(const std::shared_ptr<ArkUIBaseNode> &child, int32_t index) override { 473 nativeModule_->insertChildAt(handle_, child->GetHandle(), index); 474 } 475 }; 476 } // namespace NativeModule 477 478 #endif // MYAPPLICATION_ARKUINODE_H 479 ``` 480 481 3)实现列表组件。 482 483 ```c 484 // ArkUIListNode.h 485 // 提供列表组件的封装。 486 487 #ifndef MYAPPLICATION_ARKUILISTNODE_H 488 #define MYAPPLICATION_ARKUILISTNODE_H 489 490 #include "ArkUINode.h" 491 492 namespace NativeModule { 493 class ArkUIListNode : public ArkUINode { 494 public: 495 ArkUIListNode() 496 : ArkUINode((NativeModuleInstance::GetInstance()->GetNativeNodeAPI())->createNode(ARKUI_NODE_LIST)) {} // 创建ArkUI的列表组件。 497 498 ~ArkUIListNode() override {} 499 // List组件的属性NDK接口封装。 500 void SetScrollBarState(bool isShow) { 501 assert(handle_); 502 ArkUI_ScrollBarDisplayMode displayMode = 503 isShow ? ARKUI_SCROLL_BAR_DISPLAY_MODE_ON : ARKUI_SCROLL_BAR_DISPLAY_MODE_OFF; 504 ArkUI_NumberValue value[] = {{.i32 = displayMode}}; 505 ArkUI_AttributeItem item = {value, 1}; 506 nativeModule_->setAttribute(handle_, NODE_SCROLL_BAR_DISPLAY_MODE, &item); 507 } 508 }; 509 } // namespace NativeModule 510 511 #endif // MYAPPLICATION_ARKUILISTNODE_H 512 ``` 513 514 4)实现列表项组件。 515 516 ```c 517 // ArkUIListItemNode.h 518 // 提供列表项的封装类。 519 520 #ifndef MYAPPLICATION_ARKUISTACKNODE_H 521 #define MYAPPLICATION_ARKUISTACKNODE_H 522 523 #include "ArkUINode.h" 524 525 namespace NativeModule { 526 class ArkUIListItemNode : public ArkUINode { 527 public: 528 ArkUIListItemNode() 529 : ArkUINode((NativeModuleInstance::GetInstance()->GetNativeNodeAPI())->createNode(ARKUI_NODE_LIST_ITEM)) {} 530 }; 531 } // namespace NativeModule 532 533 #endif // MYAPPLICATION_ARKUISTACKNODE_H 534 ``` 535 536 5)实现文本组件。 537 538 ```c 539 // ArkUITextNode.h 540 // 实现文本组件的封装类。 541 542 #ifndef MYAPPLICATION_ARKUITEXTNODE_H 543 #define MYAPPLICATION_ARKUITEXTNODE_H 544 545 #include "ArkUINode.h" 546 547 #include <string> 548 549 namespace NativeModule { 550 class ArkUITextNode : public ArkUINode { 551 public: 552 ArkUITextNode() 553 : ArkUINode((NativeModuleInstance::GetInstance()->GetNativeNodeAPI())->createNode(ARKUI_NODE_TEXT)) {} 554 // 文本属性NDK接口封装。 555 void SetFontSize(float fontSize) { 556 assert(handle_); 557 ArkUI_NumberValue value[] = {{.f32 = fontSize}}; 558 ArkUI_AttributeItem item = {value, 1}; 559 nativeModule_->setAttribute(handle_, NODE_FONT_SIZE, &item); 560 } 561 void SetFontColor(uint32_t color) { 562 assert(handle_); 563 ArkUI_NumberValue value[] = {{.u32 = color}}; 564 ArkUI_AttributeItem item = {value, 1}; 565 nativeModule_->setAttribute(handle_, NODE_FONT_COLOR, &item); 566 } 567 void SetTextContent(const std::string &content) { 568 assert(handle_); 569 ArkUI_AttributeItem item = {nullptr, 0, content.c_str()}; 570 nativeModule_->setAttribute(handle_, NODE_TEXT_CONTENT, &item); 571 } 572 void SetTextAlign(ArkUI_TextAlignment align) { 573 assert(handle_); 574 ArkUI_NumberValue value[] = {{.i32 = align}}; 575 ArkUI_AttributeItem item = {value, 1}; 576 nativeModule_->setAttribute(handle_, NODE_TEXT_ALIGN, &item); 577 } 578 }; 579 } // namespace NativeModule 580 581 #endif // MYAPPLICATION_ARKUITEXTNODE_H 582 ``` 583 5845. 完善步骤3的CreateTextListExample函数,实现Native文本列表的创建和挂载显示。 585 ```c 586 // NormalTextListExample.h 587 // 自定义NDK接口入口函数。 588 589 #ifndef MYAPPLICATION_NORMALTEXTLISTEXAMPLE_H 590 #define MYAPPLICATION_NORMALTEXTLISTEXAMPLE_H 591 592 #include "ArkUIBaseNode.h" 593 #include "ArkUIListItemNode.h" 594 #include "ArkUIListNode.h" 595 #include "ArkUITextNode.h" 596 #include <hilog/log.h> 597 598 namespace NativeModule { 599 600 std::shared_ptr<ArkUIBaseNode> CreateTextListExample() { 601 // 创建组件并挂载 602 // 1:使用智能指针创建List组件。 603 auto list = std::make_shared<ArkUIListNode>(); 604 list->SetPercentWidth(1); 605 list->SetPercentHeight(1); 606 list->SetScrollBarState(true); 607 // 2:创建ListItem子组件并挂载到List上。 608 for (int32_t i = 0; i < 30; ++i) { 609 auto listItem = std::make_shared<ArkUIListItemNode>(); 610 auto textNode = std::make_shared<ArkUITextNode>(); 611 textNode->SetTextContent(std::to_string(i)); 612 textNode->SetFontSize(16); 613 textNode->SetFontColor(0xFFff00ff); 614 textNode->SetPercentWidth(1); 615 textNode->SetWidth(300); 616 textNode->SetHeight(100); 617 textNode->SetBackgroundColor(0xFFfffacd); 618 textNode->SetTextAlign(ARKUI_TEXT_ALIGNMENT_CENTER); 619 listItem->InsertChild(textNode, i); 620 list->AddChild(listItem); 621 } 622 return list; 623 } 624 } // namespace NativeModule 625 626 #endif // MYAPPLICATION_NORMALTEXTLISTEXAMPLE_H 627 ```