鸿蒙开发之相对布局、倒计时TextTimer示例代码

我来为您详细介绍鸿蒙开发中的相对布局和倒计时TextTimer组件的示例代码。

1. 相对布局(RelativeContainer)详解

相对布局是鸿蒙系统中常用的布局方式,允许子组件相对于父容器或其他组件进行定位。

图片[1]_鸿蒙开发之相对布局、倒计时TextTimer示例代码_知途无界

基本相对布局示例

// RelativeLayoutExample.ets
@Entry
@Component
struct RelativeLayoutExample {
  @State message: string = '相对布局示例'

  build() {
    Column({ space: 20 }) {
      Text(this.message)
        .fontSize(20)
        .fontWeight(FontWeight.Bold)
        .margin({ bottom: 20 })

      // 相对布局容器
      RelativeContainer() {
        // 父容器背景
        Row()
          .width('100%')
          .height('100%')
          .backgroundColor('#F5F5F5')
          .alignRules({
            center: { anchor: '__container__', align: VerticalAlign.Center },
            middle: { anchor: '__container__', align: HorizontalAlign.Center }
          })

        // 中心红色方块 - 作为锚点
        Row()
          .width(80)
          .height(80)
          .backgroundColor(Color.Red)
          .id('center_box')
          .alignRules({
            center: { anchor: '__container__', align: VerticalAlign.Center },
            middle: { anchor: '__container__', align: HorizontalAlign.Center }
          })
          .margin({ top: 10, left: 10 })

        // 上方绿色方块 - 相对于中心方块
        Row()
          .width(60)
          .height(60)
          .backgroundColor(Color.Green)
          .id('top_box')
          .alignRules({
            bottom: { anchor: 'center_box', align: VerticalAlign.Top },
            middle: { anchor: 'center_box', align: HorizontalAlign.Center }
          })
          .margin({ bottom: 20 })

        // 右侧蓝色方块 - 相对于中心方块
        Row()
          .width(60)
          .height(60)
          .backgroundColor(Color.Blue)
          .id('right_box')
          .alignRules({
            left: { anchor: 'center_box', align: HorizontalAlign.End },
            center: { anchor: 'center_box', align: VerticalAlign.Center }
          })
          .margin({ left: 20 })

        // 左下角黄色方块 - 相对于父容器
        Row()
          .width(60)
          .height(60)
          .backgroundColor(Color.Yellow)
          .id('bottom_left_box')
          .alignRules({
            left: { anchor: '__container__', align: HorizontalAlign.Start },
            bottom: { anchor: '__container__', align: VerticalAlign.Bottom }
          })
          .margin({ left: 20, bottom: 20 })

        // 右下角紫色方块 - 相对于父容器和其他组件
        Row()
          .width(60)
          .height(60)
          .backgroundColor(Color.Purple)
          .id('bottom_right_box')
          .alignRules({
            right: { anchor: '__container__', align: HorizontalAlign.End },
            bottom: { anchor: '__container__', align: VerticalAlign.Bottom },
            left: { anchor: 'bottom_left_box', align: HorizontalAlign.End }
          })
          .margin({ right: 20, bottom: 20 })

      }
      .width(300)
      .height(300)
      .backgroundColor('#FFFFFF')
      .border({ width: 2, color: Color.Black })

      // 说明文字
      Text('红色为中心锚点,其他组件相对其定位')
        .fontSize(14)
        .fontColor(Color.Gray)
        .margin({ top: 10 })

    }
    .width('100%')
    .padding(20)
    .backgroundColor('#FFFFFF')
  }
}

复杂相对布局示例(登录页面)

// LoginPage.ets
@Entry
@Component
struct LoginPage {
  @State username: string = ''
  @State password: string = ''

