1# Developing a Long List with Lazy Loading
2
3For the **List**, **Grid**, **WaterFlow**, and **Swiper** components, the [NodeAdapter](../reference/apis-arkui/_ark_u_i___native_module.md#arkui_nodeadapterhandle) object is provided as an alternative to the ArkTS **LazyForEach** feature for on-demand child component generation. The specific attribute enumeration values are as follows:
4
5- **List**: **NODE_LIST_NODE_ADAPTER**
6- **Grid**: **NODE_GRID_NODE_ADAPTER**
7- **WaterFlow**: **NODE_WATER_FLOW_NODE_ADAPTER**
8- **Swiper**: **NODE_SWIPER_NODE_ADAPTER**
9
10Key specifications of **NodeAdapter** include:
11
12
13- Nodes with **NodeAdapter** set do not support direct child addition APIs like **addChild**. Child components are managed entirely by **NodeAdapter**. If a parent component already has child nodes, setting **NodeAdapter** will fail and return an error code.
14
15- **NodeAdapter** uses events to notify you to generate components on demand. You must register an [event listener](../reference/apis-arkui/_ark_u_i___native_module.md#oh_arkui_nodeadapter_registereventreceiver) and handle logic within listener events, which are defined by [ArkUI_NodeAdapterEventType](../reference/apis-arkui/_ark_u_i___native_module.md#arkui_nodeadaptereventtype). **NodeAdapter** does not automatically release off-screen component objects; you must manage object release or caching during the [NODE_ADAPTER_EVENT_ON_REMOVE_NODE_FROM_ADAPTER](../reference/apis-arkui/_ark_u_i___native_module.md#arkui_nodeadaptereventtype) event. The following image illustrates the event triggering mechanism in a typical list scrolling scenario.
16  ![en-us_image_0000001949769409](figures/en-us_image_0000001949769409.png)
17
18
19The following example optimizes the code in the [Integrating with ArkTS Pages](ndk-access-the-arkts-page.md) section, by introducing a lazy loading mechanism for a text list:
20
21
221. Integrate ArkTS into your project. For details, see [Integrating with ArkTS Pages](ndk-access-the-arkts-page.md).
23
242. Implement lazy loading adapter functionality.
25   ```
26   // ArkUIListItemAdapter
27   // Code for lazy loading functionality in a text list.
28
29   #ifndef MYAPPLICATION_ARKUILISTITEMADAPTER_H
30   #define MYAPPLICATION_ARKUILISTITEMADAPTER_H
31
32   #include <arkui/native_node.h>
33   #include <stack>
34   #include <string>
35   #include <unordered_set>
36
37   #include "ArkUIListItemNode.h"
38   #include "ArkUITextNode.h"
39   #include "nativeModule.h"
40
41   namespace NativeModule {
42
43   class ArkUIListItemAdapter {
44   public:
45       ArkUIListItemAdapter()
46           : module_(NativeModuleInstance::GetInstance()->GetNativeNodeAPI()), handle_(OH_ArkUI_NodeAdapter_Create()) { // Use the NodeAdapter creation function.
47           // Initialize lazy loading data.
48           for (int32_t i = 0; i < 1000; i++) {
49               data_.emplace_back(std::to_string(i));
50           }
51           // Set lazy loading data.
52           OH_ArkUI_NodeAdapter_SetTotalNodeCount(handle_, data_.size());
53           // Register the event receiver for lazy loading.
54           OH_ArkUI_NodeAdapter_RegisterEventReceiver(handle_, this, OnStaticAdapterEvent);
55       }
56
57       ~ArkUIListItemAdapter() {
58           // Release created components.
59           while (!cachedItems_.empty()) {
60               cachedItems_.pop();
61           }
62           items_.clear();
63           // Release adapter resources.
64           OH_ArkUI_NodeAdapter_UnregisterEventReceiver(handle_);
65           OH_ArkUI_NodeAdapter_Dispose(handle_);
66       }
67
68       ArkUI_NodeAdapterHandle GetHandle() const { return handle_; }
69
70       void RemoveItem(int32_t index) {
71           // Remove the item at the specified index.
72           data_.erase(data_.begin() + index);
73           // If the index change affects the visibility of items in the visible area, the NODE_ADAPTER_EVENT_ON_REMOVE_NODE_FROM_ADAPTER event will be triggered to remove the element.
74           // If items are added, the NODE_ADAPTER_EVENT_ON_GET_NODE_ID and NODE_ADAPTER_EVENT_ON_ADD_NODE_TO_ADAPTER events will be triggered accordingly.
75           OH_ArkUI_NodeAdapter_RemoveItem(handle_, index, 1);
76           // Update the new total count.
77           OH_ArkUI_NodeAdapter_SetTotalNodeCount(handle_, data_.size());
78       }
79
80       void InsertItem(int32_t index, const std::string &value) {
81           data_.insert(data_.begin() + index, value);
82           // If the index change affects the visibility of elements in the visible area, the NODE_ADAPTER_EVENT_ON_GET_NODE_ID and NODE_ADAPTER_EVENT_ON_ADD_NODE_TO_ADAPTER events will be triggered.
83           // If items are removed, the NODE_ADAPTER_EVENT_ON_REMOVE_NODE_FROM_ADAPTER event will be triggered accordingly.
84           OH_ArkUI_NodeAdapter_InsertItem(handle_, index, 1);
85           // Update the new total count.
86           OH_ArkUI_NodeAdapter_SetTotalNodeCount(handle_, data_.size());
87       }
88
89       void MoveItem(int32_t oldIndex, int32_t newIndex) {
90           auto temp = data_[oldIndex];
91           data_.insert(data_.begin() + newIndex, temp);
92           data_.erase(data_.begin() + oldIndex);
93           // If the move changes the visibility of items within the visible area, the corresponding events will be triggered.
94           OH_ArkUI_NodeAdapter_MoveItem(handle_, oldIndex, newIndex);
95       }
96
97       void ReloadItem(int32_t index, const std::string &value) {
98           data_[index] = value;
99           // If the index is within the visible area, first trigger the NODE_ADAPTER_EVENT_ON_REMOVE_NODE_FROM_ADAPTER event to remove the old item,
100           // then trigger the NODE_ADAPTER_EVENT_ON_GET_NODE_ID and NODE_ADAPTER_EVENT_ON_ADD_NODE_TO_ADAPTER events.
101           OH_ArkUI_NodeAdapter_ReloadItem(handle_, index, 1);
102       }
103
104       void ReloadAllItem() {
105           std::reverse(data_.begin(), data_.end());
106           // In the scenario where all items are reloaded, the NODE_ADAPTER_EVENT_ON_GET_NODE_ID event will be triggered to obtain new component IDs,
107           // compare the new component IDs, and reuse those whose IDs have not changed,
108           // for items with new IDs, trigger the NODE_ADAPTER_EVENT_ON_ADD_NODE_TO_ADAPTER event to create new components,
109           // then identify any unused IDs from the old data and call NODE_ADAPTER_EVENT_ON_REMOVE_NODE_FROM_ADAPTER to remove the old items.
110           OH_ArkUI_NodeAdapter_ReloadAllItems(handle_);
111       }
112
113   private:
114       static void OnStaticAdapterEvent(ArkUI_NodeAdapterEvent *event) {
115           // Obtain the instance object and invoke its event callback.
116           auto itemAdapter = reinterpret_cast<ArkUIListItemAdapter *>(OH_ArkUI_NodeAdapterEvent_GetUserData(event));
117           itemAdapter->OnAdapterEvent(event);
118       }
119
120       void OnAdapterEvent(ArkUI_NodeAdapterEvent *event) {
121           auto type = OH_ArkUI_NodeAdapterEvent_GetType(event);
122           switch (type) {
123           case NODE_ADAPTER_EVENT_ON_GET_NODE_ID:
124               OnNewItemIdCreated(event);
125               break;
126           case NODE_ADAPTER_EVENT_ON_ADD_NODE_TO_ADAPTER:
127               OnNewItemAttached(event);
128               break;
129           case NODE_ADAPTER_EVENT_ON_REMOVE_NODE_FROM_ADAPTER:
130               OnItemDetached(event);
131               break;
132           default:
133               break;
134           }
135       }
136
137       // Assign IDs to items that need to be displayed, used for element diffing in the ReloadAllItems scenario.
138       void OnNewItemIdCreated(ArkUI_NodeAdapterEvent *event) {
139           auto index = OH_ArkUI_NodeAdapterEvent_GetItemIndex(event);
140           static std::hash<std::string> hashId = std::hash<std::string>();
141           auto id = hashId(data_[index]);
142           OH_ArkUI_NodeAdapterEvent_SetNodeId(event, id);
143       }
144
145       // Handle the display of new items in the visible area.
146       void OnNewItemAttached(ArkUI_NodeAdapterEvent *event) {
147           auto index = OH_ArkUI_NodeAdapterEvent_GetItemIndex(event);
148           ArkUI_NodeHandle handle = nullptr;
149           if (!cachedItems_.empty()) {
150               // Use and update the recycled item from the cache.
151               auto recycledItem = cachedItems_.top();
152               auto textItem = std::dynamic_pointer_cast<ArkUITextNode>(recycledItem->GetChildren().back());
153               textItem->SetTextContent(data_[index]);
154               handle = recycledItem->GetHandle();
155               // Release the reference from the cache.
156               cachedItems_.pop();
157           } else {
158               // Create a new item.
159               auto listItem = std::make_shared<ArkUIListItemNode>();
160               auto textNode = std::make_shared<ArkUITextNode>();
161               textNode->SetTextContent(data_[index]);
162               textNode->SetFontSize(16);
163               textNode->SetPercentWidth(1);
164               textNode->SetHeight(100);
165               textNode->SetBackgroundColor(0xFFfffacd);
166               textNode->SetTextAlign(ARKUI_TEXT_ALIGNMENT_CENTER);
167               listItem->AddChild(textNode);
168               listItem->RegisterOnClick([index]() { OH_LOG_INFO(LOG_APP, "on %{public}d list item click", index); });
169               handle = listItem->GetHandle();
170               // Keep a reference to the text list item.
171               items_.emplace(handle, listItem);
172           }
173           // Set the item to be displayed.
174           OH_ArkUI_NodeAdapterEvent_SetItem(event, handle);
175       }
176
177       // Remove an item from the visible area.
178       void OnItemDetached(ArkUI_NodeAdapterEvent *event) {
179           auto item = OH_ArkUI_NodeAdapterEvent_GetRemovedNode(event);
180           // Place the item in the cache pool for recycling and reuse.
181           cachedItems_.emplace(items_[item]);
182       }
183
184
185       std::vector<std::string> data_;
186       ArkUI_NativeNodeAPI_1 *module_ = nullptr;
187       ArkUI_NodeAdapterHandle handle_ = nullptr;
188
189       // Manage items generated by the NodeAdapter.
190       std::unordered_map<ArkUI_NodeHandle, std::shared_ptr<ArkUIListItemNode>> items_;
191
192       // Manage the component reuse pool.
193       std::stack<std::shared_ptr<ArkUIListItemNode>> cachedItems_;
194   };
195
196   } // namespace NativeModule
197
198   #endif // MYAPPLICATION_ARKUILISTITEMADAPTER_H
199   ```
200
2013. Enhance the encapsulated list class object used in the [Integrating with ArkTS Pages](ndk-access-the-arkts-page.md) section with additional lazy loading capabilities.
202   ```
203   // ArkUIListNode.h
204   // Encapsulated list class object.
205
206   #ifndef MYAPPLICATION_ARKUILISTNODE_H
207   #define MYAPPLICATION_ARKUILISTNODE_H
208
209   #include "ArkUIListItemAdapter.h"
210   #include "ArkUINode.h"
211   #include <hilog/log.h>
212
213   namespace NativeModule {
214   class ArkUIListNode : public ArkUINode {
215   public:
216       ArkUIListNode()
217           : ArkUINode((NativeModuleInstance::GetInstance()->GetNativeNodeAPI())->createNode(ARKUI_NODE_LIST)) {}
218
219       ~ArkUIListNode() override {
220           nativeModule_->unregisterNodeEvent(handle_, NODE_LIST_ON_SCROLL_INDEX);
221           if (adapter_) {
222               // Unmount UI components associated with the adapter upon destruction.
223               nativeModule_->resetAttribute(handle_, NODE_LIST_NODE_ADAPTER);
224               adapter_.reset();
225           }
226       }
227
228       void SetScrollBarState(bool isShow) {
229           assert(handle_);
230           ArkUI_ScrollBarDisplayMode displayMode =
231               isShow ? ARKUI_SCROLL_BAR_DISPLAY_MODE_ON : ARKUI_SCROLL_BAR_DISPLAY_MODE_OFF;
232           ArkUI_NumberValue value[] = {{.i32 = displayMode}};
233           ArkUI_AttributeItem item = {value, 1};
234           nativeModule_->setAttribute(handle_, NODE_SCROLL_BAR_DISPLAY_MODE, &item);
235       }
236
237       void RegisterOnScrollIndex(const std::function<void(int32_t index)> &onScrollIndex) {
238           assert(handle_);
239           onScrollIndex_ = onScrollIndex;
240           nativeModule_->registerNodeEvent(handle_, NODE_LIST_ON_SCROLL_INDEX, 0, nullptr);
241       }
242       // Import the lazy loading module.
243       void SetLazyAdapter(const std::shared_ptr<ArkUIListItemAdapter> &adapter) {
244           assert(handle_);
245           ArkUI_AttributeItem item{nullptr, 0, nullptr, adapter->GetHandle()};
246           nativeModule_->setAttribute(handle_, NODE_LIST_NODE_ADAPTER, &item);
247           adapter_ = adapter;
248       }
249
250   protected:
251       void OnNodeEvent(ArkUI_NodeEvent *event) override {
252           auto eventType = OH_ArkUI_NodeEvent_GetEventType(event);
253           switch (eventType) {
254           case NODE_LIST_ON_SCROLL_INDEX: {
255               auto index = OH_ArkUI_NodeEvent_GetNodeComponentEvent(event)->data[0];
256               if (onScrollIndex_) {
257                   onScrollIndex_(index.i32);
258               }
259           }
260           default: {
261           }
262           }
263       }
264
265   private:
266       std::function<void(int32_t index)> onScrollIndex_;
267
268       std::shared_ptr<ArkUIListItemAdapter> adapter_;
269   };
270   } // namespace NativeModule
271
272   #endif // MYAPPLICATION_ARKUILISTNODE_H
273   ```
274
2754. Write code for lazy loading of a list.
276   ```
277   // ArkUILazyTextListExample
278   // Sample code for lazy loading a list.
279
280   #ifndef MYAPPLICATION_LAZYTEXTLISTEXAMPLE_H
281   #define MYAPPLICATION_LAZYTEXTLISTEXAMPLE_H
282
283   #include "ArkUIBaseNode.h"
284   #include "ArkUIListNode.h"
285   #include "UITimer.h"
286   #include <thread>
287   #include <uv.h>
288
289   namespace NativeModule {
290
291   std::shared_ptr<ArkUIBaseNode> CreateLazyTextListExample(napi_env env) {
292       // Create components and mount them.
293       // 1: Create a List component.
294       auto list = std::make_shared<ArkUIListNode>();
295       list->SetPercentWidth(1);
296       list->SetPercentHeight(1);
297       // 2: Create ListItem child components for lazy loading and mount them to the List component.
298       auto adapter = std::make_shared<ArkUIListItemAdapter>();
299       list->SetLazyAdapter(adapter);
300
301       // 3: Simulate lazy loading operations.
302       CreateNativeTimer(env, adapter.get(), 4, [](void *userdata, int32_t count) {
303           auto adapter = reinterpret_cast<ArkUIListItemAdapter *>(userdata);
304           switch (count) {
305           case 0: {
306               // Remove the 0th item.
307               adapter->RemoveItem(0);
308               break;
309           }
310           case 1: {
311               // Insert the 0th item.
312               adapter->InsertItem(0, "0");
313               break;
314           }
315           case 2: {
316               // Move an item to a new position.
317               adapter->MoveItem(0, 2);
318               break;
319           }
320           case 3: {
321               // Reload a single item.
322               adapter->ReloadItem(0, "1112");
323               break;
324           }
325           case 4: {
326               // Reload all items.
327               adapter->ReloadAllItem();
328               break;
329           }
330           default: {
331           }
332           }
333       });
334
335       // 3: Register list-related listening events.
336       list->RegisterOnScrollIndex([](int32_t index) { OH_LOG_INFO(LOG_APP, "on list scroll index: %{public}d", index); });
337       // 4: Register the appear event.
338       list->RegisterOnAppear([]() { OH_LOG_INFO(LOG_APP, "on list mount to tree"); });
339       // 4: Register the disappear event.
340       list->RegisterOnDisappear([]() { OH_LOG_INFO(LOG_APP, "on list unmount from tree"); });
341       return list;
342   }
343   } // namespace NativeModule
344
345   #endif // MYAPPLICATION_LAZYTEXTLISTEXAMPLE_H
346   ```
347
3485. Implement a simple timer module.
349   ```
350   // UITimer.h
351   // Timer module.
352
353   #ifndef MYAPPLICATION_UITIMER_H
354   #define MYAPPLICATION_UITIMER_H
355
356   #include <hilog/log.h>
357   #include <js_native_api.h>
358   #include <js_native_api_types.h>
359   #include <node_api.h>
360   #include <node_api_types.h>
361   #include <string>
362   #include <thread>
363   #include <uv.h>
364
365   namespace NativeModule {
366
367   struct UIData {
368       void *userData = nullptr;
369       int32_t count = 0;
370       int32_t totalCount = 0;
371       void (*func)(void *userData, int32_t count) = nullptr;
372   };
373
374   napi_threadsafe_function threadSafeFunction = nullptr;
375
376   void CreateNativeTimer(napi_env env, void *userData, int32_t totalCount, void (*func)(void *userData, int32_t count)) {
377       napi_value name;
378       std::string str = "UICallback";
379       napi_create_string_utf8(env, str.c_str(), str.size(), &name);
380       // UI main thread callback function.
381       napi_create_threadsafe_function(
382           env, nullptr, nullptr, name, 0, 1, nullptr, nullptr, nullptr,
383           [](napi_env env, napi_value value, void *context, void *data) {
384               auto userdata = reinterpret_cast<UIData *>(data);
385               userdata->func(userdata->userData, userdata->count);
386               delete userdata;
387           },
388           &threadSafeFunction);
389       // Start the timer to simulate data changes.
390       std::thread timerThread([data = userData, totalCount, func]() {
391           uv_loop_t *loop = uv_loop_new();
392           uv_timer_t *timer = new uv_timer_t();
393           uv_timer_init(loop, timer);
394           timer->data = new UIData{data, 0, totalCount, func};
395           uv_timer_start(
396               timer,
397               [](uv_timer_t *handle) {
398                   OH_LOG_INFO(LOG_APP, "on timeout");
399                   napi_acquire_threadsafe_function(threadSafeFunction);
400                   auto *customData = reinterpret_cast<UIData *>(handle->data);
401                   // Create callback data.
402                   auto *callbackData =
403                       new UIData{customData->userData, customData->count, customData->totalCount, customData->func};
404                   napi_call_threadsafe_function(threadSafeFunction, callbackData, napi_tsfn_blocking);
405                   customData->count++;
406                   if (customData->count > customData->totalCount) {
407                       uv_timer_stop(handle);
408                       delete handle;
409                       delete customData;
410                   }
411               },
412               4000, 4000);
413           uv_run(loop, UV_RUN_DEFAULT);
414           uv_loop_delete(loop);
415       });
416       timerThread.detach();
417   }
418   } // namespace NativeModule
419
420   #endif // MYAPPLICATION_UITIMER_H
421   ```
422
4236. Mount the lazy loading example code onto the **ContentSlot** as described in the [Integrating with ArkTS Pages](ndk-access-the-arkts-page.md) section.
424   ```
425   // NDK API entry mount file.
426
427   #include "NativeEntry.h"
428
429   #include "ArkUIMixedRefresh.h"
430   #include "LazyTextListExample.h"
431   #include "MixedRefreshExample.h"
432   #include "TextListExample.h"
433
434   #include <arkui/native_node_napi.h>
435   #include <arkui/native_type.h>
436   #include <js_native_api.h>
437   #include <uv.h>
438
439   namespace NativeModule {
440   namespace {
441   napi_env g_env;
442   }
443
444   napi_env GetNapiEnv() { return g_env; }
445
446   napi_value CreateNativeRoot(napi_env env, napi_callback_info info) {
447       size_t argc = 1;
448       napi_value args[1] = {nullptr};
449
450       napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
451
452       // Obtain NodeContent.
453       ArkUI_NodeContentHandle contentHandle;
454       OH_ArkUI_GetNodeContentFromNapiValue(env, args[0], &contentHandle);
455       NativeEntry::GetInstance()->SetContentHandle(contentHandle);
456
457       // Create a lazy-loaded text list.
458       auto node = CreateLazyTextListExample(env);
459
460       // Keep the native side object in the management class to maintain its lifecycle.
461       NativeEntry::GetInstance()->SetRootNode(node);
462       g_env = env;
463       return nullptr;
464   }
465
466   napi_value DestroyNativeRoot(napi_env env, napi_callback_info info) {
467       // Release the native side object from the management class.
468       NativeEntry::GetInstance()->DisposeRootNode();
469       return nullptr;
470   }
471
472   } // namespace NativeModule
473   ```
474