harmony 鸿蒙Developing a Long List with Lazy Loading
Developing a Long List with Lazy Loading
For the List, Grid, WaterFlow, and Swiper components, the NodeAdapter 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:
- List: NODE_LIST_NODE_ADAPTER
- Grid: NODE_GRID_NODE_ADAPTER
- WaterFlow: NODE_WATER_FLOW_NODE_ADAPTER
- Swiper: NODE_SWIPER_NODE_ADAPTER
Key specifications of NodeAdapter include:
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.
NodeAdapter uses events to notify you to generate components on demand. You must register an event listener and handle logic within listener events, which are defined by 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 event. The following image illustrates the event triggering mechanism in a typical list scrolling scenario.
The following example optimizes the code in the Integrating with ArkTS Pages section, by introducing a lazy loading mechanism for a text list:
Integrate ArkTS into your project. For details, see Integrating with ArkTS Pages.
Implement lazy loading adapter functionality. “` // ArkUIListItemAdapter // Code for lazy loading functionality in a text list.
#ifndef MYAPPLICATION_ARKUILISTITEMADAPTER_H #define MYAPPLICATION_ARKUILISTITEMADAPTER_H
#include
#include “ArkUIListItemNode.h” #include “ArkUITextNode.h” #include “nativeModule.h”
namespace NativeModule {
class ArkUIListItemAdapter { public: ArkUIListItemAdapter() : module(NativeModuleInstance::GetInstance()->GetNativeNodeAPI()), handle(OH_ArkUI_NodeAdapter_Create()) { // Use the NodeAdapter creation function. // Initialize lazy loading data. for (int32t i = 0; i < 1000; i++) { data.emplace_back(std::to_string(i)); } // Set lazy loading data. OH_ArkUI_NodeAdapterSetTotalNodeCount(handle, data_.size()); // Register the event receiver for lazy loading. OH_ArkUI_NodeAdapterRegisterEventReceiver(handle, this, OnStaticAdapterEvent); }
~ArkUIListItemAdapter() {
// Release created components.
while (!cachedItems_.empty()) {
cachedItems_.pop();
}
items_.clear();
// Release adapter resources.
OH_ArkUI_NodeAdapter_UnregisterEventReceiver(handle_);
OH_ArkUI_NodeAdapter_Dispose(handle_);
}
ArkUI_NodeAdapterHandle GetHandle() const { return handle_; }
void RemoveItem(int32_t index) {
// Remove the item at the specified index.
data_.erase(data_.begin() + index);
// 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.
// 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.
OH_ArkUI_NodeAdapter_RemoveItem(handle_, index, 1);
// Update the new total count.
OH_ArkUI_NodeAdapter_SetTotalNodeCount(handle_, data_.size());
}
void InsertItem(int32_t index, const std::string &value) {
data_.insert(data_.begin() + index, value);
// 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.
// If items are removed, the NODE_ADAPTER_EVENT_ON_REMOVE_NODE_FROM_ADAPTER event will be triggered accordingly.
OH_ArkUI_NodeAdapter_InsertItem(handle_, index, 1);
// Update the new total count.
OH_ArkUI_NodeAdapter_SetTotalNodeCount(handle_, data_.size());
}
void MoveItem(int32_t oldIndex, int32_t newIndex) {
auto temp = data_[oldIndex];
data_.insert(data_.begin() + newIndex, temp);
data_.erase(data_.begin() + oldIndex);
// If the move changes the visibility of items within the visible area, the corresponding events will be triggered.
OH_ArkUI_NodeAdapter_MoveItem(handle_, oldIndex, newIndex);
}
void ReloadItem(int32_t index, const std::string &value) {
data_[index] = value;
// 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,
// then trigger the NODE_ADAPTER_EVENT_ON_GET_NODE_ID and NODE_ADAPTER_EVENT_ON_ADD_NODE_TO_ADAPTER events.
OH_ArkUI_NodeAdapter_ReloadItem(handle_, index, 1);
}
void ReloadAllItem() {
std::reverse(data_.begin(), data_.end());
// 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,
// compare the new component IDs, and reuse those whose IDs have not changed,
// for items with new IDs, trigger the NODE_ADAPTER_EVENT_ON_ADD_NODE_TO_ADAPTER event to create new components,
// then identify any unused IDs from the old data and call NODE_ADAPTER_EVENT_ON_REMOVE_NODE_FROM_ADAPTER to remove the old items.
OH_ArkUI_NodeAdapter_ReloadAllItems(handle_);
}
private:
static void OnStaticAdapterEvent(ArkUI_NodeAdapterEvent *event) {
// Obtain the instance object and invoke its event callback.
auto itemAdapter = reinterpret_cast
void OnAdapterEvent(ArkUI_NodeAdapterEvent *event) {
auto type = OH_ArkUI_NodeAdapterEvent_GetType(event);
switch (type) {
case NODE_ADAPTER_EVENT_ON_GET_NODE_ID:
OnNewItemIdCreated(event);
break;
case NODE_ADAPTER_EVENT_ON_ADD_NODE_TO_ADAPTER:
OnNewItemAttached(event);
break;
case NODE_ADAPTER_EVENT_ON_REMOVE_NODE_FROM_ADAPTER:
OnItemDetached(event);
break;
default:
break;
}
}
// Assign IDs to items that need to be displayed, used for element diffing in the ReloadAllItems scenario.
void OnNewItemIdCreated(ArkUI_NodeAdapterEvent *event) {
auto index = OH_ArkUI_NodeAdapterEvent_GetItemIndex(event);
static std::hash<std::string> hashId = std::hash<std::string>();
auto id = hashId(data_[index]);
OH_ArkUI_NodeAdapterEvent_SetNodeId(event, id);
}
// Handle the display of new items in the visible area.
void OnNewItemAttached(ArkUI_NodeAdapterEvent *event) {
auto index = OH_ArkUI_NodeAdapterEvent_GetItemIndex(event);
ArkUI_NodeHandle handle = nullptr;
if (!cachedItems_.empty()) {
// Use and update the recycled item from the cache.
auto recycledItem = cachedItems_.top();
auto textItem = std::dynamic_pointer_cast<ArkUITextNode>(recycledItem->GetChildren().back());
textItem->SetTextContent(data_[index]);
handle = recycledItem->GetHandle();
// Release the reference from the cache.
cachedItems_.pop();
} else {
// Create a new item.
auto listItem = std::make_shared<ArkUIListItemNode>();
auto textNode = std::make_shared<ArkUITextNode>();
textNode->SetTextContent(data_[index]);
textNode->SetFontSize(16);
textNode->SetPercentWidth(1);
textNode->SetHeight(100);
textNode->SetBackgroundColor(0xFFfffacd);
textNode->SetTextAlign(ARKUI_TEXT_ALIGNMENT_CENTER);
listItem->AddChild(textNode);
listItem->RegisterOnClick([index]() { OH_LOG_INFO(LOG_APP, "on %{public}d list item click", index); });
handle = listItem->GetHandle();
// Keep a reference to the text list item.
items_.emplace(handle, listItem);
}
// Set the item to be displayed.
OH_ArkUI_NodeAdapterEvent_SetItem(event, handle);
}
// Remove an item from the visible area.
void OnItemDetached(ArkUI_NodeAdapterEvent *event) {
auto item = OH_ArkUI_NodeAdapterEvent_GetRemovedNode(event);
// Place the item in the cache pool for recycling and reuse.
cachedItems_.emplace(items_[item]);
}
std::vector<std::string> data_;
ArkUI_NativeNodeAPI_1 *module_ = nullptr;
ArkUI_NodeAdapterHandle handle_ = nullptr;
// Manage items generated by the NodeAdapter.
std::unordered_map<ArkUI_NodeHandle, std::shared_ptr<ArkUIListItemNode>> items_;
// Manage the component reuse pool.
std::stack<std::shared_ptr<ArkUIListItemNode>> cachedItems_;
};
} // namespace NativeModule
#endif // MYAPPLICATION_ARKUILISTITEMADAPTER_H
3. Enhance the encapsulated list class object used in the [Integrating with ArkTS Pages](https://m.seaxiang.com/blog/mlDQQ5) section with additional lazy loading capabilities.
// ArkUIListNode.h // Encapsulated list class object.
#ifndef MYAPPLICATION_ARKUILISTNODE_H #define MYAPPLICATION_ARKUILISTNODE_H
#include “ArkUIListItemAdapter.h”
#include “ArkUINode.h”
#include
namespace NativeModule { class ArkUIListNode : public ArkUINode { public: ArkUIListNode() : ArkUINode((NativeModuleInstance::GetInstance()->GetNativeNodeAPI())->createNode(ARKUI_NODE_LIST)) {}
~ArkUIListNode() override {
nativeModule_->unregisterNodeEvent(handle_, NODE_LIST_ON_SCROLL_INDEX);
if (adapter_) {
// Unmount UI components associated with the adapter upon destruction.
nativeModule_->resetAttribute(handle_, NODE_LIST_NODE_ADAPTER);
adapter_.reset();
}
}
void SetScrollBarState(bool isShow) {
assert(handle_);
ArkUI_ScrollBarDisplayMode displayMode =
isShow ? ARKUI_SCROLL_BAR_DISPLAY_MODE_ON : ARKUI_SCROLL_BAR_DISPLAY_MODE_OFF;
ArkUI_NumberValue value[] = {{.i32 = displayMode}};
ArkUI_AttributeItem item = {value, 1};
nativeModule_->setAttribute(handle_, NODE_SCROLL_BAR_DISPLAY_MODE, &item);
}
void RegisterOnScrollIndex(const std::function<void(int32_t index)> &onScrollIndex) {
assert(handle_);
onScrollIndex_ = onScrollIndex;
nativeModule_->registerNodeEvent(handle_, NODE_LIST_ON_SCROLL_INDEX, 0, nullptr);
}
// Import the lazy loading module.
void SetLazyAdapter(const std::shared_ptr<ArkUIListItemAdapter> &adapter) {
assert(handle_);
ArkUI_AttributeItem item{nullptr, 0, nullptr, adapter->GetHandle()};
nativeModule_->setAttribute(handle_, NODE_LIST_NODE_ADAPTER, &item);
adapter_ = adapter;
}
protected: void OnNodeEvent(ArkUI_NodeEvent *event) override { auto eventType = OH_ArkUI_NodeEvent_GetEventType(event); switch (eventType) { case NODE_LIST_ON_SCROLL_INDEX: { auto index = OH_ArkUI_NodeEventGetNodeComponentEvent(event)->data[0]; if (onScrollIndex) { onScrollIndex_(index.i32); } } default: { } } }
private:
std::function
std::shared_ptr<ArkUIListItemAdapter> adapter_;
}; } // namespace NativeModule
#endif // MYAPPLICATION_ARKUILISTNODE_H
4. Write code for lazy loading of a list.
// ArkUILazyTextListExample // Sample code for lazy loading a list.
#ifndef MYAPPLICATION_LAZYTEXTLISTEXAMPLE_H #define MYAPPLICATION_LAZYTEXTLISTEXAMPLE_H
#include “ArkUIBaseNode.h”
#include “ArkUIListNode.h”
#include “UITimer.h”
#include
namespace NativeModule {
std::shared_ptr
// 3: Simulate lazy loading operations.
CreateNativeTimer(env, adapter.get(), 4, [](void *userdata, int32_t count) {
auto adapter = reinterpret_cast<ArkUIListItemAdapter *>(userdata);
switch (count) {
case 0: {
// Remove the 0th item.
adapter->RemoveItem(0);
break;
}
case 1: {
// Insert the 0th item.
adapter->InsertItem(0, "0");
break;
}
case 2: {
// Move an item to a new position.
adapter->MoveItem(0, 2);
break;
}
case 3: {
// Reload a single item.
adapter->ReloadItem(0, "1112");
break;
}
case 4: {
// Reload all items.
adapter->ReloadAllItem();
break;
}
default: {
}
}
});
// 3: Register list-related listening events.
list->RegisterOnScrollIndex([](int32_t index) { OH_LOG_INFO(LOG_APP, "on list scroll index: %{public}d", index); });
// 4: Register the appear event.
list->RegisterOnAppear([]() { OH_LOG_INFO(LOG_APP, "on list mount to tree"); });
// 4: Register the disappear event.
list->RegisterOnDisappear([]() { OH_LOG_INFO(LOG_APP, "on list unmount from tree"); });
return list;
} } // namespace NativeModule
#endif // MYAPPLICATION_LAZYTEXTLISTEXAMPLE_H
5. Implement a simple timer module.
// UITimer.h // Timer module.
#ifndef MYAPPLICATION_UITIMER_H #define MYAPPLICATION_UITIMER_H
#include
namespace NativeModule {
struct UIData { void *userData = nullptr; int32_t count = 0; int32_t totalCount = 0; void (*func)(void *userData, int32_t count) = nullptr; };
napi_threadsafe_function threadSafeFunction = nullptr;
void CreateNativeTimer(napi_env env, void *userData, int32_t totalCount, void (*func)(void *userData, int32_t count)) {
napi_value name;
std::string str = “UICallback”;
napi_create_string_utf8(env, str.c_str(), str.size(), &name);
// UI main thread callback function.
napi_create_threadsafe_function(
env, nullptr, nullptr, name, 0, 1, nullptr, nullptr, nullptr,
[](napi_env env, napi_value value, void *context, void *data) {
auto userdata = reinterpret_cast
#endif // MYAPPLICATION_UITIMER_H
6. Mount the lazy loading example code onto the **ContentSlot** as described in the [Integrating with ArkTS Pages](https://m.seaxiang.com/blog/mlDQQ5) section.
// NDK API entry mount file.
#include “NativeEntry.h”
#include “ArkUIMixedRefresh.h” #include “LazyTextListExample.h” #include “MixedRefreshExample.h” #include “TextListExample.h”
#include
namespace NativeModule { namespace { napi_env g_env; }
napi_env GetNapiEnv() { return g_env; }
napi_value CreateNativeRoot(napi_env env, napi_callback_info info) { size_t argc = 1; napi_value args[1] = {nullptr};
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
// Obtain NodeContent.
ArkUI_NodeContentHandle contentHandle;
OH_ArkUI_GetNodeContentFromNapiValue(env, args[0], &contentHandle);
NativeEntry::GetInstance()->SetContentHandle(contentHandle);
// Create a lazy-loaded text list.
auto node = CreateLazyTextListExample(env);
// Keep the native side object in the management class to maintain its lifecycle.
NativeEntry::GetInstance()->SetRootNode(node);
g_env = env;
return nullptr;
}
napi_value DestroyNativeRoot(napi_env env, napi_callback_info info) { // Release the native side object from the management class. NativeEntry::GetInstance()->DisposeRootNode(); return nullptr; }
} // namespace NativeModule “`
你可能感兴趣的鸿蒙文章
harmony 鸿蒙Atomic Service Full Screen Launch Component (FullScreenLaunchComponent)
harmony 鸿蒙Arc Button (ArcButton)
harmony 鸿蒙Frame Animation (ohos.animator)
harmony 鸿蒙Implementing Property Animation
- 所属分类: 后端技术
- 本文标签:
热门推荐
-
2、 - 优质文章
-
3、 gate.io
-
8、 golang
-
9、 openharmony
-
10、 Vue中input框自动聚焦