  build() {
    Column({ space: 30 }) {
      Text('用户登录')
        .fontSize(24)
        .fontWeight(FontWeight.Bold)
        .fontColor('#333333')

      // 登录表单的相对布局
      RelativeContainer() {
        // 背景卡片
        Column()
          .width('100%')
          .height('100%')
          .backgroundColor(Color.White)
          .borderRadius(12)
          .alignRules({
            center: { anchor: '__container__', align: VerticalAlign.Center },
            middle: { anchor: '__container__', align: HorizontalAlign.Center }
          })
          .padding(30)

        // Logo - 顶部居中
        Image($r('app.media.logo'))
          .width(80)
          .height(80)
          .id('logo')
          .alignRules({
            top: { anchor: '__container__', align: VerticalAlign.Top },
            middle: { anchor: '__container__', align: HorizontalAlign.Center }
          })
          .margin({ top: 20 })

        // 用户名输入框 - 在Logo下方
        TextInput({ placeholder: '请输入用户名' })
          .layoutWeight(1)
          .backgroundColor('#F8F8F8')
          .borderRadius(8)
          .id('username_input')
          .alignRules({
            top: { anchor: 'logo', align: VerticalAlign.Bottom },
            left: { anchor: '__container__', align: HorizontalAlign.Start },
            right: { anchor: '__container__', align: HorizontalAlign.End }
          })
          .margin({ top: 30, left: 10, right: 10 })

        // 密码输入框 - 在用户名输入框下方
        TextInput({ placeholder: '请输入密码' })
          .type(InputType.Password)
          .layoutWeight(1)
          .backgroundColor('#F8F8F8')
          .borderRadius(8)
          .id('password_input')
          .alignRules({
            top: { anchor: 'username_input', align: VerticalAlign.Bottom },
            left: { anchor: 'username_input', align: HorizontalAlign.Start },
            right: { anchor: 'username_input', align: HorizontalAlign.End }
          })
          .margin({ top: 15, left: 0, right: 0 })

        // 登录按钮 - 在密码输入框下方
        Button('登录')
          .width('100%')
          .height(45)
          .backgroundColor('#007DFF')
          .fontColor(Color.White)
          .fontSize(16)
          .fontWeight(FontWeight.Medium)
          .borderRadius(8)
          .id('login_button')
          .alignRules({
            top: { anchor: 'password_input', align: VerticalAlign.Bottom },
            left: { anchor: 'password_input', align: HorizontalAlign.Start },
            right: { anchor: 'password_input', align: HorizontalAlign.End }
          })
          .margin({ top: 25, left: 0, right: 0 })

        // 忘记密码链接 - 在登录按钮右侧
        Text('忘记密码?')
          .fontSize(14)
          .fontColor('#007DFF')
          .id('forgot_password')
          .alignRules({
            top: { anchor: 'login_button', align: VerticalAlign.Top },
            right: { anchor: 'login_button', align: HorizontalAlign.End }
          })
          .margin({ top: 0, right: 5 })

      }
      .width('90%')
      .height(320)
      .backgroundColor('#F0F2F5')
      .borderRadius(12)

    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
    .backgroundColor('#F0F2F5')
  }
}

2. TextTimer 倒计时组件详解

TextTimer是鸿蒙提供的倒计时组件,支持毫秒级精度和多种显示格式。

基础TextTimer示例

// TextTimerBasicExample.ets
@Entry
@Component
struct TextTimerBasicExample {
  @State count: number = 0
  @State timerRunning: boolean = false
  private timerController: TextTimerController = new TextTimerController()

  build() {
    Column({ space: 20 }) {
      Text('TextTimer 倒计时示例')
        .fontSize(20)
        .fontWeight(FontWeight.Bold)
        .margin({ bottom: 20 })

      // 基础倒计时 - 从10秒开始
      TextTimer({ controller: this.timerController })
        .format('HH:mm:ss.SS')
        .fontSize(24)
        .fontColor(Color.Blue)
        .fontWeight(FontWeight.Bold)
        .margin({ bottom: 10 })

      // 控制按钮
      Row({ space: 15 }) {
        Button(this.timerRunning ? '暂停' : '开始')
          .onClick(() => {
            if (this.timerRunning) {
              this.timerController.pause()
              this.timerRunning = false
            } else {
              // 从10秒开始倒计时
              this.timerController.start(10000) // 10秒 = 10000毫秒
              this.timerRunning = true
            }
          })
          .width(80)
          .height(35)

        Button('重置')
          .onClick(() => {
            this.timerController.reset()
            this.timerRunning = false
          })
          .width(80)
          .height(35)

        Button('停止')
          .onClick(() => {
            this.timerController.stop()
            this.timerRunning = false
          })
          .width(80)
          .height(35)
      }

      // 状态显示
      Text(`计时器状态: ${this.timerRunning ? '运行中' : '已停止'}`)
        .fontSize(14)
        .fontColor(this.timerRunning ? Color.Green : Color.Red)
        .margin({ top: 10 })

      Divider()
        .margin({ vertical: 20 })

      // 不同格式的示例
      Text('不同格式展示:')
        .fontSize(16)
        .fontWeight(FontWeight.Medium)

      // 格式1: 时分秒
      TextTimer({ controller: new TextTimerController() })
        .format('HH:mm:ss')
        .fontSize(18)
        .fontColor(Color.Orange)
        .margin({ bottom: 5 })
        .start(60000) // 1分钟

      // 格式2: 秒和毫秒
      TextTimer({ controller: new TextTimerController() })
        .format('ss.SS')
        .fontSize(18)
        .fontColor(Color.Purple)
        .margin({ bottom: 5 })
        .start(3000) // 3秒

      // 格式3: 自定义格式
      TextTimer({ controller: new TextTimerController() })
        .format('mm分ss秒')
        .fontSize(18)
        .fontColor(Color.Green)
        .start(123456) // 123.456秒

    }
    .width('100%')
    .padding(20)
    .backgroundColor('#FFFFFF')
  }
}

实用倒计时应用示例(验证码倒计时)

// CountdownTimerExample.ets
@Entry
@Component
struct CountdownTimerExample {
  @State countdown: number = 0
  @State isCounting: boolean = false
  @State phoneNumber: string = ''
  @State verificationCode: string = ''
  private countdownController: TextTimerController = new TextTimerController()
  private maxCountdown: number = 60 // 60秒倒计时

