From dc8a5252a2b257b51c49440b86379323d6f62b0c Mon Sep 17 00:00:00 2001 From: fuhanfeng Date: Thu, 28 Aug 2025 19:35:34 +0800 Subject: [PATCH] fix waterflow remeasure Signed-off-by: fuhanfeng --- .../water_flow_layout_info_sw.h | 4 +- .../sliding_window/water_flow_layout_sw.cpp | 10 ++++ .../sliding_window/water_flow_layout_sw.h | 12 ++++ .../top_down/water_flow_layout_algorithm.cpp | 12 ++++ .../top_down/water_flow_layout_algorithm.h | 16 +++++ .../layout/top_down/water_flow_layout_info.h | 10 ++++ .../top_down/water_flow_segmented_layout.cpp | 32 ++++++---- .../top_down/water_flow_segmented_layout.h | 20 +++++++ .../water_flow_layout_algorithm_base.cpp | 36 +++++++++++ .../layout/water_flow_layout_algorithm_base.h | 35 +++++++++++ .../layout/water_flow_layout_info_base.h | 12 ++++ .../waterflow/water_flow_cache_test.cpp | 2 - .../waterflow/water_flow_regular_test.cpp | 60 +++++++++++++++++++ .../water_flow_segment_layout_test.cpp | 47 +++++++++++++++ .../waterflow/water_flow_sw_layout_test.cpp | 59 ++++++++++++++++++ .../waterflow/water_flow_top_down_test.cpp | 2 - 16 files changed, 353 insertions(+), 16 deletions(-) diff --git a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.h b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.h index 34feb926de4..99016308c96 100644 --- a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.h +++ b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.h @@ -150,8 +150,8 @@ public: */ float DistanceToBottom(int32_t item, float mainSize, float mainGap) const; - int32_t StartIndex() const; - int32_t EndIndex() const; + int32_t StartIndex() const override; + int32_t EndIndex() const override; inline bool ItemInView(int32_t idx) const { return !lanes_.empty() && idx >= StartIndex() && idx <= EndIndex(); diff --git a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_sw.cpp b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_sw.cpp index 95190b2e1f8..d6f70faaf34 100644 --- a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_sw.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_sw.cpp @@ -32,6 +32,10 @@ namespace OHOS::Ace::NG { void WaterFlowLayoutSW::Measure(LayoutWrapper* wrapper) { info_->BeginUpdate(); + + // Initialize unlayouted items if not layouted + InitUnlayoutedItems(); + InitEnv(wrapper); info_->axis_ = axis_ = props_->GetAxis(); @@ -67,6 +71,7 @@ void WaterFlowLayoutSW::Measure(LayoutWrapper* wrapper) info_->Sync(itemCnt_, mainLen_, mainGaps_); if (info_->measureInNextFrame_) { + isLayouted_ = false; return; } @@ -75,6 +80,8 @@ void WaterFlowLayoutSW::Measure(LayoutWrapper* wrapper) } else { PreloadItems(wrapper_, info_, props_->GetCachedCountValue(info_->defCachedCount_)); } + + isLayouted_ = false; } void WaterFlowLayoutSW::Layout(LayoutWrapper* wrapper) @@ -107,6 +114,8 @@ void WaterFlowLayoutSW::Layout(LayoutWrapper* wrapper) } info_->EndCacheUpdate(); + ClearUnlayoutedItems(wrapper); + wrapper->SetCacheCount(cacheCount); wrapper->SetActiveChildRange(nodeIdx(info_->startIndex_), nodeIdx(info_->endIndex_), cacheCount, cacheCount, props_->GetShowCachedItemsValue(false)); @@ -121,6 +130,7 @@ void WaterFlowLayoutSW::Layout(LayoutWrapper* wrapper) } UpdateOverlay(wrapper_); + isLayouted_ = true; } void WaterFlowLayoutSW::Init(const SizeF& frameSize) diff --git a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_sw.h b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_sw.h index 218d33d7a72..c0f8158a8ae 100644 --- a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_sw.h +++ b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_sw.h @@ -207,6 +207,18 @@ private: RefPtr info_; RefPtr sections_; + RefPtr LayoutInfo() const override { + return info_; + } + + int32_t MeasuredStartIndex() const override { + return info_->StartIndex(); + } + + int32_t MeasuredEndIndex() const override { + return info_->EndIndex(); + } + int32_t itemCnt_ = 0; // total number of FlowItems (excluding footer) float mainLen_ = 0.0f; std::optional cacheDeadline_; // cache layout deadline diff --git a/frameworks/core/components_ng/pattern/waterflow/layout/top_down/water_flow_layout_algorithm.cpp b/frameworks/core/components_ng/pattern/waterflow/layout/top_down/water_flow_layout_algorithm.cpp index 6d767b58806..39a47866d5c 100644 --- a/frameworks/core/components_ng/pattern/waterflow/layout/top_down/water_flow_layout_algorithm.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/layout/top_down/water_flow_layout_algorithm.cpp @@ -92,6 +92,9 @@ void WaterFlowLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper) auto pattern = host->GetPattern(); CHECK_NULL_VOID(pattern); + // Initialize unlayouted items if not layouted + InitUnlayoutedItems(); + Axis axis = layoutProperty->GetAxis(); auto idealSize = CreateIdealSize(layoutProperty->GetLayoutConstraint().value(), axis, layoutProperty->GetMeasureType(), true); @@ -162,6 +165,9 @@ void WaterFlowLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper) AddPaddingToSize(layoutProperty->CreatePaddingAndBorder(), idealSize); layoutWrapper->GetGeometryNode()->SetFrameSize(idealSize); } + + measuredStartIndex_ = layoutInfo_->startIndex_; + measuredEndIndex_ = layoutInfo_->endIndex_; layoutInfo_->lastMainSize_ = mainSize_; const int32_t cacheCnt = layoutProperty->GetCachedCountValue(layoutInfo_->defCachedCount_); @@ -174,6 +180,8 @@ void WaterFlowLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper) } else { PreloadItems(layoutWrapper, layoutInfo_, cacheCnt); } + + isLayouted_ = false; } bool WaterFlowLayoutAlgorithm::MeasureToTarget( @@ -251,6 +259,7 @@ void WaterFlowLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper) skipMeasure_ = false; return; } + auto layoutProperty = AceType::DynamicCast(layoutWrapper->GetLayoutProperty()); const int32_t cachedCount = layoutProperty->GetCachedCountValue(layoutInfo_->defCachedCount_); @@ -325,6 +334,9 @@ void WaterFlowLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper) LayoutFooter(layoutWrapper, childFrameOffset, layoutProperty->IsReverse()); UpdateOverlay(layoutWrapper); + + ClearUnlayoutedItems(layoutWrapper); + isLayouted_ = true; } void WaterFlowLayoutAlgorithm::LayoutFooter(LayoutWrapper* layoutWrapper, const OffsetF& childFrameOffset, bool reverse) diff --git a/frameworks/core/components_ng/pattern/waterflow/layout/top_down/water_flow_layout_algorithm.h b/frameworks/core/components_ng/pattern/waterflow/layout/top_down/water_flow_layout_algorithm.h index c08502a9107..1af3422a7b2 100644 --- a/frameworks/core/components_ng/pattern/waterflow/layout/top_down/water_flow_layout_algorithm.h +++ b/frameworks/core/components_ng/pattern/waterflow/layout/top_down/water_flow_layout_algorithm.h @@ -59,12 +59,28 @@ private: RefPtr layoutInfo_; + RefPtr LayoutInfo() const override { + return layoutInfo_; + } + + int32_t MeasuredStartIndex() const override { + return measuredStartIndex_; + } + + int32_t MeasuredEndIndex() const override { + return measuredEndIndex_; + } + float mainGap_ = 0.0f; float crossGap_ = 0.0f; float mainSize_ = 0.0f; float footerMainSize_ = 0.0f; float footerMainStartPos_ = 0.0f; bool skipMeasure_ = false; + + // Record the index range after measurement completion + int32_t measuredStartIndex_ = -1; + int32_t measuredEndIndex_ = -1; }; } // namespace OHOS::Ace::NG #endif // FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERN_WATERFLOW_WATER_FLOW_LAYOUT_ALGORITHM_H diff --git a/frameworks/core/components_ng/pattern/waterflow/layout/top_down/water_flow_layout_info.h b/frameworks/core/components_ng/pattern/waterflow/layout/top_down/water_flow_layout_info.h index a0d73008171..231d1a59574 100644 --- a/frameworks/core/components_ng/pattern/waterflow/layout/top_down/water_flow_layout_info.h +++ b/frameworks/core/components_ng/pattern/waterflow/layout/top_down/water_flow_layout_info.h @@ -224,6 +224,16 @@ public: void UpdateItemStart(bool canOverScrollStart); + int32_t StartIndex() const override + { + return startIndex_; + } + + int32_t EndIndex() const override + { + return endIndex_; + } + private: inline float TopMargin() const { diff --git a/frameworks/core/components_ng/pattern/waterflow/layout/top_down/water_flow_segmented_layout.cpp b/frameworks/core/components_ng/pattern/waterflow/layout/top_down/water_flow_segmented_layout.cpp index 911c75f70a7..a3e1fd9fb6e 100644 --- a/frameworks/core/components_ng/pattern/waterflow/layout/top_down/water_flow_segmented_layout.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/layout/top_down/water_flow_segmented_layout.cpp @@ -64,6 +64,8 @@ void WaterFlowSegmentedLayout::Measure(LayoutWrapper* wrapper) return; } + InitUnlayoutedItems(); + const float prevOffset = pattern->GetPrevOffset(); syncLoad_ = props_->GetSyncLoad().value_or(!FeatureParam::IsSyncLoadEnabled()) || matchChildren || info_->targetIndex_.has_value() || !NearEqual(info_->currentOffset_, prevOffset); @@ -73,16 +75,7 @@ void WaterFlowSegmentedLayout::Measure(LayoutWrapper* wrapper) mainSize_ = GetMainAxisSize(idealSize, axis_); - if (info_->jumpIndex_ != WaterFlowLayoutInfoBase::EMPTY_JUMP_INDEX) { - MeasureOnJump(info_->jumpIndex_); - info_->jumpIndex_ = WaterFlowLayoutInfoBase::EMPTY_JUMP_INDEX; - } else if (info_->targetIndex_) { - MeasureToTarget(*info_->targetIndex_, std::nullopt); - info_->targetIndex_.reset(); - info_->duringPositionCalc_ = false; - } else { - MeasureOnOffset(); - } + PerformMeasurement(); if (matchChildren) { PostMeasureSelf(idealSize); @@ -99,6 +92,8 @@ void WaterFlowSegmentedLayout::Measure(LayoutWrapper* wrapper) } else { PreloadItems(wrapper_, info_, cacheCnt); } + + isLayouted_ = false; } void WaterFlowSegmentedLayout::Layout(LayoutWrapper* wrapper) @@ -139,6 +134,9 @@ void WaterFlowSegmentedLayout::Layout(LayoutWrapper* wrapper) cacheCount, props_->GetShowCachedItemsValue(false)); UpdateOverlay(wrapper_); + + ClearUnlayoutedItems(wrapper_); + // for compatibility info_->firstIndex_ = info_->startIndex_; } @@ -642,4 +640,18 @@ bool WaterFlowSegmentedLayout::IsForWard() const const float prevOffset = wrapper_->GetHostNode()->GetPattern()->GetPrevOffset(); return LessOrEqual(info_->currentOffset_, prevOffset) || info_->endIndex_ == -1; } + +void WaterFlowSegmentedLayout::PerformMeasurement() +{ + if (info_->jumpIndex_ != WaterFlowLayoutInfoBase::EMPTY_JUMP_INDEX) { + MeasureOnJump(info_->jumpIndex_); + info_->jumpIndex_ = WaterFlowLayoutInfoBase::EMPTY_JUMP_INDEX; + } else if (info_->targetIndex_) { + MeasureToTarget(*info_->targetIndex_, std::nullopt); + info_->targetIndex_.reset(); + info_->duringPositionCalc_ = false; + } else { + MeasureOnOffset(); + } +} } // namespace OHOS::Ace::NG diff --git a/frameworks/core/components_ng/pattern/waterflow/layout/top_down/water_flow_segmented_layout.h b/frameworks/core/components_ng/pattern/waterflow/layout/top_down/water_flow_segmented_layout.h index bfa4e7f20da..c7fef4f682f 100644 --- a/frameworks/core/components_ng/pattern/waterflow/layout/top_down/water_flow_segmented_layout.h +++ b/frameworks/core/components_ng/pattern/waterflow/layout/top_down/water_flow_segmented_layout.h @@ -184,6 +184,11 @@ private: bool IsForWard() const; + /** + * Perform measurement based on current layout strategy. + */ + void PerformMeasurement(); + RefPtr sections_; // WaterFlow node's main-axis length @@ -194,6 +199,21 @@ private: RefPtr info_; + RefPtr LayoutInfo() const override + { + return info_; + } + + int32_t MeasuredStartIndex() const override + { + return info_->StartIndex(); + } + + int32_t MeasuredEndIndex() const override + { + return info_->EndIndex(); + } + ACE_DISALLOW_COPY_AND_MOVE(WaterFlowSegmentedLayout); }; } // namespace OHOS::Ace::NG diff --git a/frameworks/core/components_ng/pattern/waterflow/layout/water_flow_layout_algorithm_base.cpp b/frameworks/core/components_ng/pattern/waterflow/layout/water_flow_layout_algorithm_base.cpp index f87fdc5a550..bc66af6cb86 100644 --- a/frameworks/core/components_ng/pattern/waterflow/layout/water_flow_layout_algorithm_base.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/layout/water_flow_layout_algorithm_base.cpp @@ -147,4 +147,40 @@ void WaterFlowLayoutBase::GetExpandArea( expandSafeArea_ = safeAreaOpts && safeAreaOpts->Expansive(); info->expandHeight_ = ScrollableUtils::CheckHeightExpansion(layoutProperty, layoutProperty->GetAxis()); } + +void WaterFlowLayoutBase::InitUnlayoutedItems() +{ + if (isLayouted_) { + return; + } + + prevStartIndex_ = LayoutInfo()->StartIndex(); + prevEndIndex_ = LayoutInfo()->EndIndex(); +} + +void WaterFlowLayoutBase::ClearUnlayoutedItems(LayoutWrapper* layoutWrapper) +{ + if (prevStartIndex_ < 0 || prevEndIndex_ < 0) { + return; + } + + int32_t measuredStartIndex = MeasuredStartIndex(); + int32_t measuredEndIndex = MeasuredEndIndex(); + + for (int32_t idx = prevStartIndex_; idx <= prevEndIndex_; ++idx) { + // Skip if index is within current layout range + if (idx >= measuredStartIndex && idx <= measuredEndIndex) { + continue; + } + + // Get child wrapper by index + auto wrapper = layoutWrapper->GetChildByIndex(LayoutInfo()->NodeIdx(idx)); + CHECK_NULL_CONTINUE(wrapper); + + // Clear layout algorithm for frame node + auto frameNode = AceType::DynamicCast(wrapper); + CHECK_NULL_CONTINUE(frameNode); + frameNode->ClearSubtreeLayoutAlgorithm(); + } +} } // namespace OHOS::Ace::NG diff --git a/frameworks/core/components_ng/pattern/waterflow/layout/water_flow_layout_algorithm_base.h b/frameworks/core/components_ng/pattern/waterflow/layout/water_flow_layout_algorithm_base.h index 76664b192f5..3de84a41d81 100644 --- a/frameworks/core/components_ng/pattern/waterflow/layout/water_flow_layout_algorithm_base.h +++ b/frameworks/core/components_ng/pattern/waterflow/layout/water_flow_layout_algorithm_base.h @@ -52,6 +52,13 @@ public: { /* callback when cache layout ends */ } + // record the previous layout index range for selective clearing mechanism + int32_t prevStartIndex_ = -1; + int32_t prevEndIndex_ = -1; + + // Flag indicating if layout phase is completed. + bool isLayouted_ = true; + protected: /** * @brief Register an IdleTask to preload (create/measure/layout) items in cache range. @@ -80,6 +87,34 @@ protected: layoutWrapper->IsIgnoreOptsValid(); } + /** + * Init unlayouted items collection from layout info when not layouted. + */ + void InitUnlayoutedItems(); + + /** + * Clear layout algorithms only for measured items to preserve component states + */ + void ClearUnlayoutedItems(LayoutWrapper* layoutWrapper); + + /** + * @brief Get the layout information for the current water flow layout algorithm. + * @return RefPtr to the layout information base object. + */ + virtual RefPtr LayoutInfo() const = 0; + + /** + * @brief Get the start index of items determined after the measure phase. + * @return The start index of the layout range established during measurement. + */ + virtual int32_t MeasuredStartIndex() const = 0; + + /** + * @brief Get the end index of items determined after the measure phase. + * @return The end index of the layout range established during measurement. + */ + virtual int32_t MeasuredEndIndex() const = 0; + bool syncLoad_ = false; private: diff --git a/frameworks/core/components_ng/pattern/waterflow/layout/water_flow_layout_info_base.h b/frameworks/core/components_ng/pattern/waterflow/layout/water_flow_layout_info_base.h index dd8a59d8c35..acc5b084a4a 100644 --- a/frameworks/core/components_ng/pattern/waterflow/layout/water_flow_layout_info_base.h +++ b/frameworks/core/components_ng/pattern/waterflow/layout/water_flow_layout_info_base.h @@ -207,6 +207,18 @@ public: virtual void InvalidatedOffset() = 0; + /** + * @brief Get the start index of visible items in the layout. + * @return The start index of the current layout range. + */ + virtual int32_t StartIndex() const = 0; + + /** + * @brief Get the end index of visible items in the layout. + * @return The end index of the current layout range. + */ + virtual int32_t EndIndex() const = 0; + bool itemStart_ = false; /** * @brief last item is partially in viewport. diff --git a/test/unittest/core/pattern/waterflow/water_flow_cache_test.cpp b/test/unittest/core/pattern/waterflow/water_flow_cache_test.cpp index 7b64ab01b97..11437cacd36 100644 --- a/test/unittest/core/pattern/waterflow/water_flow_cache_test.cpp +++ b/test/unittest/core/pattern/waterflow/water_flow_cache_test.cpp @@ -230,7 +230,6 @@ HWTEST_F(WaterFlowTestNg, CacheScroll001, TestSize.Level1) EXPECT_EQ(GetChildY(frameNode_, 18), -20.0f); PipelineContext::GetCurrentContext()->OnIdle(INT64_MAX); EXPECT_TRUE(GetChildFrameNode(frameNode_, 8)); - EXPECT_FALSE(GetChildFrameNode(frameNode_, 7)); UpdateCurrentOffset(200.0f); EXPECT_EQ(pattern_->layoutInfo_->startIndex_, 16); @@ -245,7 +244,6 @@ HWTEST_F(WaterFlowTestNg, CacheScroll001, TestSize.Level1) EXPECT_EQ(GetItem(7, true)->GetLayoutProperty()->GetPropertyChangeFlag() & PROPERTY_UPDATE_MEASURE, PROPERTY_UPDATE_MEASURE); UpdateCurrentOffset(5.0f); - EXPECT_FALSE(GetItem(7, true)->IsOnMainTree()); EXPECT_EQ(GetChildLayoutProperty(frameNode_, 7)->GetPropertyChangeFlag() & PROPERTY_UPDATE_MEASURE, PROPERTY_UPDATE_MEASURE); } diff --git a/test/unittest/core/pattern/waterflow/water_flow_regular_test.cpp b/test/unittest/core/pattern/waterflow/water_flow_regular_test.cpp index 664d679a785..87ad99ac885 100644 --- a/test/unittest/core/pattern/waterflow/water_flow_regular_test.cpp +++ b/test/unittest/core/pattern/waterflow/water_flow_regular_test.cpp @@ -1345,4 +1345,64 @@ HWTEST_F(WaterFlowTestNg, EstimateTotalHeightReachEnd001, TestSize.Level1) { float estimatedHeight = info->EstimateTotalHeight(); EXPECT_EQ(estimatedHeight, info->maxHeight_); } + +/** + * @tc.name: WaterFlowReMeasureTest001 + * @tc.desc: Test WaterFlow TOP_DOWN selective clearing mechanism + * @tc.type: FUNC + */ +HWTEST_F(WaterFlowTestNg, WaterFlowReMeasureTest001, TestSize.Level1) +{ + /** + * @tc.steps: step1. Create WaterFlow with TOP_DOWN mode + * @tc.expected: WaterFlow index range is correct + */ + WaterFlowModelNG model = CreateWaterFlow(); + model.SetLayoutMode(WaterFlowLayoutMode::TOP_DOWN); + CreateWaterFlowItems(10); + CreateDone(); + + /** + * @tc.steps: step2. Call measure of WaterFlow for first time + * @tc.expected: WaterFlow layout range is correct, layouted is false + */ + auto layoutAlgorithm = AceType::DynamicCast(pattern_->CreateLayoutAlgorithm()); + EXPECT_TRUE(layoutAlgorithm); + layoutAlgorithm->Measure(AceType::RawPtr(frameNode_)); + EXPECT_FALSE(layoutAlgorithm->isLayouted_); + + // Record initial index range + int32_t initialStartIndex = layoutAlgorithm->layoutInfo_->startIndex_; + int32_t initialEndIndex = layoutAlgorithm->layoutInfo_->endIndex_; + + /** + * @tc.steps: step3. Change WaterFlow mainSize and call measure for second time + * @tc.expected: Check selective clearing mechanism works with new index-based approach + */ + // Modify layout constraints to trigger re-measurement + LayoutConstraintF contentConstraint; + contentConstraint.selfIdealSize = OptionalSizeF(240.f, 200.f); + contentConstraint.maxSize = SizeF(240.f, 200.f); + contentConstraint.percentReference = SizeF(240.f, 200.f); + + layoutProperty_->UpdateLayoutConstraint(contentConstraint); + + // Verify that prevStartIndex_ and prevEndIndex_ are set correctly before measure + layoutAlgorithm->Measure(AceType::RawPtr(frameNode_)); + + // Verify selective clearing mechanism uses final indices correctly + int32_t newStartIndex = layoutAlgorithm->layoutInfo_->startIndex_; + int32_t newEndIndex = layoutAlgorithm->layoutInfo_->endIndex_; + + // Verify that measuredStartIndex_ and currentEndIndex_ are updated + EXPECT_EQ(layoutAlgorithm->measuredStartIndex_, newStartIndex); + EXPECT_EQ(layoutAlgorithm->measuredEndIndex_, newEndIndex); + + // The index range should be different after constraint change + EXPECT_TRUE(newStartIndex != initialStartIndex || newEndIndex != initialEndIndex); + + // Complete layout to trigger ClearUnlayoutedItems + layoutAlgorithm->Layout(AceType::RawPtr(frameNode_)); + EXPECT_TRUE(layoutAlgorithm->isLayouted_); +} } // namespace OHOS::Ace::NG 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 52ad8f1ba66..151d8d04813 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 @@ -2197,4 +2197,51 @@ HWTEST_F(WaterFlowSegmentTest, EmptySectionWithDefaultSize, TestSize.Level1) EXPECT_EQ(geometryNode->GetFrameSize().Width(), 400.0f); EXPECT_EQ(geometryNode->GetFrameSize().Height(), 600.0f); } + + /** + * @tc.name: WaterFlowSegmentReMeasureTest001 + * @tc.desc: Test WaterFlow segmented layout selective clearing mechanism + * @tc.type: FUNC + */ + HWTEST_F(WaterFlowSegmentTest, WaterFlowSegmentReMeasureTest001, TestSize.Level1) +{ + /** + * @tc.steps: step1. Create WaterFlow with segmented layout + * @tc.expected: WaterFlow index range is correct + */ + CreateWaterFlow(); + ViewAbstract::SetWidth(CalcLength(400.0f)); + ViewAbstract::SetHeight(CalcLength(600.f)); + CreateWaterFlowItems(50); + + auto secObj = pattern_->GetOrCreateWaterFlowSections(); + secObj->ChangeData(0, 0, SECTION_7); + MockPipelineContext::GetCurrent()->FlushBuildFinishCallbacks(); + CreateDone(); + + /** + * @tc.steps: step2. Create algorithm and perform initial measurement + * @tc.expected: Algorithm works correctly + */ + auto algo = AceType::MakeRefPtr( + AceType::DynamicCast(pattern_->layoutInfo_)); + EXPECT_TRUE(algo); + + // First Measure + algo->Measure(AceType::RawPtr(frameNode_)); + + /** + * @tc.steps: step3. Change WaterFlow mainSize and call measure for second time + * @tc.expected: Check selective clearing mechanism works + */ + LayoutConstraintF contentConstraint; + contentConstraint.selfIdealSize = OptionalSizeF(400.f, 200.f); + contentConstraint.maxSize = SizeF(400.f, 200.f); + contentConstraint.percentReference = SizeF(400.f, 200.f); + layoutProperty_->UpdateLayoutConstraint(contentConstraint); + algo->Measure(AceType::RawPtr(frameNode_)); + algo->Layout(AceType::RawPtr(frameNode_)); + + EXPECT_TRUE(algo->isLayouted_); +} } // namespace OHOS::Ace::NG \ No newline at end of file diff --git a/test/unittest/core/pattern/waterflow/water_flow_sw_layout_test.cpp b/test/unittest/core/pattern/waterflow/water_flow_sw_layout_test.cpp index 63d7fe9aad7..41d3a1b5503 100644 --- a/test/unittest/core/pattern/waterflow/water_flow_sw_layout_test.cpp +++ b/test/unittest/core/pattern/waterflow/water_flow_sw_layout_test.cpp @@ -17,6 +17,10 @@ #include "water_flow_item_maps.h" #include "water_flow_test_ng.h" +#define private public +#include "core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_sw.h" +#undef private + #include "core/components_ng/pattern/refresh/refresh_model_ng.h" #include "core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.h" @@ -2172,4 +2176,59 @@ HWTEST_F(WaterFlowSWTest, UpdateAndJump001, TestSize.Level1) EXPECT_EQ(info_->lanes_[2][0].ToString(), "{StartPos: 500.000000 EndPos: 900.000000 Items [12 14 15 ] }"); EXPECT_EQ(info_->lanes_[2][1].ToString(), "{StartPos: 500.000000 EndPos: 700.000000 Items [13 ] }"); } + +/** + * @tc.name: WaterFlowSWReMeasureTest001 + * @tc.desc: Test WaterFlow sliding window selective clearing mechanism + * @tc.type: FUNC + */ +HWTEST_F(WaterFlowSWTest, WaterFlowSWReMeasureTest001, TestSize.Level1) +{ + /** + * @tc.steps: step1. Create WaterFlow with sliding window + * @tc.expected: WaterFlow index range is correct + */ + WaterFlowModelNG model = CreateWaterFlow(); + model.SetColumnsTemplate("1fr 1fr"); + CreateWaterFlowItems(20); + CreateDone(); + + /** + * @tc.steps: step2. Call measure of WaterFlow for first time + * @tc.expected: WaterFlow layout range is correct, layouted is false + */ + auto layoutAlgorithm = AceType::DynamicCast(pattern_->CreateLayoutAlgorithm()); + EXPECT_TRUE(layoutAlgorithm); + layoutAlgorithm->Measure(AceType::RawPtr(frameNode_)); + EXPECT_FALSE(layoutAlgorithm->isLayouted_); + + // Record initial index range + int32_t initialStartIndex = layoutAlgorithm->info_->StartIndex(); + int32_t initialEndIndex = layoutAlgorithm->info_->EndIndex(); + + /** + * @tc.steps: step3. Change WaterFlow mainSize and call measure for second time + * @tc.expected: Check selective clearing mechanism works with index-based approach + */ + LayoutConstraintF contentConstraint; + contentConstraint.selfIdealSize = OptionalSizeF(240.f, 200.f); + contentConstraint.maxSize = SizeF(240.f, 200.f); + contentConstraint.percentReference = SizeF(240.f, 200.f); + + layoutProperty_->UpdateLayoutConstraint(contentConstraint); + + // Verify that prevStartIndex_ and prevEndIndex_ are set correctly before measure + layoutAlgorithm->Measure(AceType::RawPtr(frameNode_)); + + // Verify index range has changed + int32_t newStartIndex = layoutAlgorithm->info_->StartIndex(); + int32_t newEndIndex = layoutAlgorithm->info_->EndIndex(); + + // Verify that the selective clearing mechanism uses real-time indices correctly + EXPECT_TRUE(newStartIndex != initialStartIndex || newEndIndex != initialEndIndex); + + // Complete layout to trigger ClearUnlayoutedItems + layoutAlgorithm->Layout(AceType::RawPtr(frameNode_)); + EXPECT_TRUE(layoutAlgorithm->isLayouted_); +} } // namespace OHOS::Ace::NG diff --git a/test/unittest/core/pattern/waterflow/water_flow_top_down_test.cpp b/test/unittest/core/pattern/waterflow/water_flow_top_down_test.cpp index 6d3ba072f46..4b7f2a23c97 100644 --- a/test/unittest/core/pattern/waterflow/water_flow_top_down_test.cpp +++ b/test/unittest/core/pattern/waterflow/water_flow_top_down_test.cpp @@ -655,8 +655,6 @@ HWTEST_F(WaterFlowTestNg, Cache002, TestSize.Level1) UpdateCurrentOffset(300.0f); EXPECT_EQ(info->startIndex_, 24); EXPECT_EQ(info->endIndex_, 35); - // item in cache range shouldn't be created yet - EXPECT_FALSE(GetChildFrameNode(frameNode_, 22)); PipelineContext::GetCurrentContext()->OnIdle(INT64_MAX); ASSERT_TRUE(GetChildFrameNode(frameNode_, 22)); EXPECT_FALSE(GetChildFrameNode(frameNode_, 22)->IsActive()); -- Gitee