针对List、Grid、WaterFlow、Swiper组件,提供 NodeAdapter 对象替代ArkTS侧的LazyForEach功能,用于按需生成子组件,其中List组件的属性枚举值为NODE_LIST_NODE_ADAPTER,Grid组件的属性枚举值为NODE_GRID_NODE_ADAPTER,WaterFlow组件的属性枚举值为NODE_WATER_FLOW_NODE_ADAPTER,Swiper组件的属性枚举值为NODE_SWIPER_NODE_ADAPTER。
虽然都用于按需生成组件,但不同于ArkTS的LazyForEach,NodeAdapter对象的规格如下:
-
设置了NodeAdapter属性的节点,不再支持addChild等直接添加子组件的接口。子组件完全由NodeAdapter管理,使用属性方法设置NodeAdapter时,会判断父组件是否已经存在子节点,如果父组件已经存在子节点,则设置NodeAdapter操作失败,返回错误码。
-
NodeApdater通过相关事件通知开发者按需生成组件,类似组件事件机制,开发者使用NodeAdapter时要注册 事件监听器 ,在监听器事件中处理逻辑,相关事件通过 ArkUI_NodeAdapterEventType 定义。另外NodeAdapter不会主动释放不在屏幕内显示的组件对象,开发者需要在 NODE_ADAPTER_EVENT_ON_REMOVE_NODE_FROM_ADAPTER 事件中进行组件对象的释放,或者进行缓存复用。下图展示了典型列表滑动场景下的事件触发机制:
以下示例代码针对 接入ArkTS页面 章节代码进行优化,引入懒加载机制实现文本列表:
-
接入ArkTS,参考 接入ArkTS页面 。
-
懒加载适配器相关功能实现。
// ArkUIListItemAdapter
// 用于文本列表懒加载功能代码。
#ifndef MYAPPLICATION_ARKUILISTITEMADAPTER_H
#define MYAPPLICATION_ARKUILISTITEMADAPTER_H
#include
#include
#include
#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()) { // 使用NodeAdapter创建函数。
// 初始化懒加载数据。
for (int32_t i = 0; i < 1000; i++) {
data_.emplace_back(std::to_string(i));
}
// 设置懒加载数据。
OH_ArkUI_NodeAdapter_SetTotalNodeCount(handle_, data_.size());
// 设置懒加载回调事件。
OH_ArkUI_NodeAdapter_RegisterEventReceiver(handle_, this, OnStaticAdapterEvent);
}
~ArkUIListItemAdapter() {
// 释放创建的组件。
while (!cachedItems_.empty()) {
cachedItems_.pop();
}
items_.clear();
// 释放Adapter相关资源。
OH_ArkUI_NodeAdapter_UnregisterEventReceiver(handle_);
OH_ArkUI_NodeAdapter_Dispose(handle_);
}
ArkUI_NodeAdapterHandle GetHandle() const { return handle_; }
void RemoveItem(int32_t index) {
// 删除第index个数据。
data_.erase(data_.begin() + index);
// 如果index会导致可视区域元素发生可见性变化,则会回调NODE_ADAPTER_EVENT_ON_REMOVE_NODE_FROM_ADAPTER事件删除元素,
// 根据是否有新增元素回调NODE_ADAPTER_EVENT_ON_GET_NODE_ID和NODE_ADAPTER_EVENT_ON_ADD_NODE_TO_ADAPTER事件。
OH_ArkUI_NodeAdapter_RemoveItem(handle_, index, 1);
// 更新新的数量。
OH_ArkUI_NodeAdapter_SetTotalNodeCount(handle_, data_.size());
}
void InsertItem(int32_t index, const std::string &value) {
data_.insert(data_.begin() + index, value);
// 如果index会导致可视区域元素发生可见性变化,则会回调NODE_ADAPTER_EVENT_ON_GET_NODE_ID和NODE_ADAPTER_EVENT_ON_ADD_NODE_TO_ADAPTER事件,
// 根据是否有删除元素回调NODE_ADAPTER_EVENT_ON_REMOVE_NODE_FROM_ADAPTER事件。
OH_ArkUI_NodeAdapter_InsertItem(handle_, index, 1);
// 更新新的数量。
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);
// 移到位置如果未发生可视区域内元素的可见性变化,则不回调事件,反之根据新增和删除场景回调对应的事件。
OH_ArkUI_NodeAdapter_MoveItem(handle_, oldIndex, newIndex);
}
void ReloadItem(int32_t index, const std::string &value) {
data_[index] = value;
// 如果index位于可视区域内,先回调NODE_ADAPTER_EVENT_ON_REMOVE_NODE_FROM_ADAPTER删除老元素,
// 再回调NODE_ADAPTER_EVENT_ON_GET_NODE_ID和NODE_ADAPTER_EVENT_ON_ADD_NODE_TO_ADAPTER事件。
OH_ArkUI_NodeAdapter_ReloadItem(handle_, index, 1);
}
void ReloadAllItem() {
std::reverse(data_.begin(), data_.end());
// 全部重新加载场景下,会回调NODE_ADAPTER_EVENT_ON_GET_NODE_ID接口获取新的组件ID,
// 根据新的组件ID进行对比,ID不发生变化的进行复用,
// 针对新增ID的元素,调用NODE_ADAPTER_EVENT_ON_ADD_NODE_TO_ADAPTER事件创建新的组件,
// 然后判断老数据中遗留的未使用ID,调用NODE_ADAPTER_EVENT_ON_REMOVE_NODE_FROM_ADAPTER删除老元素。
OH_ArkUI_NodeAdapter_ReloadAllItems(handle_);
}
private:
static void OnStaticAdapterEvent(ArkUI_NodeAdapterEvent *event) {
// 获取实例对象,回调实例事件。
auto itemAdapter = reinterpret_cast(OH_ArkUI_NodeAdapterEvent_GetUserData(event));
itemAdapter->OnAdapterEvent(event);
}
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;
}
}
// 分配ID给需要显示的Item,用于ReloadAllItems场景的元素diff。
void OnNewItemIdCreated(ArkUI_NodeAdapterEvent *event) {
auto index = OH_ArkUI_NodeAdapterEvent_GetItemIndex(event);
static std::hash hashId = std::hash();
auto id = hashId(data_[index]);
OH_ArkUI_NodeAdapterEvent_SetNodeId(event, id);
}
// 需要新的Item显示在可见区域。
void OnNewItemAttached(ArkUI_NodeAdapterEvent *event) {
auto index = OH_ArkUI_NodeAdapterEvent_GetItemIndex(event);
ArkUI_NodeHandle handle = nullptr;
if (!cachedItems_.empty()) {
// 使用并更新回收复用的缓存。
auto recycledItem = cachedItems_.top();
auto textItem = std::dynamic_pointer_cast(recycledItem->GetChildren().back());
textItem->SetTextContent(data_[index]);
handle = recycledItem->GetHandle();
// 释放缓存池的引用。
cachedItems_.pop();
} else {
// 创建新的元素。
auto listItem = std::make_shared();
auto textNode = std::make_shared();
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();
// 保持文本列表项的引用。
items_.emplace(handle, listItem);
}
// 设置需要展示的元素。
OH_ArkUI_NodeAdapterEvent_SetItem(event, handle);
}
// Item从可见区域移除。
void OnItemDetached(ArkUI_NodeAdapterEvent *event) {
auto item = OH_ArkUI_NodeAdapterEvent_GetRemovedNode(event);
// 放置到缓存池中进行回收复用。
cachedItems_.emplace(items_[item]);
}
std::vector data_;
ArkUI_NativeNodeAPI_1 *module_ = nullptr;
ArkUI_NodeAdapterHandle handle_ = nullptr;
// 管理NodeAdapter生成的元素。
std::unordered_map> items_;
// 管理回收复用组件池。
std::stack> cachedItems_;
};
} // namespace NativeModule
#endif // MYAPPLICATION_ARKUILISTITEMADAPTER_H
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 针对 接入ArkTS页面 章节使用的列表封装类对象,添加额外懒加载能力。
// ArkUIListNode.h
// 列表封装对象。
#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_) {
// 析构的时候卸载adapter下的UI组件。
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 &onScrollIndex) {
assert(handle_);
onScrollIndex_ = onScrollIndex;
nativeModule_->registerNodeEvent(handle_, NODE_LIST_ON_SCROLL_INDEX, 0, nullptr);
}
// 引入懒加载模块。
void SetLazyAdapter(const std::shared_ptr &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_NodeEvent_GetNodeComponentEvent(event)->data[0];
if (onScrollIndex_) {
onScrollIndex_(index.i32);
}
}
default: {
}
}
}
private:
std::function onScrollIndex_;
std::shared_ptr adapter_;
};
} // namespace NativeModule
#endif // MYAPPLICATION_ARKUILISTNODE_H
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 创建列表懒加载的示例代码。
// ArkUILazyTextListExample
// 懒加载列表示例代码。
#ifndef MYAPPLICATION_LAZYTEXTLISTEXAMPLE_H
#define MYAPPLICATION_LAZYTEXTLISTEXAMPLE_H
#include "ArkUIBaseNode.h"
#include "ArkUIListNode.h"
#include "UITimer.h"
#include
#include
namespace NativeModule {
std::shared_ptr CreateLazyTextListExample(napi_env env) {
// 创建组件并挂载
// 1:创建List组件。
auto list = std::make_shared();
list->SetPercentWidth(1);
list->SetPercentHeight(1);
// 2:创建ListItem懒加载组件并挂载到List上。
auto adapter = std::make_shared();
list->SetLazyAdapter(adapter);
// 3:模拟相关懒加载操作。
CreateNativeTimer(env, adapter.get(), 4, [](void *userdata, int32_t count) {
auto adapter = reinterpret_cast(userdata);
switch (count) {
case 0: {
// 删除第0个元素。
adapter->RemoveItem(0);
break;
}
case 1: {
// 插入第0个元素。
adapter->InsertItem(0, "0");
break;
}
case 2: {
// 移到元素位置。
adapter->MoveItem(0, 2);
break;
}
case 3: {
// 重载元素。
adapter->ReloadItem(0, "1112");
break;
}
case 4: {
// 全量重载。
adapter->ReloadAllItem();
break;
}
default: {
}
}
});
// 3:注册List相关监听事件.
list->RegisterOnScrollIndex([](int32_t index) { OH_LOG_INFO(LOG_APP, "on list scroll index: %{public}d", index); });
// 4: 注册挂载事件。
list->RegisterOnAppear([]() { OH_LOG_INFO(LOG_APP, "on list mount to tree"); });
// 4: 注册卸载事件。
list->RegisterOnDisappear([]() { OH_LOG_INFO(LOG_APP, "on list unmount from tree"); });
return list;
}
} // namespace NativeModule
#endif // MYAPPLICATION_LAZYTEXTLISTEXAMPLE_H
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 定时器模块相关简单实现。
// UITimer.h
// 定时器模块。
#ifndef MYAPPLICATION_UITIMER_H
#define MYAPPLICATION_UITIMER_H
#include
#include
#include
#include
#include
#include
#include
#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主线程回调函数。
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(data);
userdata->func(userdata->userData, userdata->count);
delete userdata;
},
&threadSafeFunction);
// 启动定时器,模拟数据变化。
std::thread timerThread([data = userData, totalCount, func]() {
uv_loop_t *loop = uv_loop_new();
uv_timer_t *timer = new uv_timer_t();
uv_timer_init(loop, timer);
timer->data = new UIData{data, 0, totalCount, func};
uv_timer_start(
timer,
[](uv_timer_t *handle) {
OH_LOG_INFO(LOG_APP, "on timeout");
napi_acquire_threadsafe_function(threadSafeFunction);
auto *customData = reinterpret_cast(handle->data);
// 创建回调数据。
auto *callbackData =
new UIData{customData->userData, customData->count, customData->totalCount, customData->func};
napi_call_threadsafe_function(threadSafeFunction, callbackData, napi_tsfn_blocking);
customData->count++;
if (customData->count > customData->totalCount) {
uv_timer_stop(handle);
delete handle;
delete customData;
}
},
4000, 4000);
uv_run(loop, UV_RUN_DEFAULT);
uv_loop_delete(loop);
});
timerThread.detach();
}
} // namespace NativeModule
#endif // MYAPPLICATION_UITIMER_H
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 按照 接入ArkTS页面 章节将懒加载相关示例代码挂载到ContentSlot上显示。
// NDK接口入口挂载文件。
#include "NativeEntry.h"
#include "ArkUIMixedRefresh.h"
#include "LazyTextListExample.h"
#include "MixedRefreshExample.h"
#include "TextListExample.h"
#include
#include
#include
#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);
// 获取NodeContent
ArkUI_NodeContentHandle contentHandle;
OH_ArkUI_GetNodeContentFromNapiValue(env, args[0], &contentHandle);
NativeEntry::GetInstance()->SetContentHandle(contentHandle);
// 创建懒加载文本列表
auto node = CreateLazyTextListExample(env);
// 保持Native侧对象到管理类中,维护生命周期。
NativeEntry::GetInstance()->SetRootNode(node);
g_env = env;
return nullptr;
}
napi_value DestroyNativeRoot(napi_env env, napi_callback_info info) {
// 从管理类中释放Native侧对象。
NativeEntry::GetInstance()->DisposeRootNode();
return nullptr;
}
} // namespace NativeModule
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
鸿蒙全栈开发全新学习指南
有很多小伙伴不知道学习哪些鸿蒙开发技术?不知道需要重点掌握哪些鸿蒙应用开发知识点?而且学习时频繁踩坑,最终浪费大量时间。所以要有一份实用的鸿蒙(HarmonyOS NEXT)学习路线与学习文档用来跟着学习是非常有必要的。
针对一些列因素,整理了一套纯血版鸿蒙(HarmonyOS Next)全栈开发技术的学习路线,包含了鸿蒙开发必掌握的核心知识要点,内容有(ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、WebGL、元服务、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、OpenHarmony驱动开发、系统定制移植等等)鸿蒙(HarmonyOS NEXT)技术知识点。
本路线共分为四个阶段:
第一阶段:鸿蒙初中级开发必备技能
第二阶段:鸿蒙南北双向高工技能基础:gitee.com/MNxiaona/733GH
第三阶段:应用开发中高级就业技术
第四阶段:全网首发-工业级南向设备开发就业技术:gitee.com/MNxiaona/733GH
《鸿蒙 (Harmony OS)开发学习手册》(共计892页)
如何快速入门?
1.基本概念
2.构建第一个ArkTS应用
3.……
开发基础知识:gitee.com/MNxiaona/733GH
1.应用基础知识
2.配置文件
3.应用数据管理
4.应用安全管理
5.应用隐私保护
6.三方应用调用管控机制
7.资源分类与访问
8.学习ArkTS语言
9.……
基于ArkTS 开发
1.Ability开发
2.UI开发
3.公共事件与通知
4.窗口管理
5.媒体
6.安全
7.网络与链接
8.电话服务
9.数据管理
10.后台任务(Background Task)管理
11.设备管理
12.设备使用信息统计
13.DFX
14.国际化开发
15.折叠屏系列
16.……
鸿蒙开发面试真题(含参考答案):gitee.com/MNxiaona/733GH
鸿蒙入门教学视频:
美团APP实战开发教学:gitee.com/MNxiaona/733GH
写在最后
- 如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙:
- 点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。
- 关注小编,同时可以期待后续文章ing?,不定期分享原创知识。
- 想要获取更多完整鸿蒙最新学习资源,请移步前往小编:
gitee.com/MNxiaona/733GH



评论记录:
回复评论: