HarmonyOS 鸿蒙Next中如何实现自定义倒计时功能?可支持自定义时间格式(HH:MM:SS/MM:SS)

HarmonyOS 鸿蒙Next中如何实现自定义倒计时功能?可支持自定义时间格式(HH:MM:SS/MM:SS) 如何实现自定义倒计时功能?要求支持自定义时间格式(HH:MM:SS/MM:SS)、可配置倒计时时长、倒计时结束触发自定义业务逻辑(如启用按钮、提示用户),适配多版本且无需依赖额外资源包?

6 回复

我们开发 HarmonyOS 应用时,常需实现倒计时功能(如验证码倒计时、活动倒计时、提交按钮冷却倒计时),要求支持自定义时间格式(HH:MM:SS/MM:SS)、可配置倒计时时长、倒计时结束触发自定义业务逻辑(如启用按钮、提示用户)等,这里就给大家一个基本的实现示例

一、核心点

1. 核心倒计时逻辑(Timer 实现)

使用setInterval创建每秒执行的定时器,通过remainingSeconds记录剩余秒数,每秒递减;

封装clearTimer方法统一管理定时器销毁,避免多次创建定时器导致倒计时加速;

2. 自定义时间格式

支持HH:MM:SS(时:分: 秒)和MM:SS(分:秒)两种格式,通过formatType状态切换;

封装formatTime通用格式化方法,内置padZero补零函数,确保时间显示为两位数(如 01:02 而非 1:2);

MM:SS格式自动将小时转换为分钟(如 1 小时 20 分钟 = 80:00),适配短时长倒计时场景。

3. 灵活的倒计时控制

开始:支持传入自定义总秒数,重置并启动倒计时;

暂停:保留当前剩余秒数,停止定时器;

重置:恢复初始倒计时秒数,停止定时器。

4. 结束回调(自定义业务逻辑)

封装onCountdownFinish方法,倒计时结束时触发,示例中实现 “启用按钮” 逻辑;

可按需扩展:如弹出提示、提交表单、跳转页面等,无需修改核心倒计时逻辑。

二、关键代码实现

时间格式化:

/**
 * 格式化时间:根据指定格式转换秒数为 HH:MM:SS 或 MM:SS
 * @param seconds 剩余秒数
 * @returns 格式化后的时间字符串
 */
private formatTime(seconds: number): string {
  // 处理负数情况
  if (seconds < 0) seconds = 0;

  const hours = Math.floor(seconds / 3600);
  const minutes = Math.floor((seconds % 3600) / 60);
  const secs = seconds % 60;

  // 补零函数:确保数字为两位数
  const padZero = (num: number): string => num.toString().padStart(2, '0');

  if (this.formatType === 'HH:MM:SS') {
    return `${padZero(hours)}:${padZero(minutes)}:${padZero(secs)}`;
  } else {
    // MM:SS格式:小时自动转换为分钟(如120秒=02:00,3720秒=62:00)
    const totalMinutes = Math.floor(seconds / 60);
    return `${padZero(totalMinutes)}:${padZero(secs)}`;
  }
}

倒计时结束回调:

  /**
   * 倒计时结束回调(自定义业务逻辑)
   */
  private onCountdownFinish(): void {
    console.log('倒计时结束:触发自定义业务逻辑');
    // 示例1:启用按钮
    this.btnDisabled = false;
    // 示例2:弹窗提示(可根据需求扩展)
    // promptAction.showToast({ message: '倒计时结束!' });
  }
}

三、完整代码

// components/CountdownTimer/CountdownTimer.ets
@Entry
@Component
struct CountdownTimerDemo {
  // 倒计时核心状态
  @State remainingSeconds: number = 120; // 总倒计时秒数(示例:2分钟)
  @State isCounting: boolean = false; // 是否正在倒计时
  @State timerId: number = -1; // 定时器ID,用于销毁定时器
  @State formatType: 'HH:MM:SS' | 'MM:SS' = 'MM:SS'; // 时间格式类型
  @State btnDisabled: boolean = true; // 示例:倒计时结束启用按钮

