1 /* 2 * Copyright (c) 2023 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16 #ifndef FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERNS_LIST_LIST_HEIGHT_OFFSET_CALCULATOR_H 17 #define FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERNS_LIST_LIST_HEIGHT_OFFSET_CALCULATOR_H 18 19 #include "core/components_ng/base/ui_node.h" 20 #include "core/components_ng/syntax/lazy_for_each_node.h" 21 #include "core/components_ng/syntax/repeat_virtual_scroll_node.h" 22 #include "core/components_ng/pattern/list/list_item_group_pattern.h" 23 #include "core/components_ng/pattern/list/list_item_pattern.h" 24 #include "core/components_ng/pattern/list/list_layout_algorithm.h" 25 26 namespace OHOS::Ace::NG { 27 namespace { 28 constexpr float DEFAULT_ITEM_HEIGHT = 64.f; 29 } 30 class ListHeightOffsetCalculator { 31 public: ListHeightOffsetCalculator(const ListLayoutAlgorithm::PositionMap & itemPosition,float space,int32_t lanes,Axis axis)32 ListHeightOffsetCalculator(const ListLayoutAlgorithm::PositionMap& itemPosition, float space, 33 int32_t lanes, Axis axis) : axis_(axis), spaceWidth_(space), lanes_(lanes), itemPosition_(itemPosition) 34 { 35 if (!itemPosition.empty()) { 36 targetPos_ = { itemPosition.begin()->second.startPos, itemPosition.begin()->second.endPos }; 37 startIndex_ = itemPosition.begin()->first; 38 endIndex_ = itemPosition.rbegin()->first; 39 float itemsSize = itemPosition.rbegin()->second.endPos - itemPosition.begin()->second.startPos + space; 40 estimateItemHeight_ = itemsSize / itemPosition.size() - space; 41 for (const auto& pos : itemPosition) { 42 if (pos.second.groupInfo && pos.second.groupInfo.value().averageHeight > 0) { 43 groupedItemHeight_ = pos.second.groupInfo.value().averageHeight; 44 break; 45 } 46 } 47 } 48 } 49 CalculateFrameNode(RefPtr<FrameNode> frameNode)50 void CalculateFrameNode(RefPtr<FrameNode> frameNode) 51 { 52 CHECK_NULL_VOID(frameNode); 53 auto listItemGroupPatten = frameNode->GetPattern<ListItemGroupPattern>(); 54 if (listItemGroupPatten) { 55 if (currentIndex_ > 0) { 56 estimateHeight_ += spaceWidth_; 57 } 58 if (currentIndex_ == startIndex_) { 59 estimateOffset_ = listItemGroupPatten->GetEstimateOffset(estimateHeight_, targetPos_); 60 } 61 if (currLane_ > 0) { 62 estimateHeight_ += currRowHeight_; 63 currLane_ = 0; 64 currRowHeight_ = 0.0f; 65 } 66 estimateHeight_ += listItemGroupPatten->GetEstimateHeight(groupedItemHeight_); 67 currentIndex_++; 68 return; 69 } 70 auto listItemPatten = frameNode->GetPattern<ListItemPattern>(); 71 if (currentIndex_ > 0 && currLane_ == 0) { 72 estimateHeight_ += spaceWidth_; 73 } 74 if (currentIndex_ == startIndex_) { 75 estimateOffset_ = estimateHeight_ - targetPos_.first; 76 } 77 float height = 0.0f; 78 if (listItemPatten) { 79 height = listItemPatten->GetEstimateHeight(GetAverageItemHeight(), axis_); 80 } else { 81 height = GetMainAxisSize(frameNode->GetGeometryNode()->GetMarginFrameSize(), axis_); 82 } 83 currRowHeight_ = std::max(currRowHeight_, height); 84 currLane_++; 85 if (currLane_ == lanes_) { 86 estimateHeight_ += currRowHeight_; 87 currLane_ = 0; 88 currRowHeight_ = 0.0f; 89 } 90 currentIndex_++; 91 if (listItemPatten && listItemPatten->GetLayouted()) { 92 totalItemHeight_ += height; 93 totalItemCount_++; 94 } 95 } 96 CalculateUINode(RefPtr<UINode> node)97 void CalculateUINode(RefPtr<UINode> node) 98 { 99 CHECK_NULL_VOID(node); 100 auto children = node->GetChildren(); 101 for (const auto& child : children) { 102 if (AceType::InstanceOf<FrameNode>(child)) { 103 auto frameNode = AceType::DynamicCast<FrameNode>(child); 104 CalculateFrameNode(frameNode); 105 } else if (AceType::InstanceOf<LazyForEachNode>(child) || 106 AceType::InstanceOf<RepeatVirtualScrollNode>(child)) { 107 CalculateLazyForEachNode(child); 108 } else { 109 CalculateUINode(child); 110 } 111 } 112 } 113 GetLazyForEachIndexAverageHeight(RefPtr<UINode> node,int32_t startIndex,int32_t endIndex,bool & hasGroup)114 float GetLazyForEachIndexAverageHeight(RefPtr<UINode> node, 115 int32_t startIndex, int32_t endIndex, bool &hasGroup) 116 { 117 auto itor = itemPosition_.find(startIndex); 118 float totalHeight = 0.0f; 119 int32_t itemCount = 0; 120 while (itor != itemPosition_.end() && itor->first <= endIndex) { 121 if (!itor->second.isGroup) { 122 totalHeight += itor->second.endPos - itor->second.startPos; 123 itor++; 124 itemCount++; 125 continue; 126 } 127 hasGroup = true; 128 if (itor->first == startIndex_ || itor->first == endIndex_) { 129 auto child = node->GetFrameChildByIndex(itor->first - currentIndex_, false); 130 auto frameNode = AceType::DynamicCast<FrameNode>(child); 131 if (!frameNode) { 132 itor++; 133 continue; 134 } 135 auto group = frameNode->GetPattern<ListItemGroupPattern>(); 136 if (!group || !group->HasLayoutedItem()) { 137 itor++; 138 continue; 139 } 140 totalHeight += group->GetEstimateHeight(groupedItemHeight_); 141 } else { 142 totalHeight += itor->second.endPos - itor->second.startPos; 143 } 144 itor++; 145 itemCount++; 146 } 147 if (itemCount == 0) { 148 return estimateItemHeight_; 149 } 150 return totalHeight / itemCount; 151 } 152 CalculateOffset(RefPtr<UINode> node,float averageHeight)153 float CalculateOffset(RefPtr<UINode> node, float averageHeight) 154 { 155 auto itor = itemPosition_.begin(); 156 float skipHeight = 0.0f; 157 while (itor != itemPosition_.end() && itor->second.isGroup) { 158 auto child = node->GetFrameChildByIndex(itor->first - currentIndex_, false); 159 auto frameNode = AceType::DynamicCast<FrameNode>(child); 160 if (frameNode) { 161 auto group = frameNode->GetPattern<ListItemGroupPattern>(); 162 if (group && group->HasLayoutedItem()) { 163 std::pair<float, float> pos = { itor->second.startPos, itor->second.endPos }; 164 return group->GetEstimateOffset(estimateOffset_ + skipHeight, pos); 165 } 166 } 167 skipHeight += averageHeight; 168 itor++; 169 } 170 return estimateOffset_ - targetPos_.first; 171 } 172 CalculateLazyForEachNode(RefPtr<UINode> node)173 void CalculateLazyForEachNode(RefPtr<UINode> node) 174 { 175 int32_t count = node->FrameCount(); 176 if (count <= 0) { 177 return; 178 } 179 if ((endIndex_ < currentIndex_) || (startIndex_ >= currentIndex_ + count)) { 180 estimateHeight_ += (estimateItemHeight_ + spaceWidth_) * GetLines(lanes_, count); 181 if (currentIndex_ > 0) { 182 estimateHeight_ -= spaceWidth_; 183 } 184 currentIndex_ += count; 185 return; 186 } 187 if (currentIndex_ > 0 && currLane_ == 0) { 188 estimateHeight_ += spaceWidth_; 189 } 190 bool hasGroup = false; 191 int32_t startIndex = std::max(currentIndex_, startIndex_); 192 int32_t endIndex = std::min(currentIndex_ + count - 1, endIndex_); 193 float averageHeight = GetLazyForEachIndexAverageHeight(node, startIndex, endIndex, hasGroup); 194 int32_t lanes = hasGroup ? 1 : lanes_; 195 if (startIndex == startIndex_) { 196 int32_t curr = GetLines(lanes, startIndex_ - currentIndex_); 197 estimateOffset_ = estimateHeight_ + (averageHeight + spaceWidth_) * curr; 198 estimateOffset_ = CalculateOffset(node, averageHeight); 199 } 200 estimateHeight_ += (averageHeight + spaceWidth_) * GetLines(lanes, count) - spaceWidth_; 201 currentIndex_ += count; 202 } 203 GetEstimateHeightAndOffset(RefPtr<UINode> node)204 bool GetEstimateHeightAndOffset(RefPtr<UINode> node) 205 { 206 CalculateUINode(node); 207 if (currLane_ > 0) { 208 estimateHeight_ += currRowHeight_; 209 currLane_ = 0; 210 currRowHeight_ = 0.0f; 211 } 212 return true; 213 } 214 GetEstimateHeight()215 float GetEstimateHeight() const 216 { 217 return estimateHeight_; 218 } 219 GetEstimateOffset()220 float GetEstimateOffset() const 221 { 222 return estimateOffset_; 223 } 224 225 private: GetAverageItemHeight()226 float GetAverageItemHeight() const 227 { 228 if (totalItemCount_ > 0) { 229 return totalItemHeight_ / totalItemCount_; 230 } 231 return estimateItemHeight_; 232 } 233 GetLines(int32_t lanes,int32_t count)234 static int32_t GetLines(int32_t lanes, int32_t count) 235 { 236 if (lanes > 1) { 237 int32_t lines = count / lanes; 238 if (count % lanes > 0) { 239 lines += 1; 240 } 241 return lines; 242 } else { 243 return count; 244 } 245 } 246 247 Axis axis_ = Axis::VERTICAL; 248 int32_t currentIndex_ = 0; 249 int32_t startIndex_ = 0; 250 int32_t endIndex_ = 0; 251 std::pair<float, float> targetPos_ = { 0.0f, 0.0f }; 252 float estimateHeight_ = 0.0f; 253 float estimateOffset_ = 0.0f; 254 float spaceWidth_ = 0.0f; 255 256 float totalItemHeight_ = 0.0f; 257 float totalItemCount_ = 0.0f; 258 259 float estimateItemHeight_ = DEFAULT_ITEM_HEIGHT; 260 float groupedItemHeight_ = DEFAULT_ITEM_HEIGHT; 261 262 int32_t lanes_ = 1; 263 int32_t currLane_ = 0; 264 float currRowHeight_ = 0.0f; 265 266 const ListLayoutAlgorithm::PositionMap& itemPosition_; 267 }; 268 } // namespace OHOS::Ace::NG 269 #endif 270