  // 格式化显示倒计时
  formatTime(milliseconds: number): string {
    const seconds = Math.ceil(milliseconds / 1000)
    return `${seconds}秒后重发`
  }

  // 开始倒计时
  startCountdown(): void {
    if (this.phoneNumber.length !== 11) {
      AlertDialog.show({
        title: '提示',
        message: '请输入正确的手机号码',
        confirm: {
          value: '确定',
          action: () => {}
        }
      })
      return
    }

    this.isCounting = true
    this.countdown = this.maxCountdown
    
    // 使用TextTimer进行倒计时
    this.countdownController.start(this.maxCountdown * 1000)
    
    // 监听倒计时变化
    this.countdownController.on('tick', (utc: number) => {
      this.countdown = Math.ceil((this.maxCountdown * 1000 - utc) / 1000)
      if (this.countdown <= 0) {
        this.isCounting = false
      }
    })
  }

  // 重置倒计时
  resetCountdown(): void {
    this.countdownController.reset()
    this.isCounting = false
    this.countdown = 0
  }

  build() {
    Column({ space: 25 }) {
      Text('手机验证码')
        .fontSize(22)
        .fontWeight(FontWeight.Bold)
        .fontColor('#333333')

      // 手机号输入
      Row({ space: 10 }) {
        TextInput({ placeholder: '请输入手机号码' })
          .layoutWeight(1)
          .height(45)
          .backgroundColor('#F5F5F5')
          .borderRadius(8)
          .onChange((value: string) => {
            this.phoneNumber = value
          })
          .type(InputType.PhoneNumber)

        Button(this.isCounting ? this.formatTime(this.countdown * 1000) : '获取验证码')
          .width(120)
          .height(45)
          .backgroundColor(this.isCounting ? '#CCCCCC' : '#007DFF')
          .fontColor(Color.White)
          .fontSize(14)
          .borderRadius(8)
          .enabled(!this.isCounting)
          .onClick(() => {
            this.startCountdown()
          })
      }
      .width('100%')
      .alignItems(VerticalAlign.Center)

      // 验证码输入
      Row({ space: 10 }) {
        TextInput({ placeholder: '请输入验证码' })
          .layoutWeight(1)
          .height(45)
          .backgroundColor('#F5F5F5')
          .borderRadius(8)
          .onChange((value: string) => {
            this.verificationCode = value
          })
          .type(InputType.Number)

        Button('验证')
          .width(80)
          .height(45)
          .backgroundColor('#28A745')
          .fontColor(Color.White)
          .fontSize(14)
          .borderRadius(8)
          .onClick(() => {
            if (this.verificationCode === '123456') { // 模拟验证码
              AlertDialog.show({
                title: '验证成功',
                message: '验证码正确!',
                confirm: {
                  value: '确定',
                  action: () => {
                    this.resetCountdown()
                  }
                }
              })
            } else {
              AlertDialog.show({
                title: '验证失败',
                message: '验证码错误,请重新输入!',
                confirm: {
                  value: '确定',
                  action: () => {}
                }
              })
            }
          })
      }
      .width('100%')
      .alignItems(VerticalAlign.Center)

      // 倒计时显示区域
      if (this.isCounting) {
        Row() {
          Text('⏰')
            .fontSize(16)
            .margin({ right: 5 })
          
          TextTimer({ controller: this.countdownController })
            .format('ss')
            .fontSize(16)
            .fontColor('#FF6B6B')
            .fontWeight(FontWeight.Medium)
          
          Text(' 秒后可重新获取')
            .fontSize(14)
            .fontColor('#666666')
        }
        .margin({ top: 10 })
      }

      Divider()
        .margin({ vertical: 20 })

      // 其他应用场景示例
      Text('其他应用场景:')
        .fontSize(16)
        .fontWeight(FontWeight.Medium)
        .margin({ bottom: 15 })

      // 考试倒计时
      Row() {
        Text('考试剩余时间: ')
          .fontSize(14)
          .fontColor('#333333')
        
        TextTimer({ controller: new TextTimerController() })
          .format('mm:ss')
          .fontSize(14)
          .fontColor('#DC3545')
          .fontWeight(FontWeight.Bold)
          .start(1800000) // 30分钟
      }
      .margin({ bottom: 10 })

      // 活动倒计时
      Row() {
        Text('活动结束倒计时: ')
          .fontSize(14)
          .fontColor('#333333')
        
        TextTimer({ controller: new TextTimerController() })
          .format('HH:mm:ss')
          .fontSize(14)
          .fontColor('#FF9800')
          .fontWeight(FontWeight.Bold)
          .start(7200000) // 2小时
      }

    }
    .width('100%')
    .padding(20)
    .backgroundColor('#FAFAFA')
  }
}

相对布局与TextTimer结合的复杂示例

// ComplexLayoutWithTimer.ets
@Entry
@Component
struct ComplexLayoutWithTimer {
  @State gameTime: number = 300 // 5分钟游戏时间
  @State score: number = 0
  private gameTimer: TextTimerController = new TextTimerController()

