From d77956739fda4d41f5b8a499368e9f7fe2c995d1 Mon Sep 17 00:00:00 2001 From: Tianer Zhou Date: Wed, 24 Apr 2024 11:42:43 +0800 Subject: [PATCH 1/3] optimize sectioned waterFlow add item Signed-off-by: Tianer Zhou Change-Id: I3779ad863486f281e54a57097e729335c8b61e91 --- .../pattern/waterflow/water_flow_pattern.cpp | 8 ++ .../pattern/waterflow/water_flow_sections.cpp | 26 +++++ .../pattern/waterflow/water_flow_sections.h | 18 +++ .../waterflow/water_flow_segmented_layout.cpp | 2 + .../water_flow_segment_layout_test.cpp | 110 +++++++++++++++++- 5 files changed, 158 insertions(+), 6 deletions(-) diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp b/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp index 014b0f1915c..3f59ff5b470 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp @@ -438,8 +438,16 @@ RefPtr WaterFlowPattern::GetOrCreateWaterFlowSections() void WaterFlowPattern::OnSectionChanged(int32_t start) { + auto host = GetHost(); + CHECK_NULL_VOID(host); + int32_t childUpdateIdx = host->GetChildrenUpdated(); + if (childUpdateIdx > -1 && layoutInfo_.GetSegment(childUpdateIdx) == start && sections_->IsSpecialUpdate()) { + // optimize adding or removing children in the last section. Prevent complete reset of that section. + ++start; + } layoutInfo_.InitSegments(sections_->GetSectionInfo(), start); layoutInfo_.margins_.clear(); + MarkDirtyNodeSelf(); } diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_sections.cpp b/frameworks/core/components_ng/pattern/waterflow/water_flow_sections.cpp index 3c9c534c1fa..e4fcc5db51e 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_sections.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_sections.cpp @@ -19,6 +19,13 @@ namespace OHOS::Ace::NG { void WaterFlowSections::ChangeData( int32_t start, int32_t deleteCount, const std::vector& newSections) { + if (static_cast(start) + 1 == sections_.size() && deleteCount == 1 && newSections.size() == 1) { + // prepare for checking special case + prevSections_ = sections_; + } else { + prevSections_.clear(); + } + TAG_LOGI(AceLogTag::ACE_WATERFLOW, "section changed, start:%{public}d, deleteCount:%{public}d, newSections:%{public}zu", start, deleteCount, newSections.size()); @@ -37,4 +44,23 @@ void WaterFlowSections::ChangeData( onSectionDataChange_(start); } } + +bool WaterFlowSections::IsSpecialUpdate() const +{ + if (sections_.empty()) { + return false; + } + if (prevSections_.size() != sections_.size()) { + return false; + } + for (size_t i = 0; i < sections_.size() - 1; ++i) { + if (sections_[i] != prevSections_[i]) { + return false; + } + } + const auto& cur = sections_.back(); + const auto& prev = prevSections_.back(); + return cur.itemsCount != prev.itemsCount && cur.crossCount == prev.crossCount && + cur.columnsGap == prev.columnsGap && cur.rowsGap == prev.rowsGap && cur.margin == prev.margin; +} } // namespace OHOS::Ace::NG diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_sections.h b/frameworks/core/components_ng/pattern/waterflow/water_flow_sections.h index fc9ff00678e..cc07e13dbcc 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_sections.h +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_sections.h @@ -45,12 +45,30 @@ public: return sections_; } + /** + * @brief check if the last update is a special case where the user is only adding / deleting items to the last section. + * + * @return true only if itemCount in the last section has changed and everything else remains the same. + */ + bool IsSpecialUpdate() const; + private: + std::vector
prevSections_; // for comparing and handling special cases std::vector
sections_; std::function onSectionDataChange_; }; struct WaterFlowSections::Section { + bool operator==(const Section& other) const + { + return itemsCount == other.itemsCount && crossCount == other.crossCount && columnsGap == other.columnsGap && + rowsGap == other.rowsGap && margin == other.margin; + } + bool operator!=(const Section& other) const + { + return !(*this == other); + } + int32_t itemsCount = 0; std::optional crossCount; diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_segmented_layout.cpp b/frameworks/core/components_ng/pattern/waterflow/water_flow_segmented_layout.cpp index 6040e0328cf..3a3c43b2749 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_segmented_layout.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_segmented_layout.cpp @@ -172,7 +172,9 @@ void WaterFlowSegmentedLayout::Init(const SizeF& frameSize) } info_.ClearCacheAfterIndex(updateIdx - 1); wrapper_->GetHostNode()->ChildrenUpdatedFrom(-1); + return; } + if (!wrapper_->IsContraintNoChanged()) { postJumpOffset_ = PrepareJump(info_); } diff --git a/test/unittest/core/pattern/waterflow/water_flow_segment_layout_test.cpp b/test/unittest/core/pattern/waterflow/water_flow_segment_layout_test.cpp index ce4f6720c13..f2725949434 100644 --- a/test/unittest/core/pattern/waterflow/water_flow_segment_layout_test.cpp +++ b/test/unittest/core/pattern/waterflow/water_flow_segment_layout_test.cpp @@ -1139,16 +1139,15 @@ HWTEST_F(WaterFlowSegmentTest, Segmented007, TestSize.Level1) secObj->ChangeData(0, 0, SECTION_4); MockPipelineContext::GetCurrent()->FlushBuildFinishCallbacks(); FlushLayoutTask(frameNode_); - EXPECT_TRUE(IsEqual(pattern_->GetItemRect(0), Rect(0, 0, 400.0f/3, 100))); - EXPECT_TRUE(IsEqual(pattern_->GetItemRect(2), Rect(400.0f/3*2, 0, 400.0f/3, 100))); + EXPECT_TRUE(IsEqual(pattern_->GetItemRect(0), Rect(0, 0, 400.0f / 3, 100))); + EXPECT_TRUE(IsEqual(pattern_->GetItemRect(2), Rect(400.0f / 3 * 2, 0, 400.0f / 3, 100))); layoutProperty_->UpdateLayoutDirection(TextDirection::RTL); FlushLayoutTask(frameNode_); - EXPECT_TRUE(IsEqual(pattern_->GetItemRect(0), Rect(400.0f/3*2, 0, 400.0f/3, 100))); - EXPECT_TRUE(IsEqual(pattern_->GetItemRect(2), Rect(0, 0, 400.0f/3, 100))); + EXPECT_TRUE(IsEqual(pattern_->GetItemRect(0), Rect(400.0f / 3 * 2, 0, 400.0f / 3, 100))); + EXPECT_TRUE(IsEqual(pattern_->GetItemRect(2), Rect(0, 0, 400.0f / 3, 100))); } - /** * @tc.name: CheckHeight001 * @tc.desc: Layout WaterFlow and check if callback height is used @@ -1635,12 +1634,111 @@ HWTEST_F(WaterFlowSegmentTest, Replace002, TestSize.Level1) secObj->ChangeData(0, 3, SECTION_7); MockPipelineContext::GetCurrent()->FlushBuildFinishCallbacks(); FlushLayoutTask(frameNode_); - + EXPECT_EQ(info.currentOffset_, -300.0f); EXPECT_EQ(info.startIndex_, 2); EXPECT_EQ(info.storedOffset_, -95); } +/** + * @tc.name: Replace003 + * @tc.desc: Layout WaterFlow and then add item in last section. + * @tc.type: FUNC + */ +HWTEST_F(WaterFlowSegmentTest, Replace003, TestSize.Level1) +{ + Create( + [](WaterFlowModelNG model) { + ViewAbstract::SetWidth(CalcLength(400.0f)); + ViewAbstract::SetHeight(CalcLength(600.f)); + CreateItem(37); + }, + false); + auto secObj = pattern_->GetOrCreateWaterFlowSections(); + secObj->ChangeData(0, 0, SECTION_7); + MockPipelineContext::GetCurrent()->FlushBuildFinishCallbacks(); + FlushLayoutTask(frameNode_); + auto& info = pattern_->layoutInfo_; + UpdateCurrentOffset(-2000.0f); + EXPECT_EQ(info.segmentStartPos_[2], 613.0f); + EXPECT_EQ(info.currentOffset_, -2000.0f); + EXPECT_EQ(info.startIndex_, 20); + EXPECT_EQ(info.endIndex_, 26); + EXPECT_EQ(info.itemInfos_.size(), 27); + EXPECT_EQ(info.endPosArray_.size(), 26); + + AddItems(10); + frameNode_->ChildrenUpdatedFrom(37); + std::vector newSection { SECTION_7[2] }; + newSection[0].itemsCount = 40; + secObj->ChangeData(2, 1, newSection); + MockPipelineContext::GetCurrent()->FlushBuildFinishCallbacks(); + EXPECT_EQ(info.itemInfos_.size(), 27); + EXPECT_EQ(info.items_[2].at(0).size(), 20); + EXPECT_EQ(info.endPosArray_.size(), 26); + EXPECT_EQ(info.segmentStartPos_[2], 613.0f); + EXPECT_EQ(info.segmentStartPos_.size(), 3); + EXPECT_EQ(info.segmentTails_.size(), 3); + EXPECT_EQ(info.segmentTails_[2], 46); + + FlushLayoutTask(frameNode_); + EXPECT_EQ(info.currentOffset_, -2000.0f); + EXPECT_EQ(info.startIndex_, 20); +} + +/** + * @tc.name: Replace004 + * @tc.desc: Layout WaterFlow and then add item in last section. + * @tc.type: FUNC + */ +HWTEST_F(WaterFlowSegmentTest, Replace004, TestSize.Level1) +{ + Create( + [](WaterFlowModelNG model) { + ViewAbstract::SetWidth(CalcLength(400.0f)); + ViewAbstract::SetHeight(CalcLength(600.f)); + CreateItem(6); + }, + false); + auto secObj = pattern_->GetOrCreateWaterFlowSections(); + secObj->ChangeData(0, 0, SECTION_9); + MockPipelineContext::GetCurrent()->FlushBuildFinishCallbacks(); + FlushLayoutTask(frameNode_); + auto& info = pattern_->layoutInfo_; + + AddItems(10); + frameNode_->ChildrenUpdatedFrom(6); + std::vector newSection { SECTION_9[0] }; + newSection[0].itemsCount = 16; + secObj->ChangeData(0, 1, newSection); + MockPipelineContext::GetCurrent()->FlushBuildFinishCallbacks(); + EXPECT_EQ(info.itemInfos_.size(), 6); + EXPECT_EQ(info.items_[0].at(0).size(), 2); + EXPECT_EQ(info.endPosArray_.size(), 2); + EXPECT_EQ(info.segmentStartPos_[0], 0.0f); + EXPECT_EQ(info.segmentStartPos_.size(), 1); + EXPECT_EQ(info.segmentTails_.size(), 1); + EXPECT_EQ(info.segmentTails_[0], 15); + + FlushLayoutTask(frameNode_); + EXPECT_EQ(info.currentOffset_, 0.0f); + EXPECT_EQ(info.endIndex_, 15); + EXPECT_EQ(info.items_[0].at(0).size(), 6); + + for (int i = 0; i < 6; ++i) { + frameNode_->RemoveChildAtIndex(10); + } + frameNode_->ChildrenUpdatedFrom(10); + newSection[0].itemsCount = 10; + secObj->ChangeData(0, 1, newSection); + MockPipelineContext::GetCurrent()->FlushBuildFinishCallbacks(); + EXPECT_EQ(info.segmentTails_[0], 9); + FlushLayoutTask(frameNode_); + EXPECT_EQ(info.currentOffset_, 0.0f); + EXPECT_EQ(info.endIndex_, 9); + EXPECT_EQ(info.items_[0].at(0).size(), 4); +} + /** * @tc.name: Illegal001 * @tc.desc: Layout WaterFlow with empty sections. -- Gitee From e79910f5f5d278a626fcf07a6f0705f6c123e917 Mon Sep 17 00:00:00 2001 From: Tianer Zhou Date: Wed, 24 Apr 2024 14:32:52 +0800 Subject: [PATCH 2/3] fix code check Signed-off-by: Tianer Zhou Change-Id: If528821d9378bb2b8dd1632e9606be71dd3694c7 --- .../components_ng/pattern/waterflow/water_flow_sections.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_sections.h b/frameworks/core/components_ng/pattern/waterflow/water_flow_sections.h index cc07e13dbcc..e14ec4977c9 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_sections.h +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_sections.h @@ -46,8 +46,8 @@ public: } /** - * @brief check if the last update is a special case where the user is only adding / deleting items to the last section. - * + * @brief check if last update was a special case where only itemCount in the last section is modified. + * * @return true only if itemCount in the last section has changed and everything else remains the same. */ bool IsSpecialUpdate() const; -- Gitee From 7327ef4a0fef62edb36169111fe96291ed07a3a0 Mon Sep 17 00:00:00 2001 From: Tianer Zhou Date: Thu, 25 Apr 2024 10:17:22 +0800 Subject: [PATCH 3/3] add and fix extreme delete case Signed-off-by: Tianer Zhou Change-Id: I66bbd5cfd962f5328f64917f91e25bd558810ba4 --- .../waterflow/water_flow_segmented_layout.cpp | 2 +- .../waterflow/water_flow_segment_layout_test.cpp | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_segmented_layout.cpp b/frameworks/core/components_ng/pattern/waterflow/water_flow_segmented_layout.cpp index 3a3c43b2749..48238ffb8f8 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_segmented_layout.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_segmented_layout.cpp @@ -128,7 +128,7 @@ float PrepareJump(WaterFlowLayoutInfo& info) // implies that LayoutInfo has already been reset, no need to jump return 0.0f; } - info.jumpIndex_ = info.startIndex_; + info.jumpIndex_ = std::min(info.startIndex_, info.childrenCount_ - 1); info.align_ = ScrollAlign::START; float itemOffset = (info.itemInfos_.size() <= static_cast(info.startIndex_)) ? info.storedOffset_ diff --git a/test/unittest/core/pattern/waterflow/water_flow_segment_layout_test.cpp b/test/unittest/core/pattern/waterflow/water_flow_segment_layout_test.cpp index f2725949434..8ea8c4988bc 100644 --- a/test/unittest/core/pattern/waterflow/water_flow_segment_layout_test.cpp +++ b/test/unittest/core/pattern/waterflow/water_flow_segment_layout_test.cpp @@ -1706,10 +1706,10 @@ HWTEST_F(WaterFlowSegmentTest, Replace004, TestSize.Level1) FlushLayoutTask(frameNode_); auto& info = pattern_->layoutInfo_; - AddItems(10); + AddItems(100); frameNode_->ChildrenUpdatedFrom(6); std::vector newSection { SECTION_9[0] }; - newSection[0].itemsCount = 16; + newSection[0].itemsCount = 106; secObj->ChangeData(0, 1, newSection); MockPipelineContext::GetCurrent()->FlushBuildFinishCallbacks(); EXPECT_EQ(info.itemInfos_.size(), 6); @@ -1718,14 +1718,15 @@ HWTEST_F(WaterFlowSegmentTest, Replace004, TestSize.Level1) EXPECT_EQ(info.segmentStartPos_[0], 0.0f); EXPECT_EQ(info.segmentStartPos_.size(), 1); EXPECT_EQ(info.segmentTails_.size(), 1); - EXPECT_EQ(info.segmentTails_[0], 15); + EXPECT_EQ(info.segmentTails_[0], 105); FlushLayoutTask(frameNode_); EXPECT_EQ(info.currentOffset_, 0.0f); - EXPECT_EQ(info.endIndex_, 15); + EXPECT_EQ(info.endIndex_, 17); EXPECT_EQ(info.items_[0].at(0).size(), 6); - - for (int i = 0; i < 6; ++i) { + UpdateCurrentOffset(-10000.0f); + EXPECT_EQ(info.currentOffset_, -3000.0f); + for (int i = 0; i < 100; ++i) { frameNode_->RemoveChildAtIndex(10); } frameNode_->ChildrenUpdatedFrom(10); -- Gitee