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_ = CREATE_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 if (NearZero(delta)) {
254 return;
255 }
256 if (refreshStatus_ == RefreshStatus::REFRESH && delta > 0.0) {
257 return;
258 }
259 Offset deltaOffset(0, delta);
260 if (refreshStatus_ == RefreshStatus::DRAG || refreshStatus_ == RefreshStatus::OVER_DRAG ||
261 refreshStatus_ == RefreshStatus::DONE) {
262 deltaOffset.SetY(GetOffset(delta));
263 }
264 scrollableOffset_ += deltaOffset;
265 scrollableOffset_.SetY(std::clamp(scrollableOffset_.GetY(), 0.0, maxScrollOffset_));
266 MarkNeedLayout();
267 }
268
HandleDragEnd()269 void RenderRefresh::HandleDragEnd()
270 {
271 if (NearEqual(scrollableOffset_.GetY(), 0.0f)) {
272 ResetStatus();
273 return;
274 }
275 if (refreshStatus_ == RefreshStatus::INACTIVE) {
276 return;
277 }
278 if (refreshStatus_ == RefreshStatus::DRAG || refreshStatus_ == RefreshStatus::DONE) {
279 double start = scrollableOffset_.GetY();
280 double end = 0.0;
281 StartAnimation(start, end, false);
282 return;
283 }
284 if (refreshStatus_ == RefreshStatus::OVER_DRAG) {
285 double start = scrollableOffset_.GetY();
286 double end = triggerRefreshDistance_;
287 loading_->SetLoadingMode(MODE_LOOP);
288 StartAnimation(start, end, false);
289 }
290 }
291
HandleDragCancel()292 void RenderRefresh::HandleDragCancel()
293 {
294 ResetStatus();
295 }
296
ResetStatus()297 void RenderRefresh::ResetStatus()
298 {
299 refreshing_ = false;
300 if (changeEvent_) {
301 changeEvent_("false");
302 }
303 refreshStatus_ = RefreshStatus::INACTIVE;
304 scrollableOffset_.Reset();
305 loading_->SetLoadingMode(MODE_DRAG);
306 loading_->SetDragDistance(scrollableOffset_.GetY());
307 MarkNeedLayout();
308 }
309
UpdateScrollOffset(double value)310 void RenderRefresh::UpdateScrollOffset(double value)
311 {
312 scrollableOffset_.SetY(value);
313 MarkNeedLayout();
314 }
315
FireRefreshEvent() const316 void RenderRefresh::FireRefreshEvent() const
317 {
318 if (refreshEvent_) {
319 LOGI("RefreshEvent, refreshing = %{public}d.", refreshing_);
320 std::string param =
321 std::string(R"("refresh",{"refreshing":)").append(refreshing_ ? "true" : "false").append("},null");
322 refreshEvent_(param);
323 }
324 if (onRefreshing_) {
325 onRefreshing_();
326 }
327 }
328
FirePullDownEvent(const std::string & state) const329 void RenderRefresh::FirePullDownEvent(const std::string& state) const
330 {
331 if (pullDownEvent_) {
332 LOGI("PullDown Event, state is %{public}s", state.c_str());
333 std::string param = std::string(R"("pulldown",{"state":")").append(state).append("\"},null");
334 pullDownEvent_(param);
335 }
336 }
337
StartAnimation(double start,double end,bool isFinished)338 void RenderRefresh::StartAnimation(double start, double end, bool isFinished)
339 {
340 animator_->ClearInterpolators();
341 animator_->ClearStopListeners();
342 translate_ = AceType::MakeRefPtr<CurveAnimation<double>>(start, end, Curves::FRICTION);
343 auto weak = AceType::WeakClaim(this);
344 translate_->AddListener(Animation<double>::ValueCallback([weak](double value) {
345 auto scroll = weak.Upgrade();
346 if (scroll) {
347 scroll->UpdateScrollOffset(value);
348 }
349 }));
350 animator_->SetDuration(ANIMATION_DURATION);
351 animator_->AddInterpolator(translate_);
352 animator_->AddStopListener([weak, isFinished]() {
353 auto refresh = weak.Upgrade();
354 if (refresh) {
355 refresh->HandleStopListener(isFinished);
356 }
357 });
358
359 animator_->Play();
360 }
361
HandleStopListener(const bool isFinished)362 void RenderRefresh::HandleStopListener(const bool isFinished)
363 {
364 // Update the last loading time
365 if (isFinished) {
366 timeComponent_->SetData(timeText_);
367 time_->Update(timeComponent_);
368 return;
369 }
370
371 if (NearEqual(scrollableOffset_.GetY(), triggerRefreshDistance_)) {
372 if (refreshing_) {
373 loading_->SetLoadingMode(MODE_LOOP);
374 }
375 refreshing_ = true;
376 if (changeEvent_) {
377 changeEvent_("true");
378 }
379 FireRefreshEvent();
380 } else if (NearEqual(scrollableOffset_.GetY(), 0.0f)) {
381 ResetStatus();
382 } else {
383 loading_->SetLoadingMode(MODE_DRAG);
384 }
385 }
386
GetNextStatus()387 RefreshStatus RenderRefresh::GetNextStatus()
388 {
389 RefreshStatus nextStatus;
390 auto context = context_.Upgrade();
391 switch (refreshStatus_) {
392 case RefreshStatus::INACTIVE:
393 if (refreshing_) {
394 StartAnimation(0.0, triggerRefreshDistance_, false);
395 nextStatus = RefreshStatus::REFRESH;
396 break;
397 }
398 if (LessOrEqual(scrollableOffset_.GetY(), 0.0)) {
399 nextStatus = RefreshStatus::INACTIVE;
400 break;
401 }
402 // No break here, continue next case
403 FirePullDownEvent(PULL_DOWN_START);
404 [[fallthrough]];
405 case RefreshStatus::DRAG:
406 if (LessOrEqual(scrollableOffset_.GetY(), 0.0)) {
407 FirePullDownEvent(PULL_DOWN_END);
408 nextStatus = RefreshStatus::INACTIVE;
409 } else if (scrollableOffset_.GetY() < triggerRefreshDistance_) {
410 nextStatus = RefreshStatus::DRAG;
411 } else {
412 nextStatus = RefreshStatus::OVER_DRAG;
413 }
414 break;
415 case RefreshStatus::OVER_DRAG:
416 if (!refreshEvent_ && !context->GetIsDeclarative()) {
417 nextStatus = RefreshStatus::DONE;
418 break;
419 }
420 if (scrollableOffset_.GetY() > triggerRefreshDistance_) {
421 nextStatus = RefreshStatus::OVER_DRAG;
422 break;
423 }
424 // No break here, continue get next status.
425 [[fallthrough]];
426 case RefreshStatus::REFRESH:
427 if (!refreshing_) {
428 timeText_ = StringUtils::FormatString(lastTimeText_.c_str(), GetFormatDateTime().c_str());
429 StartAnimation(scrollableOffset_.GetY(), 0.0, true);
430 nextStatus = RefreshStatus::DONE;
431 break;
432 }
433 nextStatus = RefreshStatus::REFRESH;
434 break;
435 case RefreshStatus::DONE:
436 if (scrollableOffset_.GetY() > 0.0) {
437 nextStatus = RefreshStatus::DONE;
438 } else {
439 FirePullDownEvent(PULL_DOWN_END);
440 nextStatus = RefreshStatus::INACTIVE;
441 }
442 break;
443 default:
444 nextStatus = RefreshStatus::INACTIVE;
445 break;
446 }
447 if (onStateChange_ && (refreshStatus_ != nextStatus)) {
448 onStateChange_(static_cast<int>(nextStatus));
449 }
450 return nextStatus;
451 }
452
GetFriction(double percentage) const453 double RenderRefresh::GetFriction(double percentage) const
454 {
455 if (NearEqual(percentage, 1.0)) {
456 return 0.0;
457 } else {
458 return frictionRatio_ * std::pow(1.0 - percentage, SQUARE);
459 }
460 }
461
GetOffset(double delta) const462 double RenderRefresh::GetOffset(double delta) const
463 {
464 double height = GetLayoutSize().Height();
465 if (!NearZero(height)) {
466 double friction = GetFriction(std::abs(scrollableOffset_.GetY() / height));
467 return friction * delta;
468 }
469 return delta;
470 }
471
MaxScrollableHeight() const472 double RenderRefresh::MaxScrollableHeight() const
473 {
474 return GetLayoutParam().GetMaxSize().Height();
475 }
476
OnTouchTestHit(const Offset & coordinateOffset,const TouchRestrict & touchRestrict,TouchTestResult & result)477 void RenderRefresh::OnTouchTestHit(
478 const Offset& coordinateOffset, const TouchRestrict& touchRestrict, TouchTestResult& result)
479 {
480 if (!dragDetector_ || hasScrollableChild_) {
481 return;
482 }
483 dragDetector_->SetCoordinateOffset(coordinateOffset);
484 result.emplace_back(dragDetector_);
485 }
486
GetLoadingDiameter() const487 double RenderRefresh::GetLoadingDiameter() const
488 {
489 double diameter = 0.0;
490 if (scrollableOffset_.GetY() < triggerLoadingDistance_) {
491 return diameter;
492 } else if (scrollableOffset_.GetY() < triggerRefreshDistance_) {
493 double maxDistance = triggerRefreshDistance_ - triggerLoadingDistance_;
494 double actualDistance = scrollableOffset_.GetY() - triggerLoadingDistance_;
495
496 // Get the diameter by actual distance
497 diameter = ((actualDistance * loadingDiameter_ * HALF) / maxDistance) + loadingDiameter_ * HALF;
498 } else {
499 diameter = loadingDiameter_;
500 }
501 return diameter;
502 }
503
GetLoadingOffset() const504 Offset RenderRefresh::GetLoadingOffset() const
505 {
506 auto pipelineContext = GetContext().Upgrade();
507 if (!pipelineContext) {
508 LOGE("pipelineContext update with nullptr");
509 return Offset::Zero();
510 }
511
512 if (scrollableOffset_.GetY() < triggerLoadingDistance_) {
513 return Offset::Zero();
514 }
515 if (!isUseOffset_) {
516 return scrollableOffset_ * HALF - Offset(0.0, GetLoadingDiameter() * HALF);
517 }
518 double factor =
519 (scrollableOffset_.GetY() - triggerLoadingDistance_) / (triggerRefreshDistance_ - triggerLoadingDistance_);
520 return Offset(0.0, indicatorOffset_ * factor);
521 }
522
GetShowTimeOffset() const523 Offset RenderRefresh::GetShowTimeOffset() const
524 {
525 auto pipelineContext = GetContext().Upgrade();
526 if (!pipelineContext) {
527 LOGE("pipelineContext update with nullptr");
528 return Offset::Zero();
529 }
530
531 double bottomOffset = timeBox_->GetLayoutSize().Height() +
532 pipelineContext->NormalizeToPx(Dimension(DEFAULT_TIME_BOX_BOTTOM_SIZE, DimensionUnit::VP));
533 return scrollableOffset_ - Offset(0.0, bottomOffset + timeOffset_);
534 }
535
GetOpacity() const536 double RenderRefresh::GetOpacity() const
537 {
538 double factor = 0.0;
539 if (scrollableOffset_.GetY() < triggerRefreshDistance_ - timeDistance_) {
540 return factor;
541 } else if (scrollableOffset_.GetY() < triggerRefreshDistance_) {
542 double actualDistance = scrollableOffset_.GetY() - triggerRefreshDistance_ + timeDistance_;
543
544 // Get the factor, timeDistance_ never be zero
545 if (!NearZero(timeDistance_)) {
546 factor = actualDistance / timeDistance_;
547 }
548 } else {
549 factor = 1.0;
550 }
551 return factor;
552 }
553
GetFormatDateTime()554 std::string RenderRefresh::GetFormatDateTime()
555 {
556 auto now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
557 auto local = std::localtime(&now);
558 if (local == nullptr) {
559 LOGE("Get localtime failed.");
560 return "";
561 }
562
563 // This is for i18n date time
564 DateTime dateTime;
565 dateTime.year = static_cast<uint32_t>(local->tm_year + BASE_YEAR);
566 dateTime.month = static_cast<uint32_t>(local->tm_mon);
567 dateTime.day = static_cast<uint32_t>(local->tm_mday);
568 dateTime.hour = static_cast<uint32_t>(local->tm_hour);
569 dateTime.minute = static_cast<uint32_t>(local->tm_min);
570 std::string time = Localization::GetInstance()->FormatDateTime(dateTime, LAST_UPDATE_FORMAT);
571 return time;
572 }
573
UpdateScrollableOffset(double delta)574 void RenderRefresh::UpdateScrollableOffset(double delta)
575 {
576 if (NearZero(delta)) {
577 LOGW("Delta is near zero!");
578 return;
579 }
580 if (refreshStatus_ == RefreshStatus::REFRESH) {
581 LOGW("The refresh status is refreshing!");
582 return;
583 }
584 Offset offset = Offset(0.0, GetOffset(delta));
585 scrollableOffset_ = scrollableOffset_ + offset;
586 scrollableOffset_.SetY(std::min(scrollableOffset_.GetY(), maxScrollOffset_));
587 MarkNeedLayout();
588 }
589
OnHiddenChanged(bool hidden)590 void RenderRefresh::OnHiddenChanged(bool hidden)
591 {
592 if (hidden) {
593 return;
594 }
595 ResetStatus();
596 }
597
PerformLayout()598 void RenderRefresh::PerformLayout()
599 {
600 const auto& children = GetChildren();
601 if (children.empty()) {
602 LOGW("Refresh has no child!");
603 return;
604 }
605 auto context = context_.Upgrade();
606 if (context == nullptr) {
607 LOGW("context is nullptr!");
608 return;
609 }
610
611 RefreshStatus nextState = GetNextStatus();
612 if (nextState != RefreshStatus::REFRESH && refreshStatus_ == RefreshStatus::REFRESH) {
613 loading_->SetLoadingMode(MODE_EXIT);
614 }
615 refreshStatus_ = nextState;
616 LayoutParam innerLayout = GetLayoutParam();
617 innerLayout.SetMinSize(Size(0.0, 0.0));
618 if (!NearEqual(scale_, context->GetDipScale())) {
619 // Notify loading to updated when window size changed.
620 CalcLoadingParams(refreshComponent_.Upgrade());
621 loading_->Layout(innerLayout);
622 }
623
624 loading_->SetDragDistance(scrollableOffset_.GetY());
625 loadingBox_->SetPosition(GetLoadingOffset());
626
627 display_->UpdateOpacity(GetOpacity() * MAX_ALPHA);
628 display_->SetPosition(GetShowTimeOffset());
629 loadingBox_->SetHidden(scrollableOffset_.GetY() < triggerLoadingDistance_);
630 loadingBox_->SetVisible(scrollableOffset_.GetY() > triggerLoadingDistance_);
631
632 columnChild_ = children.back();
633 columnChild_->Layout(innerLayout);
634 if (refreshType_ == RefreshType::PULL_DOWN && GreatNotEqual(scrollableOffset_.GetY(), 0.0)) {
635 columnChild_->SetPosition(scrollableOffset_);
636 } else {
637 columnChild_->SetPosition(Offset::Zero());
638 }
639
640 display_->Layout(innerLayout);
641 loadingBox_->Layout(innerLayout);
642 timeBox_->Layout(innerLayout);
643 SetLayoutSize(GetLayoutParam().GetMaxSize());
644 }
645
646 } // namespace OHOS::Ace
647