  build() {
    Column() {
      // 倒计时显示区域
      Text(this.formatTime(this.remainingSeconds))
        .fontSize(24)
        .fontWeight(FontWeight.Bold)
        .fontColor('#3498DB')
        .margin(30);

      // 格式切换按钮
      Row() {
        Button('HH:MM:SS格式')
          .onClick(() => {
            this.formatType = 'HH:MM:SS';
          })
          .backgroundColor(this.formatType === 'HH:MM:SS' ? '#3498DB' : '#F5F5F5')
          .fontColor(this.formatType === 'HH:MM:SS' ? '#FFFFFF' : '#333333')
          .margin(5);

        Button('MM:SS格式')
          .onClick(() => {
            this.formatType = 'MM:SS';
          })
          .backgroundColor(this.formatType === 'MM:SS' ? '#3498DB' : '#F5F5F5')
          .fontColor(this.formatType === 'MM:SS' ? '#FFFFFF' : '#333333')
          .margin(5);
      }
      .margin(10);

      // 操作按钮组
      Row() {
        Button('开始倒计时',{ type: ButtonType.Capsule, stateEffect: this.isCounting})
          .onClick(() => {
            this.startCountdown(120); // 重置为2分钟倒计时并开始
          })
          .margin(5);

        Button('暂停倒计时',{ type: ButtonType.Capsule, stateEffect: !this.isCounting})
          .onClick(() => {
            this.pauseCountdown();
          })
          .margin(5);

        Button('重置倒计时')
          .onClick(() => {
            this.resetCountdown();
          })
          .margin(5);
      }
      .margin(20);

      // 示例:倒计时结束启用的业务按钮
      Button('倒计时结束可点击',{ type: ButtonType.Capsule, stateEffect: this.btnDisabled})
        .onClick(() => {
          console.log('业务按钮触发:倒计时已结束');
        })
        .backgroundColor(this.btnDisabled ? '#CCCCCC' : '#2ECC71')
        .fontColor('#FFFFFF')
        .margin(10);
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
    .backgroundColor('#F8F9FA')
 
  }

  /**
   * 格式化时间:根据指定格式转换秒数为 HH:MM:SS 或 MM:SS
   * @param seconds 剩余秒数
   * @returns 格式化后的时间字符串
   */
  private formatTime(seconds: number): string {
    // 处理负数情况
    if (seconds < 0) seconds = 0;

    const hours = Math.floor(seconds / 3600);
    const minutes = Math.floor((seconds % 3600) / 60);
    const secs = seconds % 60;

    // 补零函数:确保数字为两位数
    const padZero = (num: number): string => num.toString().padStart(2, '0');

    if (this.formatType === 'HH:MM:SS') {
      return `${padZero(hours)}:${padZero(minutes)}:${padZero(secs)}`;
    } else {
      // MM:SS格式:小时自动转换为分钟(如120秒=02:00,3720秒=62:00)
      const totalMinutes = Math.floor(seconds / 60);
      return `${padZero(totalMinutes)}:${padZero(secs)}`;
    }
  }

  /**
   * 开始倒计时
   * @param totalSeconds 总倒计时秒数(默认传当前剩余秒数)
   */
  private startCountdown(totalSeconds: number = this.remainingSeconds): void {
    // 清除已有定时器
    this.clearTimer();
    // 重置状态
    this.remainingSeconds = totalSeconds;
    this.isCounting = true;
    this.btnDisabled = true;

    // 创建定时器:每秒执行一次
    this.timerId = setInterval(() => {
      this.remainingSeconds--;

      // 倒计时结束
      if (this.remainingSeconds <= 0) {
        this.clearTimer();
        this.isCounting = false;
        this.remainingSeconds = 0;
        // 触发结束回调(自定义业务逻辑)
        this.onCountdownFinish();
      }
    }, 1000);
  }

  /**
   * 暂停倒计时
   */
  private pauseCountdown(): void {
    this.clearTimer();
    this.isCounting = false;
  }

  /**
   * 重置倒计时
   */
  private resetCountdown(): void {
    this.clearTimer();
    this.remainingSeconds = 120; // 重置为初始值
    this.isCounting = false;
    this.btnDisabled = true;
  }

  /**
   * 清除定时器(通用方法)
   */
  private clearTimer(): void {
    if (this.timerId !== -1) {
      clearInterval(this.timerId);
      this.timerId = -1;
    }
  }

  /**
   * 倒计时结束回调(自定义业务逻辑)
   */
  private onCountdownFinish(): void {
    console.log('倒计时结束:触发自定义业务逻辑');
    // 示例1:启用按钮
    this.btnDisabled = false;
    // 示例2:弹窗提示(可根据需求扩展)
    // promptAction.showToast({ message: '倒计时结束!' });
  }
}

四、效果展示

cke_18223.png

更多关于HarmonyOS 鸿蒙Next中如何实现自定义倒计时功能?可支持自定义时间格式(HH:MM:SS/MM:SS)的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


学习了,厉害!,

如果对时间要求精准,应该用轮训,记录下开始时间戳,再定时计算过去了多久,这样就算退到后台,应用被挂起后,重新进时间也是准确的,直接setInterval会有时间差,越久偏差越大了,且不能退后台,

不过就简单计时没啥问题,

在HarmonyOS Next中,实现自定义倒计时功能需使用@ohos.worker创建后台任务。通过setInterval每秒更新剩余时间,计算时、分、秒。自定义时间格式可通过条件判断实现:检测格式字符串包含“HH”则显示小时,否则隐藏。使用Text组件显示格式化后的时间字符串,格式如${hh}:${mm}:${ss}${mm}:${ss}。倒计时结束调用clearInterval停止任务。

在HarmonyOS Next中实现自定义倒计时功能,可以通过@State@Link装饰器结合定时器与日期计算来实现,核心是管理剩余时间并格式化输出。

1. 核心实现方案:

