1# 使用瀑布流 2 3<!--Kit: ArkUI--> 4<!--Subsystem: ArkUI--> 5<!--Owner: @fangyuhao--> 6<!--Designer: @zcdqs--> 7<!--Tester: @liuzhenshuo--> 8<!--Adviser: @HelloCrease--> 9 10ArkUI开发框架在NDK接口提供了瀑布流容器组件,通过瀑布流自身的排列规则,将不同大小的"项目"自上而下如瀑布般紧密布局。 11 12## 接入ArkTS页面 13为了使用NDK接口构建UI界面,参考[接入ArkTS页面章节](../ui/ndk-access-the-arkts-page.md),在ArkTS页面上创建用于Native页面挂载的占位组件,并实现ArkTS侧的NativeNode模块接口。 14 15## 使用懒加载 16### NodeAdapter介绍 17NDK中提供了NodeAdapter对象替代ArkTS侧的LazyForeach功能,用于按需生成子组件。详情请参阅[NodeAdapter介绍](../ui/ndk-loading-long-list.md#nodeadapter介绍)。 18 19### 实现懒加载适配器 20 21使用FlowItemAdapter类管理懒加载适配器。在类的构造函数中创建NodeAdapter对象,并给NodeAdapter对象设置事件监听器,在类的析构函数中,销毁NodeAdapter对象。 22 23```c++ 24// FlowItemAdapter.h 25// 懒加载功能代码。 26 27#ifndef MYAPPLICATION_FLOWITEMADAPTER_H 28#define MYAPPLICATION_FLOWITEMADAPTER_H 29 30#include <arkui/native_node.h> 31#include <stack> 32#include <string> 33#include <unordered_set> 34#include <arkui/native_interface.h> 35 36namespace NativeModule { 37 38class FlowItemAdapter { 39public: 40 FlowItemAdapter(){ 41 42 // 初始化函数指针结构体 43 OH_ArkUI_GetModuleInterface(ARKUI_NATIVE_NODE, ArkUI_NativeNodeAPI_1, nodeApi_); 44 // 创建Adapter对象 45 adapter_ = OH_ArkUI_NodeAdapter_Create(); 46 47 // 初始化懒加载数据。 48 for (int32_t i = 0; i < 100; i++) { 49 data_.emplace_back(std::to_string(i)); 50 } 51 // 设置懒加载数据。 52 OH_ArkUI_NodeAdapter_SetTotalNodeCount(adapter_, data_.size()); 53 // 设置事件监听器。 54 OH_ArkUI_NodeAdapter_RegisterEventReceiver(adapter_, this, OnStaticAdapterEvent); 55 } 56 57 ~FlowItemAdapter() { 58 // 释放创建的组件。 59 while (!cachedItems_.empty()) { 60 cachedItems_.pop(); 61 } 62 // 释放Adapter相关资源。 63 OH_ArkUI_NodeAdapter_UnregisterEventReceiver(adapter_); 64 OH_ArkUI_NodeAdapter_Dispose(adapter_); 65 } 66 67 ArkUI_NodeAdapterHandle GetAdapter() const { return adapter_; } 68 69 void RemoveItem(int32_t index) { 70 // 删除第index个数据。 71 data_.erase(data_.begin() + index); 72 // 如果index会导致可视区域元素发生可见性变化,则会回调NODE_ADAPTER_EVENT_ON_REMOVE_NODE_FROM_ADAPTER事件删除元素, 73 // 根据是否有新增元素回调NODE_ADAPTER_EVENT_ON_GET_NODE_ID和NODE_ADAPTER_EVENT_ON_ADD_NODE_TO_ADAPTER事件。 74 OH_ArkUI_NodeAdapter_RemoveItem(adapter_, index, 1); 75 // 更新新的数量。 76 OH_ArkUI_NodeAdapter_SetTotalNodeCount(adapter_, data_.size()); 77 } 78 79 void InsertItem(int32_t index, const std::string &value) { 80 data_.insert(data_.begin() + index, value); 81 // 如果index会导致可视区域元素发生可见性变化,则会回调NODE_ADAPTER_EVENT_ON_GET_NODE_ID和NODE_ADAPTER_EVENT_ON_ADD_NODE_TO_ADAPTER事件, 82 // 根据是否有删除元素回调NODE_ADAPTER_EVENT_ON_REMOVE_NODE_FROM_ADAPTER事件。 83 OH_ArkUI_NodeAdapter_InsertItem(adapter_, index, 1); 84 // 更新新的数量。 85 OH_ArkUI_NodeAdapter_SetTotalNodeCount(adapter_, data_.size()); 86 } 87 88 void MoveItem(int32_t oldIndex, int32_t newIndex) { 89 auto temp = data_[oldIndex]; 90 data_.insert(data_.begin() + newIndex, temp); 91 data_.erase(data_.begin() + oldIndex); 92 // 移到位置如果未发生可视区域内元素的可见性变化,则不回调事件,反之根据新增和删除场景回调对应的事件。 93 OH_ArkUI_NodeAdapter_MoveItem(adapter_, oldIndex, newIndex); 94 } 95 96 void ReloadItem(int32_t index, const std::string &value) { 97 data_[index] = value; 98 // 如果index位于可视区域内,先回调NODE_ADAPTER_EVENT_ON_REMOVE_NODE_FROM_ADAPTER删除老元素, 99 // 再回调NODE_ADAPTER_EVENT_ON_GET_NODE_ID和NODE_ADAPTER_EVENT_ON_ADD_NODE_TO_ADAPTER事件。 100 OH_ArkUI_NodeAdapter_ReloadItem(adapter_, index, 1); 101 } 102 103 void ReloadAllItem() { 104 std::reverse(data_.begin(), data_.end()); 105 // 全部重新加载场景下,会回调NODE_ADAPTER_EVENT_ON_GET_NODE_ID接口获取新的组件ID, 106 // 根据新的组件ID进行对比,ID不发生变化的进行复用, 107 // 针对新增ID的元素,调用NODE_ADAPTER_EVENT_ON_ADD_NODE_TO_ADAPTER事件创建新的组件, 108 // 然后判断老数据中遗留的未使用ID,调用NODE_ADAPTER_EVENT_ON_REMOVE_NODE_FROM_ADAPTER删除老元素。 109 OH_ArkUI_NodeAdapter_ReloadAllItems(adapter_); 110 } 111 112private: 113 static void OnStaticAdapterEvent(ArkUI_NodeAdapterEvent *event) { 114 // 获取实例对象,回调实例事件。 115 auto itemAdapter = reinterpret_cast<FlowItemAdapter *>(OH_ArkUI_NodeAdapterEvent_GetUserData(event)); 116 itemAdapter->OnAdapterEvent(event); 117 } 118 119 void OnAdapterEvent(ArkUI_NodeAdapterEvent *event) { 120 auto type = OH_ArkUI_NodeAdapterEvent_GetType(event); 121 switch (type) { 122 case NODE_ADAPTER_EVENT_ON_GET_NODE_ID: 123 OnGetChildId(event); 124 break; 125 case NODE_ADAPTER_EVENT_ON_ADD_NODE_TO_ADAPTER: 126 OnCreateNewChild(event); 127 break; 128 case NODE_ADAPTER_EVENT_ON_REMOVE_NODE_FROM_ADAPTER: 129 OnDisposeChild(event); 130 break; 131 default: 132 break; 133 } 134 } 135 136 void OnGetChildId(ArkUI_NodeAdapterEvent *event) { 137 auto index = OH_ArkUI_NodeAdapterEvent_GetItemIndex(event); 138 // 设置生成组件的唯一标识符。 139 auto hash = std::hash<std::string>(); 140 OH_ArkUI_NodeAdapterEvent_SetNodeId(event, hash(data_[index])); 141 } 142 143 void OnCreateNewChild(ArkUI_NodeAdapterEvent *event) { 144 auto index = OH_ArkUI_NodeAdapterEvent_GetItemIndex(event); 145 ArkUI_NodeHandle flowItem = nullptr; 146 if (!cachedItems_.empty()) { 147 // 复用缓存 148 flowItem = cachedItems_.top(); 149 cachedItems_.pop(); 150 // 更新数据 151 auto *text = nodeApi_->getFirstChild(flowItem); 152 ArkUI_AttributeItem item{nullptr, 0, data_[index].c_str()}; 153 nodeApi_->setAttribute(text, NODE_TEXT_CONTENT, &item); 154 } else { 155 // 重新创建。 156 auto *text = nodeApi_->createNode(ARKUI_NODE_TEXT); 157 ArkUI_AttributeItem item{nullptr, 0, data_[index].c_str()}; 158 nodeApi_->setAttribute(text, NODE_TEXT_CONTENT, &item); 159 flowItem = nodeApi_->createNode(ARKUI_NODE_FLOW_ITEM); 160 ArkUI_NumberValue value[] = {100}; 161 ArkUI_AttributeItem height{value, 1}; 162 nodeApi_->setAttribute(flowItem, NODE_HEIGHT, &height); 163 value[0] = {1}; 164 ArkUI_AttributeItem width{value, 1}; 165 nodeApi_->setAttribute(flowItem, NODE_WIDTH_PERCENT, &width); 166 value[0] = {.u32 = 0xFFFF0000}; 167 ArkUI_AttributeItem backgroundColor{value, 1}; 168 169 nodeApi_->setAttribute(flowItem, NODE_BACKGROUND_COLOR, &backgroundColor); 170 nodeApi_->addChild(flowItem, text); 171 } 172 OH_ArkUI_NodeAdapterEvent_SetItem(event, flowItem); 173 } 174 175 void OnDisposeChild(ArkUI_NodeAdapterEvent *event) { 176 auto *node = OH_ArkUI_NodeAdapterEvent_GetRemovedNode(event); 177 // 缓存节点 178 cachedItems_.emplace(node); 179 } 180 181 std::vector<std::string> data_; 182 ArkUI_NativeNodeAPI_1 *nodeApi_ = nullptr; 183 ArkUI_NodeAdapterHandle adapter_ = nullptr; 184 185 // 管理回收复用组件池。 186 std::stack<ArkUI_NodeHandle> cachedItems_; 187}; 188 189} // namespace NativeModule 190 191#endif //MYAPPLICATION_FLOWITEMADAPTER_H 192 193``` 194## 创建分组 195使用WaterflowSection类管理waterflow中的分组,其中SectionOption用于描述一个分段的各项配置信息。在类的构造函数中创建ArkUI_WaterFlowSectionOption对象,在析构函数中将其销毁。 196 197```c++ 198//WaterflowSection.h 199 200#ifndef MYAPPLICATION_WATERFLOWSECTION_H 201#define MYAPPLICATION_WATERFLOWSECTION_H 202 203#include <arkui/native_node.h> 204#include <hilog/log.h> 205 206namespace NativeModule { 207 208struct SectionOption { 209 int32_t itemsCount = 0; 210 int32_t crossCount; 211 float columnsGap; 212 float rowsGap; 213 // {上外边距,右外边距,下外边距,左外边距} 214 ArkUI_Margin margin{0, 0, 0, 0}; 215 float (*onGetItemMainSizeByIndex)(int32_t itemIndex); 216 void *userData; 217}; 218 219class WaterflowSection { 220public: 221 WaterflowSection() : sectionOptions_(OH_ArkUI_WaterFlowSectionOption_Create()){}; 222 223 ~WaterflowSection(){ 224 OH_ArkUI_WaterFlowSectionOption_Dispose(sectionOptions_); 225 } 226 227 void SetSection(ArkUI_WaterFlowSectionOption *sectionOptions, int32_t index, SectionOption section) { 228 OH_ArkUI_WaterFlowSectionOption_SetItemCount(sectionOptions, index, section.itemsCount); 229 OH_ArkUI_WaterFlowSectionOption_SetCrossCount(sectionOptions, index, section.crossCount); 230 OH_ArkUI_WaterFlowSectionOption_SetColumnGap(sectionOptions, index, section.columnsGap); 231 OH_ArkUI_WaterFlowSectionOption_SetRowGap(sectionOptions, index, section.rowsGap); 232 OH_ArkUI_WaterFlowSectionOption_SetMargin(sectionOptions, index, section.margin.top, section.margin.right, 233 section.margin.bottom, section.margin.left); 234 OH_ArkUI_WaterFlowSectionOption_RegisterGetItemMainSizeCallbackByIndex(sectionOptions, index, 235 section.onGetItemMainSizeByIndex); 236 } 237 238 ArkUI_WaterFlowSectionOption *GetSectionOptions() const { 239 return sectionOptions_; 240 } 241 242 void PrintSectionOptions() { 243 int32_t sectionCnt = OH_ArkUI_WaterFlowSectionOption_GetSize(sectionOptions_); 244 for (int32_t i = 0; i < sectionCnt; i++) { 245 ArkUI_Margin margin = OH_ArkUI_WaterFlowSectionOption_GetMargin(sectionOptions_, i); 246 OH_LOG_Print(LOG_APP, LOG_INFO, LOG_DOMAIN, "CreateWaterflowExample", 247 "Section[%{public}d].margin:{%{public}f, %{public}f, %{public}f, %{public}f}", i, margin.top, 248 margin.right, margin.bottom, margin.left); 249 } 250 } 251 252private: 253 ArkUI_WaterFlowSectionOption *sectionOptions_ = nullptr; 254}; 255} // namespace NativeModule 256 257#endif // MYAPPLICATION_WATERFLOWSECTION_H 258 259``` 260 261## 创建瀑布流 262使用ArkUIWaterflowNode类管理Waterflow。支持通过SetLazyAdapter为其设置一个FlowItemAdapter,通过SetSection为其设置分段。 263 264```c++ 265//waterflow.h 266 267#ifndef MYAPPLICATION_WATERFLOW_H 268#define MYAPPLICATION_WATERFLOW_H 269 270#include "FlowItemAdapter.h" 271#include "WaterflowSection.h" 272 273namespace NativeModule { 274class ArkUIWaterflowNode { 275public: 276 277 ArkUIWaterflowNode() { 278 279 OH_ArkUI_GetModuleInterface(ARKUI_NATIVE_NODE, ArkUI_NativeNodeAPI_1, nodeApi_); 280 // 创建Waterflow 281 waterflow_ = nodeApi_->createNode(ARKUI_NODE_WATER_FLOW); 282 283 } 284 285 ~ArkUIWaterflowNode() { 286 nodeApi_->disposeNode(waterflow_); 287 288 // 销毁adapter 289 adapter_.reset(); 290 291 // 销毁分段 292 section_.reset(); 293 } 294 295 void SetWidth(float width) { 296 ArkUI_NumberValue value[] = {{.f32 = width}}; 297 ArkUI_AttributeItem item = {value, 1}; 298 nodeApi_->setAttribute(waterflow_, NODE_WIDTH, &item); 299 } 300 301 void SetHeight(float height) { 302 ArkUI_NumberValue value[] = {{.f32 = height}}; 303 ArkUI_AttributeItem item = {value, 1}; 304 nodeApi_->setAttribute(waterflow_, NODE_HEIGHT, &item); 305 } 306 307 void SetLazyAdapter(const std::shared_ptr<FlowItemAdapter> &adapter) { 308 ArkUI_AttributeItem item{nullptr,0, nullptr, adapter->GetAdapter()}; 309 nodeApi_->setAttribute(waterflow_, NODE_WATER_FLOW_NODE_ADAPTER, &item); 310 adapter_ = adapter; 311 } 312 313 void SetSection(const std::shared_ptr<WaterflowSection> §ion) { 314 ArkUI_NumberValue start[] = {{.i32 = 0}}; 315 ArkUI_AttributeItem optionsItem = {start, 1, nullptr, section->GetSectionOptions()}; 316 if(!section->GetSectionOptions()){ 317 return; 318 } 319 nodeApi_->setAttribute(waterflow_, NODE_WATER_FLOW_SECTION_OPTION, &optionsItem); 320 section_ = section; 321 } 322 323 ArkUI_NodeHandle GetWaterflow() { return waterflow_; } 324 325 std::shared_ptr<WaterflowSection> GetWaterflowSection() { return section_; } 326 327public: 328 ArkUI_NativeNodeAPI_1 *nodeApi_ = nullptr; 329 ArkUI_NodeHandle waterflow_ = nullptr; 330 331 std::shared_ptr<WaterflowSection> section_ = nullptr; 332 333 std::shared_ptr<FlowItemAdapter> adapter_; 334}; 335}// namespace NativeModule 336 337#endif // MYAPPLICATION_WATERFLOW_H 338``` 339 340## 使用瀑布流 341创建一个ArkUIWaterflowNode类的实例,设置其宽高,并绑定NodeAdapter和分段。 342 343```c++ 344// CreateWaterflowExample.h 345 346#ifndef MYAPPLICATION_CREATEWATERFLOWEXAMPLE_H 347#define MYAPPLICATION_CREATEWATERFLOWEXAMPLE_H 348#include "waterflow.h" 349 350namespace NativeModule { 351std::shared_ptr<ArkUIWaterflowNode> CreateWaterflowExample() { 352 // 创建Waterflow组件。 353 auto waterflow = std::make_shared<ArkUIWaterflowNode>(); 354 waterflow->SetHeight(600); 355 waterflow->SetWidth(400); 356 357 // 绑定Adapter 358 waterflow->SetLazyAdapter(std::make_shared<FlowItemAdapter>()); 359 360 // 设置分段 361 auto sections = std::make_shared<WaterflowSection>(); 362 SectionOption MARGIN_GAP_SECTION_1 = {10, 2, 10, 10, {20, 30, 40, 50}, nullptr, nullptr}; 363 SectionOption MARGIN_GAP_SECTION_2 = {10, 4, 10, 10, {20, 30, 40, 50}, nullptr, nullptr}; 364 for (int i = 0; i < 10; i++) { 365 sections->SetSection(sections->GetSectionOptions(), i, i % 2 ? MARGIN_GAP_SECTION_1 : MARGIN_GAP_SECTION_2); 366 } 367 waterflow->SetSection(sections); 368 369 return waterflow; 370} 371} // namespace NativeModule 372 373#endif // MYAPPLICATION_CREATEWATERFLOWEXAMPLE_H 374 375```