  aboutToAppear(): void {
    // 游戏开始时启动倒计时
    this.gameTimer.start(this.gameTime * 1000)
    this.gameTimer.on('tick', (utc: number) => {
      this.gameTime = Math.ceil((this.gameTime * 1000 - utc) / 1000)
      if (this.gameTime <= 0) {
        this.gameOver()
      }
    })
  }

  gameOver(): void {
    AlertDialog.show({
      title: '游戏结束',
      message: `游戏时间到!您的得分是:${this.score}`,
      confirm: {
        value: '重新开始',
        action: () => {
          this.restartGame()
        }
      }
    })
  }

  restartGame(): void {
    this.score = 0
    this.gameTime = 300
    this.gameTimer.reset()
    this.gameTimer.start(this.gameTime * 1000)
  }

  build() {
    RelativeContainer() {
      // 游戏背景
      Column()
        .width('100%')
        .height('100%')
        .backgroundColor('#1A1A2E')
        .alignRules({
          center: { anchor: '__container__', align: VerticalAlign.Center },
          middle: { anchor: '__container__', align: HorizontalAlign.Center }
        })

      // 顶部状态栏
      Row()
        .width('100%')
        .height(60)
        .backgroundColor('#16213E')
        .id('status_bar')
        .alignRules({
          top: { anchor: '__container__', align: VerticalAlign.Top },
          left: { anchor: '__container__', align: HorizontalAlign.Start },
          right: { anchor: '__container__', align: HorizontalAlign.End }
        })
        .padding({ left: 20, right: 20 })

      // 时间显示 - 在状态栏左侧
      Row() {
        Text('⏱️')
          .fontSize(20)
          .fontColor('#00D4AA')
          .margin({ right: 8 })
        
        TextTimer({ controller: this.gameTimer })
          .format('mm:ss')
          .fontSize(18)
          .fontColor('#00D4AA')
          .fontWeight(FontWeight.Bold)
      }
      .id('time_display')
      .alignRules({
        center: { anchor: 'status_bar', align: VerticalAlign.Center },
        left: { anchor: 'status_bar', align: HorizontalAlign.Start }
      })

      // 分数显示 - 在状态栏右侧
      Row() {
        Text('🏆')
          .fontSize(20)
          .fontColor('#FFD700')
          .margin({ right: 8 })
        
        Text(`${this.score}`)
          .fontSize(18)
          .fontColor('#FFD700')
          .fontWeight(FontWeight.Bold)
      }
      .id('score_display')
      .alignRules({
        center: { anchor: 'status_bar', align: VerticalAlign.Center },
        right: { anchor: 'status_bar', align: HorizontalAlign.End }
      })

      // 游戏主区域 - 在时间显示下方
      Column()
        .width('90%')
        .height(400)
        .backgroundColor('#0F3460')
        .borderRadius(12)
        .id('game_area')
        .alignRules({
          top: { anchor: 'status_bar', align: VerticalAlign.Bottom },
          left: { anchor: '__container__', align: HorizontalAlign.Start },
          right: { anchor: '__container__', align: HorizontalAlign.End }
        })
        .margin({ top: 20 })

      // 游戏标题 - 在游戏区域内部顶部
      Text('点击游戏区域得分!')
        .fontSize(16)
        .fontColor('#FFFFFF')
        .id('game_title')
        .alignRules({
          top: { anchor: 'game_area', align: VerticalAlign.Top },
          left: { anchor: 'game_area', align: HorizontalAlign.Start },
          right: { anchor: 'game_area', align: HorizontalAlign.End }
        })
        .margin({ top: 20, left: 20, right: 20 })

      // 游戏按钮 - 在游戏区域中央
      Button('点击得分 +1')
        .width(150)
        .height(50)
        .backgroundColor('#E94560')
        .fontColor(Color.White)
        .fontSize(16)
        .fontWeight(FontWeight.Medium)
        .borderRadius(25)
        .id('game_button')
        .alignRules({
          center: { anchor: 'game_area', align: VerticalAlign.Center },
          center: { anchor: 'game_area', align: HorizontalAlign.Center }
        })
        .onClick(() => {
          this.score++
        })

      // 底部控制按钮 - 在游戏区域下方
      Row({ space: 20 })
        .width('90%')
        .height(50)
        .id('control_buttons')
        .alignRules({
          top: { anchor: 'game_area', align: VerticalAlign.Bottom },
          left: { anchor: '__container__', align: HorizontalAlign.Start },
          right: { anchor: '__container__', align: HorizontalAlign.End }
        })
        .margin({ top: 20 })

      // 暂停按钮 - 在控制按钮区域左侧
      Button('暂停')
        .width(100)
        .height(45)
        .backgroundColor('#6C757D')
        .fontColor(Color.White)
        .borderRadius(8)
        .id('pause_button')
        .alignRules({
          center: { anchor: 'control_buttons', align: VerticalAlign.Center },
          left: { anchor: 'control_buttons', align: HorizontalAlign.Start }
        })
        .onClick(() => {
          this.gameTimer.pause()
        })

      // 重置按钮 - 在控制按钮区域右侧
      Button('重置')
        .width(100)
        .height(45)
        .backgroundColor('#17A2B8')
        .fontColor(Color.White)
        .borderRadius(8)
        .id('reset_button')
        .alignRules({
          center: { anchor: 'control_buttons', align: VerticalAlign.Center },
          right: { anchor: 'control_buttons', align: HorizontalAlign.End }
        })
        .onClick(() => {
          this.restartGame()
        })

    }
    .width('100%')
    .height('100%')
    .backgroundColor('#0A0A0A')
  }
}

关键知识点总结

相对布局要点:

  1. 锚点系统​:每个组件都需要设置锚点(anchor)和对齐方式(align)
  2. ID标识​:使用.id()为组件设置唯一标识,供其他组件引用
  3. 相对关系​:组件可以相对于父容器(__container__)或其他组件定位
  4. 边距设置​:使用.margin()调整组件间的间距

TextTimer要点:

  1. 控制器​:使用TextTimerController控制倒计时的开始、暂停、重置
  2. 时间格式​:支持HH:mm:ss.SSmm:ssss.SS等多种格式
  3. 事件监听​:通过on('tick')监听倒计时变化
  4. 时间单位​:start()方法的参数是毫秒

这些示例涵盖了相对布局和TextTimer的主要用法,可以根据实际需求进行调整和扩展。

© 版权声明
THE END
喜欢就点个赞,支持一下吧!
点赞90 分享
评论 抢沙发
头像
欢迎您留下评论!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容