1 /*
2 * Copyright (c) 2021-2022 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 #include "core/components/refresh/render_refresh.h"
17
18 #include <chrono>
19 #include <ctime>
20
21 #include "base/i18n/localization.h"
22 #include "base/utils/string_utils.h"
23 #include "base/utils/utils.h"
24 #include "core/animation/curve_animation.h"
25 #include "core/event/ace_event_helper.h"
26
27 namespace OHOS::Ace {
28 namespace {
29
30 constexpr int32_t ANIMATION_DURATION = 350; // Default animation duration
31 constexpr int32_t MAX_ALPHA = 255;
32 constexpr int32_t BASE_YEAR = 1900;
33 constexpr double DEFAULT_TIME_BOX_BOTTOM_SIZE = 8.0; // Default box height for time
34 constexpr double PERCENT = 0.01; // Percent
35 constexpr double HALF = 0.5;
36 const char REFRESH_LAST_UPDATED[] = "refresh.last_updated"; // I18n for last updated
37 const char LAST_UPDATE_FORMAT[] = "yyyy/M/d HH:mm"; // Date format for last updated
38 const char PULL_DOWN_START[] = "start"; // Pull-down event, state is start
39 const char PULL_DOWN_END[] = "end"; // Pull-down event, state is end
40
41 } // namespace
42
RenderRefresh()43 RenderRefresh::RenderRefresh()
44 {
45 loadingComponent_ = AceType::MakeRefPtr<LoadingProgressComponent>();
46 loading_ = AceType::DynamicCast<RenderLoadingProgress>(loadingComponent_->CreateRenderNode());
47 loading_->SetLoadingMode(MODE_DRAG);
48
49 loadingBackgroundBoxComponent_ = AceType::MakeRefPtr<BoxComponent>();
50 loadingBackgroundBoxComponent_->SetEnableDebugBoundary(true);
51
52 decoration_ = AceType::MakeRefPtr<Decoration>();
53 loadingBackgroundBox_ = AceType::DynamicCast<RenderBox>(loadingBackgroundBoxComponent_->CreateRenderNode());
54
55 loadingBoxComponent_ = AceType::MakeRefPtr<BoxComponent>();
56 loadingBoxComponent_->SetFlex(BoxFlex::FLEX_X);
57
58 loadingBox_ = AceType::DynamicCast<RenderBox>(loadingBoxComponent_->CreateRenderNode());
59
60 loadingBackgroundBox_->AddChild(loading_);
61 loadingBox_->AddChild(loadingBackgroundBox_);
62
63 timeBoxComponent_ = AceType::MakeRefPtr<BoxComponent>();
64 timeBoxComponent_->SetFlex(BoxFlex::FLEX_X);
65 timeBox_ = AceType::DynamicCast<RenderBox>(timeBoxComponent_->CreateRenderNode());
66
67 lastTimeText_ = Localization::GetInstance()->GetEntryLetters(REFRESH_LAST_UPDATED);
68 timeText_ = StringUtils::FormatString(lastTimeText_.c_str(), "");
69 timeComponent_ = AceType::MakeRefPtr<TextComponent>(timeText_);
70
71 displayComponent_ = AceType::MakeRefPtr<DisplayComponent>();
72
73 display_ = AceType::DynamicCast<RenderDisplay>(displayComponent_->CreateRenderNode());
74 time_ = AceType::DynamicCast<RenderText>(timeComponent_->CreateRenderNode());
75 display_->AddChild(timeBox_);
76 timeBox_->AddChild(time_);
77 }
78
Update(const RefPtr<Component> & component)79 void RenderRefresh::Update(const RefPtr<Component>& component)
80 {
81 const RefPtr<RefreshComponent> refresh = AceType::DynamicCast<RefreshComponent>(component);
82 if (!refresh) {
83 LOGW("RefreshComponent is null");
84 return;
85 }
86 if (refresh->GetOnStateChange()) {
87 onStateChange_ = *refresh->GetOnStateChange();
88 }
89 if (refresh->GetOnRefreshing()) {
90 onRefreshing_ = *refresh->GetOnRefreshing();
91 }
92
93 refreshComponent_ = AceType::WeakClaim(AceType::RawPtr(component));
94 refreshing_ = refresh->IsRefreshing();
95 showLastTime_ = refresh->IsShowLastTime();
96 isUseOffset_ = refresh->IsUseOffset();
97 refreshType_ = refresh->GetRefreshType();
98 progressColor_ = refresh->GetProgressColor();
99 backgroundColor_ = refresh->GetBackgroundColor();
100 frictionRatio_ = refresh->GetFriction() * PERCENT;
101 isRefresh_ = refresh->GetIsRefresh();
102 if (!refresh->GetChangeEvent().IsEmpty()) {
103 changeEvent_ =
104 AceAsyncEvent<void(const std::string&)>::Create(refresh->GetChangeEvent(), context_);
105 }
106
107 loadingComponent_->SetProgressColor(progressColor_);
108 loadingComponent_->SetDiameter(Dimension(GetLoadingDiameter()));
109
110 // Set the progress background color
111 decoration_->SetBackgroundColor(backgroundColor_);
112
113 if (refresh->GetTextDirection() == TextDirection::LTR) {
114 SetTextDirection(TextDirection::LTR);
115 time_->SetTextDirection(TextDirection::LTR);
116 } else {
117 SetTextDirection(TextDirection::RTL);
118 time_->SetTextDirection(TextDirection::RTL);
119 }
120
121 if (!isInitialized_) {
122 loading_->Attach(GetContext());
123 loadingBackgroundBox_->Attach(GetContext());
124 loadingBox_->Attach(GetContext());
125 AddChild(loadingBox_);
126
127 timeBox_->Attach(GetContext());
128 display_->Attach(GetContext());
129 time_->Attach(GetContext());
130 timeText_ = StringUtils::FormatString(lastTimeText_.c_str(), GetFormatDateTime().c_str());
131 timeComponent_->SetData(timeText_);
132 timeComponent_->SetTextStyle(refresh->GetTextStyle());
133 time_->Update(timeComponent_);
134 display_->Update(displayComponent_);
135 timeBox_->Update(timeBoxComponent_);
136 AddChild(display_);
137
138 // Just run on the first time
139 isInitialized_ = true;
140 }
141
142 loading_->Update(loadingComponent_);
143 loadingBackgroundBox_->Update(loadingBackgroundBoxComponent_);
144 loadingBox_->Update(loadingBoxComponent_);
145
146 refreshEvent_ = AceAsyncEvent<void(const std::string&)>::Create(refresh->GetRefreshEventId(), context_);
147 pullDownEvent_ = AceAsyncEvent<void(const std::string&)>::Create(refresh->GetPulldownEventId(), context_);
148 CalcLoadingParams(component);
149 Initialize();
150 MarkNeedLayout();
151 }
152
CalcLoadingParams(const RefPtr<Component> & component)153 void RenderRefresh::CalcLoadingParams(const RefPtr<Component>& component)
154 {
155 auto refresh = AceType::DynamicCast<RefreshComponent>(component);
156 if (refresh == nullptr) {
157 LOGW("RefreshComponent is null");
158 return;
159 }
160 auto context = context_.Upgrade();
161 if (context == nullptr) {
162 LOGW("context is nullptr!");
163 return;
164 }
165 scale_ = context->GetDipScale();
166 triggerLoadingDistance_ = NormalizeToPx(refresh->GetLoadingDistance());
167 triggerShowTimeDistance_ = NormalizeToPx(refresh->GetShowTimeDistance());
168 if (showLastTime_) {
169 timeDistance_ = NormalizeToPx(Dimension(DEFAULT_TIME_BOX_BOTTOM_SIZE, DimensionUnit::VP));
170 triggerRefreshDistance_ = triggerShowTimeDistance_;
171 } else {
172 triggerRefreshDistance_ = NormalizeToPx(refresh->GetRefreshDistance());
173 inspectorOffset_ = refresh->GetRefreshDistance();
174 }
175 loadingDiameter_ = NormalizeToPx(refresh->GetProgressDiameter());
176 maxScrollOffset_ = NormalizeToPx(refresh->GetMaxDistance());
177 indicatorOffset_ = NormalizeToPx(refresh->GetIndicatorOffset());
178 timeOffset_ = NormalizeToPx(refresh->GetTimeOffset());
179 loading_->SetDiameter(refresh->GetProgressDiameter());
180 loading_->SetDragRange(triggerLoadingDistance_, triggerRefreshDistance_);
181 loadingBox_->SetHeight(loadingDiameter_);
182 decoration_->SetBorderRadius(Radius(loadingDiameter_ * HALF));
183 loadingBackgroundBox_->SetBackDecoration(decoration_);
184 loadingBackgroundBox_->SetWidth(loadingDiameter_);
185 }
186
Initialize()187 void RenderRefresh::Initialize()
188 {
189 LOGI("RenderRefresh Initialize state:%{public}d", refreshing_);
190 if (!dragDetector_) {
191 dragDetector_ = AceType::MakeRefPtr<VerticalDragRecognizer>();
192 dragDetector_->SetOnDragUpdate([weakFresh = AceType::WeakClaim(this)](const DragUpdateInfo& info) {
193 auto refresh = weakFresh.Upgrade();
194 if (refresh) {
195 refresh->HandleDragUpdate(info.GetMainDelta());
196 }
197 });
198 dragDetector_->SetOnDragEnd([weakFresh = AceType::WeakClaim(this)](const DragEndInfo& info) {
199 auto refresh = weakFresh.Upgrade();
200 if (refresh) {
201 refresh->HandleDragEnd();
202 }
203 });
204
205 dragDetector_->SetOnDragCancel([weakFresh = AceType::WeakClaim(this)]() {
206 auto refresh = weakFresh.Upgrade();
207 if (refresh) {
208 refresh->HandleDragCancel();
209 }
210 });
211 }
212 if (!animator_) {
213 animator_ = AceType::MakeRefPtr<Animator>(GetContext());
214 }
215 if (!refreshController_) {
216 refreshController_ = AceType::MakeRefPtr<RefreshController>();
217 refreshController_->SetRefresh(AceType::WeakClaim(this));
218 }
219 InitAccessibilityEventListener();
220 }
221
InitAccessibilityEventListener()222 void RenderRefresh::InitAccessibilityEventListener()
223 {
224 auto refNode = accessibilityNode_.Upgrade();
225 if (!refNode) {
226 return;
227 }
228 refNode->AddSupportAction(AceAction::ACTION_SCROLL_FORWARD);
229 auto weakPtr = AceType::WeakClaim(this);
230 refNode->SetActionScrollForward([weakPtr]() {
231 auto refresh = weakPtr.Upgrade();
232 if (refresh) {
233 refresh->SetRefreshStatus(true);
234 refresh->MarkNeedLayout();
235 }
236 return true;
237 });
238 }
239
UpdateTouchRect()240 void RenderRefresh::UpdateTouchRect()
241 {
242 touchRect_.SetSize(viewPort_);
243 touchRect_.SetOffset(GetPosition());
244 touchRectList_.emplace_back(touchRect_);
245 SetTouchRectList(touchRectList_);
246 }
247
HandleDragUpdate(double delta)248 void RenderRefresh::HandleDragUpdate(double delta)
249 {
250 if (isRefresh_) {
251 return;
252 }
253 LOGD("RenderRefresh HandleDragUpdate delta is %{public}lf, offset is %{public}lf", delta, scrollableOffset_.GetY());
254 if (NearZero(delta)) {
255 LOGD("Delta is near zero!");
256 return;
257 }
258 if (refreshStatus_ == RefreshStatus::REFRESH && delta > 0.0) {
259 LOGD("The refresh status is refreshing!");
260 return;
261 }
262 Offset deltaOffset(0, delta);
263 if (refreshStatus_ == RefreshStatus::DRAG || refreshStatus_ == RefreshStatus::OVER_DRAG ||
264 refreshStatus_ == RefreshStatus::DONE) {
265 deltaOffset.SetY(GetOffset(delta));
266 }
267 scrollableOffset_ += deltaOffset;
268 scrollableOffset_.SetY(std::clamp(scrollableOffset_.GetY(), 0.0, maxScrollOffset_));
269 MarkNeedLayout();
270 }
271
HandleDragEnd()272 void RenderRefresh::HandleDragEnd()
273 {
274 LOGD("RenderRefresh HandleDragEnd");
275 if (NearEqual(scrollableOffset_.GetY(), 0.0f)) {
276 ResetStatus();
277 return;
278 }
279 if (refreshStatus_ == RefreshStatus::INACTIVE) {
280 return;
281 }
282 if (refreshStatus_ == RefreshStatus::DRAG || refreshStatus_ == RefreshStatus::DONE) {
283 double start = scrollableOffset_.GetY();
284 double end = 0.0;
285 StartAnimation(start, end, false);
286 return;
287 }
288 if (refreshStatus_ == RefreshStatus::OVER_DRAG) {
289 double start = scrollableOffset_.GetY();
290 double end = triggerRefreshDistance_;
291 loading_->SetLoadingMode(MODE_LOOP);
292 StartAnimation(start, end, false);
293 }
294 }
295
HandleDragCancel()296 void RenderRefresh::HandleDragCancel()
297 {
298 LOGD("RenderRefresh HandleDragCancel");
299 ResetStatus();
300 }
301
ResetStatus()302 void RenderRefresh::ResetStatus()
303 {
304 refreshing_ = false;
305 if (changeEvent_) {
306 changeEvent_("false");
307 }
308 refreshStatus_ = RefreshStatus::INACTIVE;
309 scrollableOffset_.Reset();
310 loading_->SetLoadingMode(MODE_DRAG);
311 loading_->SetDragDistance(scrollableOffset_.GetY());
312 MarkNeedLayout();
313 }
314
UpdateScrollOffset(double value)315 void RenderRefresh::UpdateScrollOffset(double value)
316 {
317 scrollableOffset_.SetY(value);
318 MarkNeedLayout();
319 }
320
FireRefreshEvent() const321 void RenderRefresh::FireRefreshEvent() const
322 {
323 if (refreshEvent_) {
324 LOGI("RefreshEvent, refreshing = %{public}d.", refreshing_);
325 std::string param =
326 std::string(R"("refresh",{"refreshing":)").append(refreshing_ ? "true" : "false").append("},null");
327 refreshEvent_(param);
328 }
329 if (onRefreshing_) {
330 onRefreshing_();
331 }
332 }
333
FirePullDownEvent(const std::string & state) const334 void RenderRefresh::FirePullDownEvent(const std::string& state) const
335 {
336 if (pullDownEvent_) {
337 LOGI("PullDown Event, state is %{public}s", state.c_str());
338 std::string param = std::string(R"("pulldown",{"state":")").append(state).append("\"},null");
339 pullDownEvent_(param);
340 }
341 }
342
StartAnimation(double start,double end,bool isFinished)343 void RenderRefresh::StartAnimation(double start, double end, bool isFinished)
344 {
345 animator_->ClearInterpolators();
346 animator_->ClearStopListeners();
347 translate_ = AceType::MakeRefPtr<CurveAnimation<double>>(start, end, Curves::FRICTION);
348 auto weak = AceType::WeakClaim(this);
349 translate_->AddListener(Animation<double>::ValueCallback([weak](double value) {
350 auto scroll = weak.Upgrade();
351 if (scroll) {
352 scroll->UpdateScrollOffset(value);
353 }
354 }));
355 animator_->SetDuration(ANIMATION_DURATION);
356 animator_->AddInterpolator(translate_);
357 animator_->AddStopListener([weak, isFinished]() {
358 auto refresh = weak.Upgrade();
359 if (refresh) {
360 refresh->HandleStopListener(isFinished);
361 }
362 });
363
364 animator_->Play();
365 }
366
HandleStopListener(const bool isFinished)367 void RenderRefresh::HandleStopListener(const bool isFinished)
368 {
369 // Update the last loading time
370 if (isFinished) {
371 timeComponent_->SetData(timeText_);
372 time_->Update(timeComponent_);
373 return;
374 }
375
376 if (NearEqual(scrollableOffset_.GetY(), triggerRefreshDistance_)) {
377 if (refreshing_) {
378 loading_->SetLoadingMode(MODE_LOOP);
379 }
380 refreshing_ = true;
381 if (changeEvent_) {
382 changeEvent_("true");
383 }
384 FireRefreshEvent();
385 } else if (NearEqual(scrollableOffset_.GetY(), 0.0f)) {
386 ResetStatus();
387 } else {
388 loading_->SetLoadingMode(MODE_DRAG);
389 }
390 }
391
GetNextStatus()392 RefreshStatus RenderRefresh::GetNextStatus()
393 {
394 RefreshStatus nextStatus;
395 auto context = context_.Upgrade();
396 switch (refreshStatus_) {
397 case RefreshStatus::INACTIVE:
398 if (refreshing_) {
399 StartAnimation(0.0, triggerRefreshDistance_, false);
400 nextStatus = RefreshStatus::REFRESH;
401 break;
402 }
403 if (LessOrEqual(scrollableOffset_.GetY(), 0.0)) {
404 nextStatus = RefreshStatus::INACTIVE;
405 break;
406 }
407 // No break here, continue next case
408 FirePullDownEvent(PULL_DOWN_START);
409 [[fallthrough]];
410 case RefreshStatus::DRAG:
411 if (LessOrEqual(scrollableOffset_.GetY(), 0.0)) {
412 FirePullDownEvent(PULL_DOWN_END);
413 nextStatus = RefreshStatus::INACTIVE;
414 } else if (scrollableOffset_.GetY() < triggerRefreshDistance_) {
415 nextStatus = RefreshStatus::DRAG;
416 } else {
417 nextStatus = RefreshStatus::OVER_DRAG;
418 }
419 break;
420 case RefreshStatus::OVER_DRAG:
421 if (!refreshEvent_ && !context->GetIsDeclarative()) {
422 nextStatus = RefreshStatus::DONE;
423 break;
424 }
425 if (scrollableOffset_.GetY() > triggerRefreshDistance_) {
426 nextStatus = RefreshStatus::OVER_DRAG;
427 break;
428 }
429 // No break here, continue get next status.
430 [[fallthrough]];
431 case RefreshStatus::REFRESH:
432 if (!refreshing_) {
433 timeText_ = StringUtils::FormatString(lastTimeText_.c_str(), GetFormatDateTime().c_str());
434 StartAnimation(scrollableOffset_.GetY(), 0.0, true);
435 nextStatus = RefreshStatus::DONE;
436 break;
437 }
438 nextStatus = RefreshStatus::REFRESH;
439 break;
440 case RefreshStatus::DONE:
441 if (scrollableOffset_.GetY() > 0.0) {
442 nextStatus = RefreshStatus::DONE;
443 } else {
444 FirePullDownEvent(PULL_DOWN_END);
445 nextStatus = RefreshStatus::INACTIVE;
446 }
447 break;
448 default:
449 nextStatus = RefreshStatus::INACTIVE;
450 break;
451 }
452 if (onStateChange_ && (refreshStatus_ != nextStatus)) {
453 onStateChange_(static_cast<int>(nextStatus));
454 }
455 return nextStatus;
456 }
457
GetFriction(double percentage) const458 double RenderRefresh::GetFriction(double percentage) const
459 {
460 if (NearEqual(percentage, 1.0)) {
461 return 0.0;
462 } else {
463 return frictionRatio_ * std::pow(1.0 - percentage, SQUARE);
464 }
465 }
466
GetOffset(double delta) const467 double RenderRefresh::GetOffset(double delta) const
468 {
469 double height = GetLayoutSize().Height();
470 if (!NearZero(height)) {
471 double friction = GetFriction(std::abs(scrollableOffset_.GetY() / height));
472 return friction * delta;
473 }
474 return delta;
475 }
476
MaxScrollableHeight() const477 double RenderRefresh::MaxScrollableHeight() const
478 {
479 return GetLayoutParam().GetMaxSize().Height();
480 }
481
OnTouchTestHit(const Offset & coordinateOffset,const TouchRestrict & touchRestrict,TouchTestResult & result)482 void RenderRefresh::OnTouchTestHit(
483 const Offset& coordinateOffset, const TouchRestrict& touchRestrict, TouchTestResult& result)
484 {
485 if (!dragDetector_ || hasScrollableChild_) {
486 return;
487 }
488 dragDetector_->SetCoordinateOffset(coordinateOffset);
489 result.emplace_back(dragDetector_);
490 }
491
GetLoadingDiameter() const492 double RenderRefresh::GetLoadingDiameter() const
493 {
494 double diameter = 0.0;
495 if (scrollableOffset_.GetY() < triggerLoadingDistance_) {
496 return diameter;
497 } else if (scrollableOffset_.GetY() < triggerRefreshDistance_) {
498 double maxDistance = triggerRefreshDistance_ - triggerLoadingDistance_;
499 double actualDistance = scrollableOffset_.GetY() - triggerLoadingDistance_;
500
501 // Get the diameter by actual distance
502 diameter = ((actualDistance * loadingDiameter_ * HALF) / maxDistance) + loadingDiameter_ * HALF;
503 } else {
504 diameter = loadingDiameter_;
505 }
506 return diameter;
507 }
508
GetLoadingOffset() const509 Offset RenderRefresh::GetLoadingOffset() const
510 {
511 auto pipelineContext = GetContext().Upgrade();
512 if (!pipelineContext) {
513 LOGE("pipelineContext update with nullptr");
514 return Offset::Zero();
515 }
516
517 if (scrollableOffset_.GetY() < triggerLoadingDistance_) {
518 return Offset::Zero();
519 }
520 if (!isUseOffset_) {
521 return scrollableOffset_ * HALF - Offset(0.0, GetLoadingDiameter() * HALF);
522 }
523 double factor =
524 (scrollableOffset_.GetY() - triggerLoadingDistance_) / (triggerRefreshDistance_ - triggerLoadingDistance_);
525 return Offset(0.0, indicatorOffset_ * factor);
526 }
527
GetShowTimeOffset() const528 Offset RenderRefresh::GetShowTimeOffset() const
529 {
530 auto pipelineContext = GetContext().Upgrade();
531 if (!pipelineContext) {
532 LOGE("pipelineContext update with nullptr");
533 return Offset::Zero();
534 }
535
536 double bottomOffset = timeBox_->GetLayoutSize().Height() +
537 pipelineContext->NormalizeToPx(Dimension(DEFAULT_TIME_BOX_BOTTOM_SIZE, DimensionUnit::VP));
538 return scrollableOffset_ - Offset(0.0, bottomOffset + timeOffset_);
539 }
540
GetOpacity() const541 double RenderRefresh::GetOpacity() const
542 {
543 double factor = 0.0;
544 if (scrollableOffset_.GetY() < triggerRefreshDistance_ - timeDistance_) {
545 return factor;
546 } else if (scrollableOffset_.GetY() < triggerRefreshDistance_) {
547 double actualDistance = scrollableOffset_.GetY() - triggerRefreshDistance_ + timeDistance_;
548
549 // Get the factor, timeDistance_ never be zero
550 if (!NearZero(timeDistance_)) {
551 factor = actualDistance / timeDistance_;
552 }
553 } else {
554 factor = 1.0;
555 }
556 return factor;
557 }
558
GetFormatDateTime()559 std::string RenderRefresh::GetFormatDateTime()
560 {
561 auto now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
562 auto local = std::localtime(&now);
563 if (local == nullptr) {
564 LOGE("Get localtime failed.");
565 return "";
566 }
567
568 // This is for i18n date time
569 DateTime dateTime;
570 dateTime.year = static_cast<uint32_t>(local->tm_year + BASE_YEAR);
571 dateTime.month = static_cast<uint32_t>(local->tm_mon);
572 dateTime.day = static_cast<uint32_t>(local->tm_mday);
573 dateTime.hour = static_cast<uint32_t>(local->tm_hour);
574 dateTime.minute = static_cast<uint32_t>(local->tm_min);
575 std::string time = Localization::GetInstance()->FormatDateTime(dateTime, LAST_UPDATE_FORMAT);
576 LOGD("Last update refresh time is %{public}s", time.c_str());
577 return time;
578 }
579
UpdateScrollableOffset(double delta)580 void RenderRefresh::UpdateScrollableOffset(double delta)
581 {
582 LOGD("Update offset is %{public}lf", delta);
583 if (NearZero(delta)) {
584 LOGW("Delta is near zero!");
585 return;
586 }
587 if (refreshStatus_ == RefreshStatus::REFRESH) {
588 LOGW("The refresh status is refreshing!");
589 return;
590 }
591 Offset offset = Offset(0.0, GetOffset(delta));
592 scrollableOffset_ = scrollableOffset_ + offset;
593 scrollableOffset_.SetY(std::min(scrollableOffset_.GetY(), maxScrollOffset_));
594 MarkNeedLayout();
595 }
596
OnHiddenChanged(bool hidden)597 void RenderRefresh::OnHiddenChanged(bool hidden)
598 {
599 if (hidden) {
600 return;
601 }
602 ResetStatus();
603 }
604
PerformLayout()605 void RenderRefresh::PerformLayout()
606 {
607 const auto& children = GetChildren();
608 if (children.empty()) {
609 LOGW("Refresh has no child!");
610 return;
611 }
612 auto context = context_.Upgrade();
613 if (context == nullptr) {
614 LOGW("context is nullptr!");
615 return;
616 }
617
618 RefreshStatus nextState = GetNextStatus();
619 if (nextState != RefreshStatus::REFRESH && refreshStatus_ == RefreshStatus::REFRESH) {
620 loading_->SetLoadingMode(MODE_EXIT);
621 }
622 refreshStatus_ = nextState;
623 LayoutParam innerLayout = GetLayoutParam();
624 innerLayout.SetMinSize(Size(0.0, 0.0));
625 if (!NearEqual(scale_, context->GetDipScale())) {
626 // Notify loading to updated when window size changed.
627 CalcLoadingParams(refreshComponent_.Upgrade());
628 loading_->Layout(innerLayout);
629 }
630
631 loading_->SetDragDistance(scrollableOffset_.GetY());
632 loadingBox_->SetPosition(GetLoadingOffset());
633
634 display_->UpdateOpacity(GetOpacity() * MAX_ALPHA);
635 display_->SetPosition(GetShowTimeOffset());
636 loadingBox_->SetHidden(scrollableOffset_.GetY() < triggerLoadingDistance_);
637 loadingBox_->SetVisible(scrollableOffset_.GetY() > triggerLoadingDistance_);
638
639 columnChild_ = children.back();
640 columnChild_->Layout(innerLayout);
641 if (refreshType_ == RefreshType::PULL_DOWN && GreatNotEqual(scrollableOffset_.GetY(), 0.0)) {
642 columnChild_->SetPosition(scrollableOffset_);
643 } else {
644 columnChild_->SetPosition(Offset::Zero());
645 }
646
647 display_->Layout(innerLayout);
648 loadingBox_->Layout(innerLayout);
649 timeBox_->Layout(innerLayout);
650 SetLayoutSize(GetLayoutParam().GetMaxSize());
651 }
652
653 } // namespace OHOS::Ace
654