diff --git a/frameworks/core/components_ng/pattern/list/list_layout_algorithm.cpp b/frameworks/core/components_ng/pattern/list/list_layout_algorithm.cpp index 378e433546d277ca32b3de7ba366c742fa5b9ea5..45550926ba45b82284920c2efaa7c11f34a554d3 100644 --- a/frameworks/core/components_ng/pattern/list/list_layout_algorithm.cpp +++ b/frameworks/core/components_ng/pattern/list/list_layout_algorithm.cpp @@ -161,13 +161,13 @@ void ListLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper) listItemAlign_ = listLayoutProperty->GetListItemAlign().value_or(V2::ListItemAlign::START); // calculate child layout constraint. UpdateListItemConstraint(axis_, contentIdealSize, childLayoutConstraint_); - if (posMap_) { + if (childrenSize_ && posMap_) { posMap_->UpdatePosMap(layoutWrapper, GetLanes(), spaceWidth_, childrenSize_); } MeasureList(layoutWrapper); } else { itemPosition_.clear(); - if (posMap_) { + if (childrenSize_ && posMap_) { posMap_->ClearPosMap(); } } @@ -622,6 +622,54 @@ float ListLayoutAlgorithm::MeasureAndGetChildHeight(LayoutWrapper* layoutWrapper return mainLen; } +std::pair ListLayoutAlgorithm::FindIndexAndDeltaInPosMap(float delta) const +{ + // If cannot find or inappropriate, return index -1 and delta 0.f. + // If a suitable location can be found based on posMap_, return the corresponding index and delta. + // The delta and index in the return value satisfies the following constraints: + // 1) delta >= -spaceWidth_; + // 2) delta < posMap[index].mainSize. + if (!posMap_) { + return { -1, 0.f }; + } + // itemPosition_ has been checked nonNull before the func is called. + int32_t curIndex = itemPosition_.begin()->first; + float startPos = itemPosition_.begin()->second.startPos; + // Consume a portion of delta to align item with the top of the List. + if (Negative(delta) && LessOrEqual(startPos, delta)) { + return { curIndex, delta - startPos }; + } + delta -= startPos; + float curPos = posMap_->GetPositionInfo(curIndex).mainPos; + float curSize = posMap_->GetPositionInfo(curIndex).mainSize; + // The abs value of the input param of the func is greater than 2 * contentMainSize_, so + // the delta here must not be 0.f + int32_t step = Negative(delta) ? -1 : 1; + while (!Negative(curPos)) { // if curIndex + if (LessOrEqual(-spaceWidth_, delta) && LessNotEqual(delta, curSize)) { + return { curIndex, delta }; + } + curIndex += step; + curPos = posMap_->GetPositionInfo(curIndex).mainPos; + curSize = posMap_->GetPositionInfo(curIndex).mainSize; + float gap = curSize + spaceWidth_; + delta -= (step > 0 ? gap : -gap); + } + return { -1, 0.f }; +} + +bool ListLayoutAlgorithm::CanUseInfoInPosMap(int32_t index, float delta) const +{ + if (index < 0 || index > totalItemCount_ - 1 || !posMap_) { + return false; + } + const auto& info = posMap_->GetPositionInfo(index); + if (info.isGroup && GreatNotEqual(info.mainSize, contentMainSize_ * 2.0f)) { + return false; + } + return true; +} + void ListLayoutAlgorithm::CheckJumpToIndex() { if (jumpIndex_.has_value() || !isNeedCheckOffset_ || childrenSize_) { @@ -630,6 +678,13 @@ void ListLayoutAlgorithm::CheckJumpToIndex() if (LessOrEqual(std::abs(currentDelta_), contentMainSize_ * 2.0f) || itemPosition_.empty()) { return; } + auto [index, delta] = FindIndexAndDeltaInPosMap(currentDelta_); + if (CanUseInfoInPosMap(index, delta)) { + jumpIndex_ = index; + currentDelta_ = delta; + isNeedCheckOffset_ = false; + return; + } for (const auto& pos : itemPosition_) { if (pos.second.isGroup) { return; @@ -2432,7 +2487,7 @@ std::pair ListLayoutAlgorithm::GetSnapEndIndexAndPos() break; } } - return std::make_pair(std::min(endIndex, totalItemCount_ -1), endPos); + return std::make_pair(std::min(endIndex, totalItemCount_ - 1), endPos); } int32_t ListLayoutAlgorithm::UpdateDefaultCachedCount(const int32_t oldCacheCount, const int32_t itemCount) diff --git a/frameworks/core/components_ng/pattern/list/list_layout_algorithm.h b/frameworks/core/components_ng/pattern/list/list_layout_algorithm.h index 0d9f70c114408e0f156a64b085befbc024c8bd16..8242b5816f8cc82305e2987c5c716881430f3818 100644 --- a/frameworks/core/components_ng/pattern/list/list_layout_algorithm.h +++ b/frameworks/core/components_ng/pattern/list/list_layout_algorithm.h @@ -524,6 +524,8 @@ private: std::optional GetListItemGroupLayoutInfo( const RefPtr& wrapper) const; int32_t GetListItemGroupItemCount(const RefPtr& wrapper) const; + std::pair FindIndexAndDeltaInPosMap(float delta) const; + bool CanUseInfoInPosMap(int32_t index, float delta) const; std::optional jumpIndex_; std::optional jumpIndexInGroup_; diff --git a/frameworks/core/components_ng/pattern/list/list_pattern.cpp b/frameworks/core/components_ng/pattern/list/list_pattern.cpp index 04b72993b95cf585e948e5b36f61e46015c235fc..bf446212346d33bdce23ef8fa7263fd4886f9bff 100644 --- a/frameworks/core/components_ng/pattern/list/list_pattern.cpp +++ b/frameworks/core/components_ng/pattern/list/list_pattern.cpp @@ -565,9 +565,9 @@ RefPtr ListPattern::CreateLayoutAlgorithm() if (!posMap_) { posMap_ = MakeRefPtr(); } + listLayoutAlgorithm->SetListPositionMap(posMap_); if (childrenSize_) { listLayoutAlgorithm->SetListChildrenMainSize(childrenSize_); - listLayoutAlgorithm->SetListPositionMap(posMap_); } bool needUseInitialIndex = Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_FOURTEEN) ? !isInitialized_ && !jumpIndex_ : !isInitialized_; @@ -1841,6 +1841,21 @@ Rect ListPattern::GetItemRectInGroup(int32_t index, int32_t indexInGroup) const groupItemGeometry->GetFrameRect().Width(), groupItemGeometry->GetFrameRect().Height()); } +bool ListPattern::CalculateJumpOffset() +{ + for (const auto& pos : itemPosition_) { + if (pos.second.groupInfo && !pos.second.groupInfo.value().atStart) { + continue; + } + ListPositionInfo info = posMap_->GetPositionInfo(pos.first); + if (!Negative(info.mainSize)) { + currentOffset_ = info.mainPos - pos.second.startPos; + return true; + } + } + return false; +} + float ListPattern::UpdateTotalOffset(const RefPtr& listLayoutAlgorithm, bool isJump) { float relativeOffset = listLayoutAlgorithm->GetCurrentOffset(); @@ -1850,10 +1865,18 @@ float ListPattern::UpdateTotalOffset(const RefPtr& listLayo currentOffset_ = itemPosition_.empty() ? 0.0f : posMap_->GetPos(itemPosition_.begin()->first, itemPosition_.begin()->second.startPos); } else { - if (isJump || needReEstimateOffset_) { + if (isJump && !needReEstimateOffset_) { + if (CalculateJumpOffset()) { + relativeOffset = 0.0f; + } else { + needReEstimateOffset_ = true; + } + } + if (needReEstimateOffset_) { auto calculate = ListHeightOffsetCalculator(itemPosition_, spaceWidth_, lanes_, GetAxis()); calculate.GetEstimateHeightAndOffset(GetHost()); currentOffset_ = calculate.GetEstimateOffset(); + currentOffset_ = calculate.GetEstimateHeight(); relativeOffset = 0; needReEstimateOffset_ = false; posMap_->ClearPosMap(); diff --git a/frameworks/core/components_ng/pattern/list/list_position_map.h b/frameworks/core/components_ng/pattern/list/list_position_map.h index 89585b2bc35f07db85ce1cd589e5094a918122b7..7a6684f4a2291a614426f8750f9748c508e61b78 100644 --- a/frameworks/core/components_ng/pattern/list/list_position_map.h +++ b/frameworks/core/components_ng/pattern/list/list_position_map.h @@ -42,6 +42,7 @@ namespace { struct PositionInfo { float mainPos; float mainSize; + bool isGroup; }; enum class ListPosMapUpdate { diff --git a/test/unittest/core/pattern/list/list_group_algorithm_test_ng.cpp b/test/unittest/core/pattern/list/list_group_algorithm_test_ng.cpp index b3606f99dfbb82c61ec45bd88b5cc76bb2d91c13..fec8716b4c25138ca12c65813a8687714a21d76e 100644 --- a/test/unittest/core/pattern/list/list_group_algorithm_test_ng.cpp +++ b/test/unittest/core/pattern/list/list_group_algorithm_test_ng.cpp @@ -27,6 +27,10 @@ public: int32_t groupNumber, V2::ListItemGroupStyle listItemGroupStyle, int32_t itemNumber = GROUP_ITEM_NUMBER); void CreateGroupWithFooter( int32_t groupNumber, V2::ListItemGroupStyle listItemGroupStyle, int32_t itemNumber = GROUP_ITEM_NUMBER); + void CreateGroupOnlySmallItem( + int32_t groupNumber, V2::ListItemGroupStyle listItemGroupStyle, int32_t itemNumber = GROUP_ITEM_NUMBER); + void CreateGroupOnlyBigItem( + int32_t groupNumber, V2::ListItemGroupStyle listItemGroupStyle, int32_t itemNumber = GROUP_ITEM_NUMBER); }; void ListGroupAlgTestNg::CreateGroupWithHeader( @@ -59,6 +63,77 @@ void ListGroupAlgTestNg::CreateGroupWithFooter( } } +void ListGroupAlgTestNg::CreateGroupOnlySmallItem( + int32_t groupNumber, V2::ListItemGroupStyle listItemGroupStyle, int32_t itemNumber) +{ + for (int32_t index = 0; index < groupNumber; index++) { + ListItemGroupModelNG groupModel = CreateListItemGroup(listItemGroupStyle); + groupModel.SetSpace(Dimension(SPACE)); + CreateListItems(itemNumber, static_cast(listItemGroupStyle)); + ViewStackProcessor::GetInstance()->Pop(); + ViewStackProcessor::GetInstance()->StopGetAccessRecording(); + } +} + +void ListGroupAlgTestNg::CreateGroupOnlyBigItem( + int32_t groupNumber, V2::ListItemGroupStyle listItemGroupStyle, int32_t itemNumber) +{ + for (int32_t index = 0; index < groupNumber; index++) { + ListItemGroupModelNG groupModel = CreateListItemGroup(listItemGroupStyle); + // 5: Increase the average height of elements by enlarging the space. + groupModel.SetSpace(Dimension(SPACE * 5)); + CreateListItems(itemNumber, static_cast(listItemGroupStyle)); + ViewStackProcessor::GetInstance()->Pop(); + ViewStackProcessor::GetInstance()->StopGetAccessRecording(); + } +} + +/** + * @tc.name: BigJumpAccuracyTest001 + * @tc.desc: jump with big offset and check position + * @tc.type: FUNC + */ + +HWTEST_F(ListGroupAlgTestNg, BigJumpAccuracyTest001, TestSize.Level1) +{ + /** + * @tc.steps: step1. Create ListItemGroup with big/small average height + * @tc.expected: big/small ListItemGroup height is 760/700 + */ + auto model = CreateList(); + model.SetInitialIndex(0); + CreateGroupOnlySmallItem(10, V2::ListItemGroupStyle::NONE, 7); + CreateGroupOnlyBigItem(10, V2::ListItemGroupStyle::NONE, 5); + CreateDone(); + EXPECT_EQ(pattern_->currentOffset_, 0.f); + + /** + * @tc.steps: step2. Slide to bottom + * @tc.expected: pos is 760 * 10 + 700 * 10 - 400 = 14200 + */ + UpdateCurrentOffset(-14200.f, SCROLL_FROM_UPDATE); + EXPECT_EQ(pattern_->currentOffset_, 14200.f); + + /** + * @tc.steps: step3. Simulate LazyForEach. reset ListItemGroup layoutInfo. + */ + for (auto i = 0; i < 10; i++) { + auto groupNode = AceType::DynamicCast(frameNode_->GetChildAtIndex(i)); + auto groupPattern = groupNode->GetPattern(); + groupPattern->ResetLayoutedInfo(); + groupPattern->cachedItemPosition_.clear(); + groupPattern->mainSize_ = 0.f; + groupNode->GetGeometryNode()->Reset(); + } + + /** + * @tc.steps: step4. backToTop. Simulate big offset callback. + * @tc.expected: after scroll, pos is 14200 - 10000 = 4200 + */ + UpdateCurrentOffset(10000.f, SCROLL_FROM_STATUSBAR); + EXPECT_EQ(pattern_->currentOffset_, 4200.f); +} + /** * @tc.name: ListItemGroupLayoutAlgorithm001 * @tc.desc: ListItemGroup Measure