The ArkUI development framework provides list components through NDK APIs, enabling efficient display of structured, scrollable content. List components allow you to control the scroll position, group display content, and use NodeAdapter for lazy loading to improve list creation performance.
Refer to Adding Event Listeners to implement scroll event listening for the list.
The NDK provides the NodeAdapter object as an alternative to LazyForEach in ArkTS for on-demand generation of child components. NodeAdapter works with List/ListItemGroup, Grid, WaterFlow, and Swiper components.
Child components cannot be added to a node where NodeAdapter is set by using APIs such as 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 notifies you through related events to generate components on demand, similar to the component event mechanism. When using NodeAdapter, register an event listener through OH_ArkUI_NodeAdapter_RegisterEventReceiver and handle the logic in the listener events. The related events are defined by ArkUI_NodeAdapterEventType. In addition, NodeAdapter does not proactively release component objects that are no longer displayed on the screen. You need to release the component objects in the NODE_ADAPTER_EVENT_ON_REMOVE_NODE_FROM_ADAPTER event, or cache and reuse them. The following figure shows the event triggering mechanism in a typical list scrolling scenario:

This example demonstrates how to implement an adapter for lazy loading.
Use the ArkUIListItemAdapter class to manage the lazy loading adapter. Create a NodeAdapter object in the class constructor and set an event listener for the NodeAdapter object. In the class destructor, destroy the NodeAdapter object.
The ArkUIListItemAdapter class is a custom generic template class. The template parameter types can be customized based on the business data and node model. This template exposes the "create child component" callback, which is used to create and mount the child component corresponding to each ListItem on demand. It also provides the "reuse ListItem" callback, which is used to perform state reset and reuse logic after a node is recycled.
// ArkUIListItemAdapter.h
// Code for lazy loading functionality in a text list.
#ifndef MYAPPLICATION_ARKUILISTITEMADAPTER_H
#define MYAPPLICATION_ARKUILISTITEMADAPTER_H
#include <algorithm>
#include <arkui/native_node.h>
#include <cstddef>
#include <cstdint>
#include <functional>
#include <memory>
#include <stack>
#include <string>
#include <unordered_map>
#include <vector>
#include "ArkUIListItemNode.h"
#include "ArkUITextNode.h"
#include "NativeModule.h"
namespace NativeModule {
class IArkUIListItemAdapter {
public:
virtual ~IArkUIListItemAdapter() = default;
virtual ArkUI_NodeAdapterHandle GetHandle() const = 0;
};
template <typename ItemType> class ArkUIListItemAdapterT : public IArkUIListItemAdapter {
public:
// Callback type for creating a list item node.
using CreateChildCallback = std::function<std::shared_ptr<ArkUIListItemNode>(const ItemType &item, int32_t index)>;
// Callback type for updating content when reusing a list item node.
using ReuseListItemCallback =
std::function<void(const std::shared_ptr<ArkUIListItemNode> &listItem, const ItemType &item, int32_t index)>;
// Callback type for generating the unique identifier of a node.
using NodeIdCallback = std::function<int64_t(const ItemType &item, int32_t index)>;
// Create a NodeAdapter and initialize the data and callbacks.
explicit ArkUIListItemAdapterT(std::vector<ItemType> data, CreateChildCallback createChildCallback,
ReuseListItemCallback reuseListItemCallback, NodeIdCallback nodeIdCallback = nullptr)
: handle_(OH_ArkUI_NodeAdapter_Create()), data_(std::move(data)),
createChildCallback_(std::move(createChildCallback)),
reuseListItemCallback_(std::move(reuseListItemCallback)), nodeIdCallback_(std::move(nodeIdCallback)) {
// Set lazy loading data.
OH_ArkUI_NodeAdapter_SetTotalNodeCount(handle_, data_.size());
// Register the event receiver for lazy loading.
OH_ArkUI_NodeAdapter_RegisterEventReceiver(handle_, this, OnStaticAdapterEvent);
}
~ArkUIListItemAdapterT() override
{
// 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 override { return handle_; }
const std::vector<ItemType> &GetData() const { return data_; }
void SetData(std::vector<ItemType> data)
{
data_ = std::move(data);
OH_ArkUI_NodeAdapter_SetTotalNodeCount(handle_, data_.size());
OH_ArkUI_NodeAdapter_ReloadAllItems(handle_);
}
void SetNodeIdCallback(NodeIdCallback callback) { nodeIdCallback_ = std::move(callback); }
void RemoveItem(size_t index)
{
// Remove the item at the specified index.
if (index >= data_.size()) {
return;
}
data_.erase(data_.begin() + static_cast<std::ptrdiff_t>(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.
// Depending on whether there are new elements, the NODE_ADAPTER_EVENT_ON_GET_NODE_ID or NODE_ADAPTER_EVENT_ON_ADD_NODE_TO_ADAPTER event will be triggered.
OH_ArkUI_NodeAdapter_RemoveItem(handle_, index, 1);
// Update the total count.
OH_ArkUI_NodeAdapter_SetTotalNodeCount(handle_, data_.size());
}
void InsertItem(int32_t index, const ItemType &value)
{
int32_t safeIndex = std::max(0, std::min(index, static_cast<int32_t>(data_.size())));
data_.insert(data_.begin() + safeIndex, 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_, safeIndex, 1);
// Update the total count.
OH_ArkUI_NodeAdapter_SetTotalNodeCount(handle_, data_.size());
}
void MoveItem(int32_t oldIndex, int32_t newIndex)
{
if (!IsValidIndex(oldIndex) || !IsValidIndex(newIndex) || oldIndex == newIndex) {
return;
}
auto temp = data_[oldIndex];
data_.insert(data_.begin() + newIndex, temp);
int32_t removeIndex = oldIndex;
if (newIndex < oldIndex) {
removeIndex += 1;
}
data_.erase(data_.begin() + removeIndex);
// If the move changes the visibility of items within the visible area, the corresponding events will be triggered. Otherwise, no event is triggered.
OH_ArkUI_NodeAdapter_MoveItem(handle_, oldIndex, newIndex);
}
void ReloadItem(int32_t index, const ItemType &value)
{
if (!IsValidIndex(index)) {
return;
}
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()
{
// 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:
struct ItemEntry {
std::shared_ptr<ArkUIListItemNode> listItem = nullptr;
};
static void OnStaticAdapterEvent(ArkUI_NodeAdapterEvent *event)
{
// Obtain the instance object and invoke the instance event callback.
auto *itemAdapter = reinterpret_cast<ArkUIListItemAdapterT *>(OH_ArkUI_NodeAdapterEvent_GetUserData(event));
if (itemAdapter != nullptr) {
itemAdapter->OnAdapterEvent(event);
}
}
void OnAdapterEvent(ArkUI_NodeAdapterEvent *event)
{
// Obtain the event type.
auto type = OH_ArkUI_NodeAdapterEvent_GetType(event);
switch (type) {
// Obtain the unique ID of the new element to add.
case NODE_ADAPTER_EVENT_ON_GET_NODE_ID:
OnNewItemIdCreated(event);
break;
// Obtain the content of the new element to add.
case NODE_ADAPTER_EVENT_ON_ADD_NODE_TO_ADAPTER:
OnNewItemAttached(event);
break;
// Remove elements from the adapter.
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 ReloadAllItems scenarios.
void OnNewItemIdCreated(ArkUI_NodeAdapterEvent *event)
{
auto index = OH_ArkUI_NodeAdapterEvent_GetItemIndex(event);
if (!IsValidIndex(index)) {
return;
}
int64_t id = nodeIdCallback_ ? nodeIdCallback_(data_[index], index) : static_cast<int64_t>(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);
if (!IsValidIndex(index)) {
return;
}
ItemEntry itemEntry;
if (!cachedItems_.empty()) {
// Use and update the recycled item from the cache.
itemEntry = cachedItems_.top();
// Release the reference in the cache pool.
cachedItems_.pop();
reuseListItemCallback_(itemEntry.listItem, data_[index], index);
} else {
// Create an element.
itemEntry.listItem = createChildCallback_(data_[index], index);
}
if (itemEntry.listItem == nullptr) {
itemEntry.listItem = std::make_shared<ArkUIListItemNode>();
}
auto handle = itemEntry.listItem->GetHandle();
items_[handle] = itemEntry;
// Set the element 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);
auto iter = items_.find(item);
if (iter != items_.end()) {
// Place the node into the cache pool for recycling and reuse.
cachedItems_.push(iter->second);
items_.erase(iter);
}
}
bool IsValidIndex(int32_t index) const { return index >= 0 && index < static_cast<int32_t>(data_.size()); }
// NodeAdapter instance handle.
ArkUI_NodeAdapterHandle handle_ = nullptr;
// List data source.
std::vector<ItemType> data_;
// Callback for creating a list item node.
CreateChildCallback createChildCallback_ = nullptr;
// Callback for reusing a list item node.
ReuseListItemCallback reuseListItemCallback_ = nullptr;
// Callback for generating the node ID.
NodeIdCallback nodeIdCallback_ = nullptr;
// Manage items generated by the NodeAdapter.
std::unordered_map<ArkUI_NodeHandle, ItemEntry> items_;
// Manage the component reuse pool.
std::stack<ItemEntry> cachedItems_;
};
} // namespace NativeModule
#endif // MYAPPLICATION_ARKUILISTITEMADAPTER_H Add the SetLazyAdapter function to ArkUIListNode, set the NODE_LIST_NODE_ADAPTER attribute for the list node, and pass NodeAdapter as the attribute parameter.
// ArkUIListNode.h
// Provide encapsulation for the list component.
#ifndef MYAPPLICATION_ARKUILISTNODE_H
#define MYAPPLICATION_ARKUILISTNODE_H
#include "ArkUINode.h"
#include "ArkUIListItemAdapter.h"
namespace NativeModule {
class ArkUIListNode : public ArkUINode {
public:
ArkUIListNode() // Create an ArkUI list component instance.
: ArkUINode((NativeModuleInstance::GetInstance()->GetNativeNodeAPI())->createNode(ARKUI_NODE_LIST)) {}
~ArkUIListNode() override
{
if (nativeModule_) {
nativeModule_->unregisterNodeEvent(handle_, NODE_LIST_ON_SCROLL_INDEX);
if (adapter_) {
// Unload the UI components under the adapter during destruction.
nativeModule_->resetAttribute(handle_, NODE_LIST_NODE_ADAPTER);
adapter_.reset();
}
}
}
// List component's API encapsulation for attributes.
void SetScrollBarState(bool isShow)
{
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)
{
onScrollIndex_ = onScrollIndex;
nativeModule_->registerNodeEvent(handle_, NODE_LIST_ON_SCROLL_INDEX, 0, nullptr);
}
// Import the lazy loading module.
void SetLazyAdapter(const std::shared_ptr<IArkUIListItemAdapter> &adapter)
{
if (!adapter) {
nativeModule_->resetAttribute(handle_, NODE_LIST_NODE_ADAPTER);
adapter_.reset();
return;
}
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_NodeEvent_GetNodeComponentEvent(event)->data[0];
if (onScrollIndex_) {
onScrollIndex_(index.i32);
}
break;
}
default: {
break;
}
}
}
private:
std::function<void(int32_t index)> onScrollIndex_;
std::shared_ptr<IArkUIListItemAdapter> adapter_;
};
} // namespace NativeModule
#endif // MYAPPLICATION_ARKUILISTNODE_H Create example code for a List using lazy loading and call the SetLazyAdapter API of the List node to set the lazy loading adapter.
// LazyTextListExample
// Example code for a lazy loading text list.
#ifndef MYAPPLICATION_LAZYTEXTLISTEXAMPLE1_H
#define MYAPPLICATION_LAZYTEXTLISTEXAMPLE1_H
#include "ArkUIBaseNode.h"
#include "ArkUIListNode.h"
#include "ArkUITextNode.h"
#include <algorithm>
#include <memory>
#include <string>
#include <vector>
namespace NativeModule {
// ...
std::shared_ptr<ArkUIBaseNode> CreateLazyTextListExample1()
{
// Create and mount the component to the parent node.
// 1: Create a List component.
auto list = std::make_shared<ArkUIListNode>();
list->SetPercentWidth(1); // 1: width
list->SetPercentHeight(1); // 1: height
auto data = BuildSampleData();
auto adapterWeakHolder = std::make_shared<std::weak_ptr<StringAdapter>>();
// 2: Callback for creating ListItem and its child components.
auto createChildCallback = [adapterWeakHolder](const std::string &item,
int32_t index) -> std::shared_ptr<ArkUIListItemNode> {
(void)index;
return BuildListItemNode(item, adapterWeakHolder);
};
// 3: Callback for reusing ListItem.
auto reuseListItemCallback = [adapterWeakHolder](const std::shared_ptr<ArkUIListItemNode> &listItem,
const std::string &item, int32_t index) {
(void)index;
ReuseListItemNode(listItem, item, adapterWeakHolder);
};
auto adapter = std::make_shared<StringAdapter>(data, createChildCallback, reuseListItemCallback);
*adapterWeakHolder = adapter;
// 4: Create the ListItem lazy loading component and mount it to the list.
list->SetLazyAdapter(adapter);
return list;
}
} // namespace NativeModule
#endif // MYAPPLICATION_LAZYTEXTLISTEXAMPLE1_H Call the List lazy loading example code in NativeEntry.cpp.
// NDK API entry mounting file.
#include "NativeEntry.h"
#include "LazyTextListExample.h"
#include "LazyTextListExample1.h"
#include <arkui/native_node_napi.h>
#include <arkui/native_type.h>
#include <js_native_api.h>
#include <algorithm>
#include <memory>
#include <string>
#include <uv.h>
#include <vector>
namespace NativeModule {
// ...
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 the NodeContent object.
ArkUI_NodeContentHandle contentHandle;
OH_ArkUI_GetNodeContentFromNapiValue(env, args[0], &contentHandle);
NativeEntry::GetInstance()->SetContentHandle(contentHandle);
// Create a lazy loading text list.
auto node = CreateLazyTextListExample();
// Save the native-side object to the management class to maintain its lifecycle.
NativeEntry::GetInstance()->SetRootNode(node);
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 Control the list to scroll to a specified offset position.
// ArkUIListNode.h
// Provide encapsulation for the list component.
// ...
class ArkUIListNode : public ArkUINode {
public:
// ...
void ScrollTo(float offset)
{
ArkUI_NumberValue value[] = {{.f32 = 0}, {.f32 = offset}, {.f32 = 0}};
ArkUI_AttributeItem item = {.value = value, .size = 3};
nativeModule_->setAttribute(handle_, NODE_SCROLL_OFFSET, &item);
}
// ...
}; Control the list to scroll to a specified element.
// ArkUIListNode.h
// Provide encapsulation for the list component.
// ...
class ArkUIListNode : public ArkUINode {
public:
// ...
void ScrollToIndex(int32_t index)
{
ArkUI_NumberValue value[] = {{.i32 = index}};
ArkUI_AttributeItem item = {.value = value, .size = 1};
nativeModule_->setAttribute(handle_, NODE_LIST_SCROLL_TO_INDEX, &item);
}
// ...
}; Control the list to scroll by a specified offset.
// ArkUIListNode.h
// Provide encapsulation for the list component.
// ...
class ArkUIListNode : public ArkUINode {
public:
// ...
void ScrollBy(float offset)
{
ArkUI_NumberValue value[] = {{.f32 = 0}, {.f32 = offset}};
ArkUI_AttributeItem item = {.value = value, .size = 2};
nativeModule_->setAttribute(handle_, NODE_SCROLL_BY, &item);
}
// ...
}; Set the NODE_LIST_ITEM_SWIPE_ACTION attribute for ListItem, and pass the ArkUI_ListItemSwipeActionOption object as the attribute parameter.
// ArkUIListItemNode.h
// Provide an encapsulation class for list items
#ifndef MYAPPLICATION_ARKUILISTITEMNODE_H
#define MYAPPLICATION_ARKUILISTITEMNODE_H
#include "ArkUINode.h"
namespace NativeModule {
class ArkUIListItemNode : public ArkUINode {
public:
ArkUIListItemNode()
: ArkUINode((NativeModuleInstance::GetInstance()->GetNativeNodeAPI())->createNode(ARKUI_NODE_LIST_ITEM)) {}
~ArkUIListItemNode() override
{
// Dispose of a ListItemSwipeActionOption instance.
if (swipeAction_) {
OH_ArkUI_ListItemSwipeActionOption_Dispose(swipeAction_);
}
// Dispose of a ListItemSwipeActionItem instance.
if (swipeItem_) {
OH_ArkUI_ListItemSwipeActionItem_Dispose(swipeItem_);
}
}
void SetSwiperAction(std::shared_ptr<ArkUINode> node)
{
swipeContent_ = node;
// Create a ListItemSwipeActionItem instance.
swipeItem_ = OH_ArkUI_ListItemSwipeActionItem_Create();
// Set the layout content for the ListItemSwipeActionItem instance.
OH_ArkUI_ListItemSwipeActionItem_SetContent(swipeItem_, node->GetHandle());
// Create a ListItemSwipeActionOption instance.
swipeAction_ = OH_ArkUI_ListItemSwipeActionOption_Create();
// Set the layout content for the right edge (for a vertical layout) or bottom edge (for a horizontal layout) of the ListItemSwipeActionItem instance.
OH_ArkUI_ListItemSwipeActionOption_SetEnd(swipeAction_, swipeItem_);
ArkUI_AttributeItem Item = {.object = swipeAction_};
nativeModule_->setAttribute(handle_, NODE_LIST_ITEM_SWIPE_ACTION, &Item);
}
std::shared_ptr<ArkUINode> GetSwipeContent() const { return swipeContent_; }
private:
ArkUI_ListItemSwipeActionOption *swipeAction_ = nullptr;
ArkUI_ListItemSwipeActionItem *swipeItem_ = nullptr;
std::shared_ptr<ArkUINode> swipeContent_ = nullptr;
};
} // namespace NativeModule
#endif // MYAPPLICATION_ARKUILISTITEMNODE_H Set the callback functions for creating and reusing ListItem. When a ListItem is created, create the swipe-out component of the ListItem and bind a click event, in which the data source deletion operation is performed. When the ListItem is reused, update the bound event of the swipe-out component.
// LazyTextListExample
// Example code for a lazy loading list.
#ifndef MYAPPLICATION_LAZYTEXTLISTEXAMPLE1_H
#define MYAPPLICATION_LAZYTEXTLISTEXAMPLE1_H
#include "ArkUIBaseNode.h"
#include "ArkUIListNode.h"
#include "ArkUITextNode.h"
#include <algorithm>
#include <memory>
#include <string>
#include <vector>
namespace NativeModule {
using StringAdapter = ArkUIListItemAdapterT<std::string>;
using AdapterWeakHolder = std::shared_ptr<std::weak_ptr<StringAdapter>>;
std::vector<std::string> BuildSampleData()
{
std::vector<std::string> data;
data.reserve(1000); // Preallocate space for 1000 data items per group.
for (int32_t i = 0; i < 1000; i++) { // 1000: space for 1000 data items
data.emplace_back(std::to_string(i));
}
return data;
}
void RegisterDeleteClick(const std::shared_ptr<ArkUINode> &target, const AdapterWeakHolder &adapterWeakHolder,
const std::string &item)
{
// Register a click event for the delete area: obtain the adapter through a weak reference to avoid the callback holding a strong reference for a long time.
target->RegisterOnClick([adapterWeakHolder, item](ArkUI_NodeEvent *event) {
(void)event;
auto adapter = adapterWeakHolder->lock();
if (!adapter) {
return;
}
// Find the current item based on the current data to ensure that the latest bound item is deleted in reuse scenarios.
const auto ¤tData = adapter->GetData();
auto it = std::find(currentData.begin(), currentData.end(), item);
if (it != currentData.end()) {
// RemoveItem requires an index, so convert the iterator position to size_t here.
auto removeIndex = static_cast<size_t>(std::distance(currentData.begin(), it));
adapter->RemoveItem(removeIndex);
}
});
}
std::shared_ptr<ArkUIListItemNode> BuildListItemNode(const std::string &item,
const AdapterWeakHolder &adapterWeakHolder)
{
// Build a ListItem: a main text area plus a right-swipe delete action area.
auto listItem = std::make_shared<ArkUIListItemNode>();
// Main content text that displays the current data item.
auto textNode = std::make_shared<ArkUITextNode>();
textNode->SetTextContent(item);
// ...
listItem->AddChild(textNode);
// Text in the right-swipe action area; tapping it triggers deletion of the current item.
auto swipeNode = std::make_shared<ArkUITextNode>();
swipeNode->SetTextContent("del");
// ...
RegisterDeleteClick(swipeNode, adapterWeakHolder, item);
listItem->SetSwiperAction(swipeNode);
return listItem;
}
void ReuseListItemNode(const std::shared_ptr<ArkUIListItemNode> &listItem, const std::string &item,
const AdapterWeakHolder &adapterWeakHolder)
{
auto &children = listItem->GetChildren();
if (children.empty()) {
return;
}
auto textNode = std::dynamic_pointer_cast<ArkUITextNode>(children.front());
if (textNode) {
textNode->SetTextContent(item);
textNode->SetBackgroundColor(0xFFfffacd);
}
auto swipeContent = listItem->GetSwipeContent();
if (swipeContent) {
RegisterDeleteClick(swipeContent, adapterWeakHolder, item);
}
}
// ...
} // namespace NativeModule
#endif // MYAPPLICATION_LAZYTEXTLISTEXAMPLE1_H When adding a new ListItem, preferentially reuse the cached ListItem instance and update its content. If no cache is available, create a new ListItem. When the callback returns null, create a default ListItem as a fallback. Finally, fill the generated node handle back into the event through OH_ArkUI_NodeAdapterEvent_SetItem to complete the binding.
// ArkUIListItemAdapter.h
// ...
template <typename ItemType> class ArkUIListItemAdapterT : public IArkUIListItemAdapter {
// ...
// Handle the display of new items in the visible area.
void OnNewItemAttached(ArkUI_NodeAdapterEvent *event)
{
auto index = OH_ArkUI_NodeAdapterEvent_GetItemIndex(event);
if (!IsValidIndex(index)) {
return;
}
ItemEntry itemEntry;
if (!cachedItems_.empty()) {
// Use and update the recycled item from the cache.
itemEntry = cachedItems_.top();
// Release the reference in the cache pool.
cachedItems_.pop();
reuseListItemCallback_(itemEntry.listItem, data_[index], index);
} else {
// Create an element.
itemEntry.listItem = createChildCallback_(data_[index], index);
}
if (itemEntry.listItem == nullptr) {
itemEntry.listItem = std::make_shared<ArkUIListItemNode>();
}
auto handle = itemEntry.listItem->GetHandle();
items_[handle] = itemEntry;
// Set the element to be displayed.
OH_ArkUI_NodeAdapterEvent_SetItem(event, handle);
}
// ...
}; Add RemoveItem to ArkUIListItemAdapter to delete the data source and call OH_ArkUI_NodeAdapter_RemoveItem to notify the framework to refresh the UI.
// ArkUIListItemAdapter.h
// ...
template <typename ItemType> class ArkUIListItemAdapterT : public IArkUIListItemAdapter {
// ...
void RemoveItem(size_t index)
{
// Remove the item at the specified index.
if (index >= data_.size()) {
return;
}
data_.erase(data_.begin() + static_cast<std::ptrdiff_t>(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.
// Depending on whether there are new elements, the NODE_ADAPTER_EVENT_ON_GET_NODE_ID or NODE_ADAPTER_EVENT_ON_ADD_NODE_TO_ADAPTER event will be triggered.
OH_ArkUI_NodeAdapter_RemoveItem(handle_, index, 1);
// Update the total count.
OH_ArkUI_NodeAdapter_SetTotalNodeCount(handle_, data_.size());
}
// ...
}; // ArkUIListItemGroupNode.h
#ifndef MYAPPLICATION_ARKUILISTITEMGROUPNODE_H
#define MYAPPLICATION_ARKUILISTITEMGROUPNODE_H
#include "ArkUINode.h"
#include "ArkUIListItemAdapter.h"
namespace NativeModule {
class ArkUIListItemGroupNode : public ArkUINode {
public:
ArkUIListItemGroupNode()
: ArkUINode((NativeModuleInstance::GetInstance()->GetNativeNodeAPI())->createNode(ARKUI_NODE_LIST_ITEM_GROUP))
{
}
void SetHeader(const std::shared_ptr<ArkUINode> &node)
{
if (node) {
// Create an attribute item, put the node handle into it, and set the header.
ArkUI_AttributeItem item = {.object = node->GetHandle()};
nativeModule_->setAttribute(handle_, NODE_LIST_ITEM_GROUP_SET_HEADER, &item);
header_ = node;
} else {
// If a null pointer (nullptr) is passed, the existing header is to be removed.
nativeModule_->resetAttribute(handle_, NODE_LIST_ITEM_GROUP_SET_HEADER);
header_.reset();
}
}
void SetFooter(const std::shared_ptr<ArkUINode> &node)
{
if (node) {
// Create an attribute item, put the node handle into it, and set the footer.
ArkUI_AttributeItem item = {.object = node->GetHandle()};
nativeModule_->setAttribute(handle_, NODE_LIST_ITEM_GROUP_SET_FOOTER, &item);
footer_ = node;
} else {
// If a null pointer (nullptr) is passed, the existing footer is to be removed.
nativeModule_->resetAttribute(handle_, NODE_LIST_ITEM_GROUP_SET_FOOTER);
footer_.reset();
}
}
std::shared_ptr<ArkUINode> GetHeader() const
{
return header_;
}
std::shared_ptr<ArkUINode> GetFooter() const
{
return footer_;
}
// Import the lazy loading module.
void SetLazyAdapter(const std::shared_ptr<IArkUIListItemAdapter> &adapter)
{
if (!adapter) {
nativeModule_->resetAttribute(handle_, NODE_LIST_ITEM_GROUP_NODE_ADAPTER);
adapter_.reset();
return;
}
ArkUI_AttributeItem item{nullptr, 0, nullptr, adapter->GetHandle()};
nativeModule_->setAttribute(handle_, NODE_LIST_ITEM_GROUP_NODE_ADAPTER, &item);
adapter_ = adapter;
}
private:
std::shared_ptr<ArkUINode> header_;
std::shared_ptr<ArkUINode> footer_;
std::shared_ptr<IArkUIListItemAdapter> adapter_;
};
}
#endif // MYAPPLICATION_ARKUILISTITEMGROUPNODE_H// ArkUIListNode.h
// Provide encapsulation for the list component.
// ...
class ArkUIListNode : public ArkUINode {
// ...
void SetSticky(ArkUI_StickyStyle style)
{
ArkUI_NumberValue value[] = {{.i32 = style}};
ArkUI_AttributeItem item = {value, 1};
nativeModule_->setAttribute(handle_, NODE_LIST_STICKY, &item);
}
// ...
};// LazyTextListExample.h
// Example code for a lazy loading text list.
#ifndef MYAPPLICATION_LAZYTEXTLISTEXAMPLE_H
#define MYAPPLICATION_LAZYTEXTLISTEXAMPLE_H
#include "ArkUIBaseNode.h"
#include "ArkUIListItemAdapter.h"
#include "ArkUIListItemGroupNode.h"
#include "ArkUIListNode.h"
#include "ArkUITextNode.h"
#include <string>
#include <vector>
namespace NativeModule {
// ...
std::shared_ptr<ArkUIListItemGroupNode> CreateListItemGroup()
{
auto header = std::make_shared<ArkUITextNode>();
header->SetTextContent("header");
header->SetFontSize(16); // 16: font size
header->SetPercentWidth(1); // 1: width
header->SetHeight(50); // 50: height
header->SetBackgroundColor(0xFFDCDCDC);
header->SetTextAlign(ARKUI_TEXT_ALIGNMENT_CENTER);
auto listItemGroup = std::make_shared<ArkUIListItemGroupNode>();
listItemGroup->SetHeader(header);
auto groupData = BuildGroupData();
auto createChildCallback = [](const std::string &item, int32_t index) -> std::shared_ptr<ArkUIListItemNode> {
return BuildListItemNode(item, index);
};
auto reuseListItemCallback = [](const std::shared_ptr<ArkUIListItemNode> &listItem, const std::string &item,
int32_t index) {
ReuseListItemNode(listItem, item, index);
};
auto adapter = std::make_shared<ArkUIListItemAdapterT<std::string>>(groupData, createChildCallback,
reuseListItemCallback);
listItemGroup->SetLazyAdapter(adapter);
return listItemGroup;
}
std::shared_ptr<ArkUIBaseNode> CreateLazyTextListExample()
{
// Create and mount the component to the parent node.
// 1: Create the List component.
auto list = std::make_shared<ArkUIListNode>();
list->SetPercentWidth(1); // 1: width
list->SetPercentHeight(1); // 1: height
list->SetSticky(ARKUI_STICKY_STYLE_BOTH);
// 2: Create a ListItemGroup and mount it to the List.
for (int32_t i = 0; i < 3; i++) { // 3: number of groups
list->AddChild(CreateListItemGroup());
}
return list;
}
} // namespace NativeModule
#endif // MYAPPLICATION_LAZYTEXTLISTEXAMPLE_H