  • 使用@State装饰器定义倒计时剩余总秒数totalSeconds和格式化后的时间字符串timeText
  • aboutToAppear()中初始化倒计时,在aboutToDisappear()中清除定时器。
  • 通过setInterval启动每秒更新的定时器,在回调中减少totalSeconds并更新timeText
  • 倒计时结束时触发自定义回调(如启用按钮),并清除定时器。

2. 关键代码示例:

// 自定义时间格式化函数
private formatTime(seconds: number, format: string): string {
  const hrs = Math.floor(seconds / 3600);
  const mins = Math.floor((seconds % 3600) / 60);
  const secs = seconds % 60;
  
  if (format === 'HH:MM:SS') {
    return `${hrs.toString().padStart(2, '0')}:${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
  } else {
    // MM:SS格式(自动处理小时部分)
    const totalMins = Math.floor(seconds / 60);
    const remainingSecs = seconds % 60;
    return `${totalMins.toString().padStart(2, '0')}:${remainingSecs.toString().padStart(2, '0')}`;
  }
}

// 倒计时逻辑
@State totalSeconds: number = 300; // 默认5分钟
@State timeText: string = '';
private timerId: number = 0;

aboutToAppear() {
  this.startCountdown();
}

startCountdown() {
  this.timerId = setInterval(() => {
    if (this.totalSeconds > 0) {
      this.totalSeconds--;
      this.timeText = this.formatTime(this.totalSeconds, 'HH:MM:SS'); // 可配置格式
    } else {
      clearInterval(this.timerId);
      this.onCountdownEnd(); // 触发结束回调
    }
  }, 1000);
}

onCountdownEnd() {
  // 执行自定义业务逻辑,如启用按钮、弹出提示
  // this.isButtonEnabled = true;
}

aboutToDisappear() {
  clearInterval(this.timerId);
}

3. 自定义配置扩展:

  • 通过@Prop接收外部传入的初始时间、时间格式参数。
  • 结束回调可通过自定义事件或函数参数暴露给父组件。
  • 支持动态暂停/重启:通过状态变量控制定时器启停。

4. 多版本适配:

  • 使用ArkTS语法,确保与HarmonyOS Next API完全兼容。
  • 避免使用已废弃的API,如getContext()相关操作需替换为当前版本推荐方式。
  • 时间格式化使用纯TypeScript/JavaScript实现,不依赖额外资源包。

此方案通过状态驱动UI更新,符合HarmonyOS Next的声明式开发范式,能灵活适配不同时间格式和业务场景。

回到顶部