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