• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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> &section) {
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```