TextClock组件显示时间不能正确刷新问题修复
一、问题介绍
上一篇分享介绍了4.1R版本TextClock在设置不同地区语音的情况下存在时间显示的问题,而在master(注意是2025-08-18,这个时间之前的master、5.1R、5.0.3R ... 到5.0.0R版本),使用TextClock组件设置format('hh:mm aa')
不显示秒,设备连接网络,系统自动同步时间,或者通过hdc shell date '2025-07-17 08:00:00'
命令设置系统时间,TextClock组件大概率不会立即更新,而是存在0~59秒的延时后才会更新显示正确时间。
二、修改文件
foundation/arkui/ace_engine/frameworks/core/components_ng/pattern/text_clock/text_clock_pattern.cpp
修改RequestUpdateForNextSecond函数
# diff 参考
diff --git a/frameworks/core/components_ng/pattern/text_clock/text_clock_pattern.cpp b/frameworks/core/components_ng/pattern/text_clock/text_clock_pattern.cpp
index f69361b3bf6aa4e3ffb6d046b6d2174b835a9f67..626f5e0619297c43fae0cf8e9c0a9e1cac68bc3b 100644
--- a/frameworks/core/components_ng/pattern/text_clock/text_clock_pattern.cpp
+++ b/frameworks/core/components_ng/pattern/text_clock/text_clock_pattern.cpp
@@ -320,8 +320,15 @@ void TextClockPattern::RequestUpdateForNextSecond()
1; // millisecond
auto nextMinuteFlag = isForm_ || (!makeFunc_.has_value() && GetFormat().find('S') == std::string::npos
&& GetFormat().find('s') == std::string::npos);
+ auto now = std::chrono::system_clock::now();
+ auto duration_cast_to_millis = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch());
+ auto timeValue = duration_cast_to_millis.count();
+ if (std::abs(timeValue - timeValue_) > LOG_INTERVAL_TIME) {
+ timeValue_ = timeValue;
+ nextMinuteFlag = false;
+ }
if (nextMinuteFlag) {
- time_t current = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
+ time_t current = std::chrono::system_clock::to_time_t(now);
auto* timeZoneTime = std::localtime(¤t);
// delay to next minute
int32_t tempTime = (TOTAL_SECONDS_OF_MINUTE - timeZoneTime->tm_sec) * MILLISECONDS_OF_SECOND;
社区修改PR(仅供参考,尚未合入):https://gitee.com/openharmony/arkui_ace_engine/pulls/68235/files
三、代码解析
RequestUpdateForNextSecond函数主要功能是发送TextClock组件下一次更新的延时任务。
在其延时任务中,会执行UpdateTimeText函数,UpdateTimeText主要功能是更新TextClock组件时间:
delayTask_.Reset([weak = WeakClaim(this)] {
auto textClock = weak.Upgrade();
CHECK_NULL_VOID(textClock);
if (!textClock->isStart_) {
return;
}
textClock->UpdateTimeText();
});
在UpdateTimeText函数中又会调用RequestUpdateForNextSecond:
void TextClockPattern::UpdateTimeText(bool isTimeChange)
{
...
if (!isForm_) {
RequestUpdateForNextSecond();
}
...
}
这里的isForm_
表示是否是卡片或者动态组件,应用页面开发时默认为false
。
这里UpdateTimeText与RequestUpdateForNextSecond的相互调用就是TextClock组件更新的基本逻辑,可以参考理解为用setTimeout调用自身模拟setInterval来实现循环执行某一操作,当然还有其他的情况会触发TextClock组件更新,这里不讨论。
在RequestUpdateForNextSecond中,默认的延时时间delayTime
:
void TextClockPattern::UpdateTimeText(bool isTimeChange)
{
/**
* 1 second = 1000 millisecond = 1000000 microsecond.
* Millisecond is abbreviated as msec. Microsecond is abbreviated as usec.
* unit of [delayTime] is msec, unit of [tv_usec] is usec
* when [tv_usec] is 000100, (INTERVAL_OF_U_SECOND - timeUsec) / MICROSECONDS_OF_MILLISECOND = 999 msec
* which will cause the delay task still arriving in current second, because 999000 + 000100 = 999100 < 1 second
* so add an additional millisecond to modify the loss of precision during division
*/
int32_t delayTime =
(INTERVAL_OF_U_SECOND - static_cast<int32_t>(currentTime.tv_usec)) / MICROSECONDS_OF_MILLISECOND +
1; // millisecond
}
代码中的注释解释了delayTime
这么计算的原因:
/**
* 1 秒 = 1000 毫秒 = 1000000 微秒。
* 毫秒缩写为 msec。微秒缩写为 usec。
* [delayTime] 的单位是毫秒,[tv_usec] 的单位是 usec
* 当 [tv_usec] 为 000100 时,(INTERVAL_OF_U_SECOND - 时间 Usec) / MICROSECONDS_OF_MILLISECOND = 999 毫秒
* 这将导致延迟任务仍然在当前秒到达,因为 999000 + 000100 = 999100 < 1 秒
* 因此,再增加一毫秒以修改除法过程中精度的损失
*/
这里减去了当前的实际时间,并修改除法过程中精度的损失,因此下一次更新的时间是小于1秒的。
而后判断是否在下一分钟来更新:
auto nextMinuteFlag = isForm_ || (!makeFunc_.has_value() && GetFormat().find('S') == std::string::npos
&& GetFormat().find('s') == std::string::npos);
if (nextMinuteFlag) {
time_t current = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
auto* timeZoneTime = std::localtime(¤t);
// delay to next minute
int32_t tempTime = (TOTAL_SECONDS_OF_MINUTE - timeZoneTime->tm_sec) * MILLISECONDS_OF_SECOND;
delayTime += (tempTime - MILLISECONDS_OF_SECOND);
}
前面提到isForm_
在应用开发时默认为false
。
后面的makeFunc_.has_value
表示是否通过样式Builder定制内容区(可以在官方文档中搜索如何使用),默认也是false
。
后面的GetFormat().find('S') == std::string::npos && GetFormat().find('s') == std::string::npos
则是判断TextClock组件设置的format属性中不存在S
或者s
。
因此设置时间格式化不带秒的时候,nextMinuteFlag为true
,从而delayTime
被修改,修改的值为60秒减去当前时间对应的秒数乘以1000得到当前整秒距离下一分钟的毫秒数,再减去1000毫秒,再加上默认算出距离下一秒的毫秒数,从而得到准确的当前时间到下一分钟的毫秒数。
因此存在当设置时间格式化不带秒时,TextClock组件更新可能在0~59秒后才能更新,而此时如果系统时间发生了改变,TextClock组件大概率不能立即触发更新。
上面的修改代码逻辑为,获取系统当前时间,并转换为毫秒数
auto now = std::chrono::system_clock::now();
auto duration_cast_to_millis = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch());
auto timeValue = duration_cast_to_millis.count();
将当前时间的毫秒数减去上一次更新的时间毫秒数timeValue_
,如果两者相差超过对了59秒对应的毫秒数,就在下一秒进行更新,将nextMinuteFlag
改为false
,并更新timeValue_
。
上面的修改方法已通过社区的门禁验证,门禁编译的镜像地址为:https://cidownload.openharmony.cn/Artifacts/dayu200/20250720-1-00053/version/Artifacts-dayu200-20250720-1-00053-version-dayu200.tar.gz
可以下载镜像烧录到RK3568设备上查看效果。
更多推荐
所有评论(0)