前言
如今 HarmonyOS NEXT 生态不断完善,ArkTS 作为官方主推开发语言,搭配 ArkUI 声明式布局,成为鸿蒙应用开发的核心技术组合。和传统移动端开发相比,数据驱动视图的设计模式大幅简化了界面更新逻辑,也是每一位鸿蒙开发者必须吃透的知识点。
对于入门学习者而言,单一的语法学习枯燥且难以落地,结合小型综合案例练习是最高效的学习方式。本文以摇骰子小游戏为实战载体,基于最新 API 12 版本开发,整合弹性布局、@State 状态管理、随机数算法、属性动画、事件拦截、数据统计等常用知识点。
整篇文章采用手把手教学模式,从环境准备、需求梳理、代码编写、功能调试到性能优化一站式讲解。项目无第三方依赖,所有代码基于鸿蒙原生 API 实现,复制即可编译运行。不管是个人练手、课程作业、实训报告,还是面试项目展示,本案例都具备很高的参考价值。
一、前期准备:开发环境搭建
1.1 软硬件要求
本项目严格适配 HarmonyOS NEXT API 12,低版本 SDK 会出现语法报错、API 失效等问题,推荐配置如下:
开发工具:DevEco Studio 4.0 及以上正式版本
编译 SDK:HarmonyOS SDK API 12
运行设备:鸿蒙模拟器 / 搭载 HarmonyOS NEXT 的真机设备
1.2 项目创建步骤
打开 DevEco Studio,点击 Create Project,选择模板 Empty Ability;
语言选择 ArkTS,编译版本指定为 API 12,自定义项目名称后完成创建;
进入工程后,依次打开 File → Project Structure,再次确认模块 SDK 版本,避免版本不兼容;
启动模拟器,等待镜像加载完成,准备后续代码调试。
温馨提示:API 12 对组件属性、动画语法做了统一规范,本文代码仅适配当前版本,向下不兼容旧版语法。
二、项目需求与界面设计
2.1 核心功能需求
在编码之前,先明确项目功能,保证开发思路清晰:
摇骰子功能:点击按钮随机生成 1~6 点骰子点数;
动画效果:骰子切换点数时增加旋转动画,提升交互质感;
防重复点击:动画执行过程中锁定按钮,防止频繁点击导致数据异常;
数据统计:实时记录总投掷次数、每个点数的出现次数;
一键重置:清空所有统计数据与运行状态,恢复初始界面;
多设备适配:使用弹性布局,兼容手机、平板等不同分辨率设备。
2.2 页面结构划分
整体页面划分为四大区域,层级清晰、逻辑分明:
顶部标题区:展示项目名称与技术说明;
核心展示区:放置骰子主体,呈现动画与当前点数;
功能按钮区:承载「摇骰子」「重置数据」两个操作按钮;
数据统计区:展示全局投掷次数、各点数出现频次。
三、核心技术知识点讲解
正式写代码前,我们先梳理本项目用到的核心技术,理解原理才能灵活运用。
3.1 声明式 UI 与 数据驱动视图
鸿蒙 ArkUI 采用声明式开发范式,和 Android、iOS 传统命令式开发有本质区别:
命令式开发:开发者主动操作控件实例,数据变化后手动调用方法刷新界面,数据与视图强耦合;
声明式开发:开发者只需要描述页面结构与样式,框架自动监听数据变化。数据更新后,框架自动刷新对应组件,开发者无需干预视图。
3.2 @State 状态装饰器
@State 是组件内部最基础的状态管理装饰器,也是本项目的核心:
被 @State 修饰的变量会被框架托管,建立数据与 UI 的双向绑定;
变量发生改变时,仅刷新依赖该变量的组件(局部刷新),渲染性能更高;
作用域仅限于当前自定义组件,适合管理组件内所有动态数据。
本项目中骰子点数、统计数据、动画状态等全部动态变量,均使用 @State 修饰。
3.3 随机数生成规则
骰子点数范围为 1~6,在 ArkTS 中使用 Math 工具类实现随机数生成:
Math.random():生成 [0, 1) 之间的随机浮点数;
Math.random() * 6:取值范围变为 [0, 6);
Math.floor():向下取整,得到 0~5 的整数;
最终 Math.floor(Math.random() * 6) + 1,生成 1~6 合法点数。
四、完整可运行源码
文件路径:entry/src/main/ets/pages/Index.ets
将原有代码全部删除,粘贴下方完整代码,即可直接编译运行。

@Entry
@Component
struct DiceGame {
  // 当前骰子点数
  @State diceNum: number = 1
  // 总投掷次数
  @State totalCount: number = 0
  // 各点数统计数组,下标0空置,1-6对应骰子点数
  @State countArr: number[] = [0, 0, 0, 0, 0, 0, 0]
  // 标记是否正在摇骰子,用于按钮拦截
  @State isRolling: boolean = false

  build() {
    // 根布局:纵向弹性布局
    Column() {
      // 标题模块
      Column() {
        Text('🎲 摇骰子小游戏')
          .fontSize(28)
          .fontWeight(FontWeight.Bold)
        Text('HarmonyOS NEXT API12 | ArkTS 实战案例')
          .fontSize(15)
          .fontColor('#999')
          .margin({ top: 8 })
      }
      .width('100%')
      .alignItems(HorizontalAlign.Center)
      .margin({ top: 20, bottom: 30 })

      // 骰子展示与动画模块
      Column() {
        Text(this.getDiceIcon(this.diceNum))
          .fontSize(140)
          // 旋转动画
          .rotate({ angle: this.isRolling ? 360 : 0 })
          .animation({ duration: 500, curve: Curve.EaseOut })

        Text(`当前点数:${this.diceNum}`)
          .fontSize(20)
          .fontColor('#0066CC')
          .margin({ top: 40 })
      }
      .width('100%')
      .height(340)
      .backgroundColor('#F0F2F5')
      .borderRadius(24)
      .justifyContent(FlexAlign.Center)
      .margin({ bottom: 30 })

      // 操作按钮模块
      Row() {
        Button('开始摇骰子')
          .width(170)
          .height(54)
          .fontSize(16)
          .backgroundColor('#007BFF')
          .enabled(!this.isRolling)
          .onClick(() => this.rollDice())

        Button('重置所有数据')
          .width(170)
          .height(54)
          .fontSize(16)
          .backgroundColor('#DC3545')
          .margin({ left: 25 })
          .onClick(() => this.resetGame())
      }
      .margin({ bottom: 35 })

      // 数据统计模块
      Column() {
        Text(`累计投掷次数:${this.totalCount}`)
          .fontSize(18)
          .fontWeight(FontWeight.Medium)
          .margin({ bottom: 20 })

        Column() {
          ForEach([1, 2, 3, 4, 5, 6], (item: number) => {
            Text(`${item} 点:${this.countArr[item]}`)
              .fontSize(16)
              .fontColor('#333')
              .margin({ top: 10 })
          })
        }
        .alignItems(HorizontalAlign.Center)
      }
      .width('100%')
      .padding(25)
      .backgroundColor('#F8F9FA')
      .borderRadius(18)
    }
    .width('94%')
    .height('100%')
    .padding(20)
    .backgroundColor('#FFFFFF')
    .justifyContent(FlexAlign.Start)
    .alignItems(HorizontalAlign.Center)
  }

  /**
   * 根据点数匹配骰子表情
   * @param num 骰子点数
   * @returns 对应表情符号
   */
  getDiceIcon(num: number): string {
    const iconList = ['', '⚀', '⚁', '⚂', '⚃', '⚄', '⚅']
    return iconList[num]
  }

  /**
   * 摇骰子核心逻辑
   */
  rollDice() {
    this.isRolling = true
    // 延时模拟摇骰子过程
    setTimeout(() => {
      // 生成1-6随机点数
      const randomNum = Math.floor(Math.random() * 6) + 1
      this.diceNum = randomNum
      // 更新统计数据
      this.totalCount += 1
      this.countArr[randomNum] += 1
      // 结束状态,解锁按钮
      this.isRolling = false
    }, 500)
  }

  /**
   * 重置游戏所有状态与数据
   */
  resetGame() {
    this.diceNum = 1
    this.totalCount = 0
    this.countArr = [0, 0, 0, 0, 0, 0, 0]
    this.isRolling = false
  }
}

五、代码逐模块深度解析
5.1 状态变量定义

@State diceNum: number = 1
@State totalCount: number = 0
@State countArr: number[] = [0, 0, 0, 0, 0, 0, 0]
@State isRolling: boolean = false

所有会改变、且需要驱动界面更新的变量,统一使用 @State 修饰:
diceNum:存储当前骰子点数,核心展示数据;
totalCount:记录全局总投掷次数;
countArr:数组存储每个点数的出现次数,下标与点数一一对应;
isRolling:状态标记,实现按钮禁用,拦截重复点击。
5.2 骰子图标匹配方法

getDiceIcon(num: number): string {
  const iconList = ['', '⚀', '⚁', '⚂', '⚃', '⚄', '⚅']
  return iconList[num]
}

将骰子表情统一封装为独立方法,做到视图资源与业务逻辑分离。后续如果想要替换图片、修改样式,仅需修改当前方法,代码可维护性更强。
5.3 摇骰子核心逻辑

rollDice() {
  this.isRolling = true
  setTimeout(() => {
    const randomNum = Math.floor(Math.random() * 6) + 1
    this.diceNum = randomNum
    this.totalCount += 1
    this.countArr[randomNum] += 1
    this.isRolling = false
  }, 500)
}

首先将 isRolling 置为 true,锁定按钮,防止重复触发;
通过 setTimeout 设置 500 毫秒延时,模拟真实摇骰子的动画过程;
调用随机数算法生成合法点数,同步更新界面与统计数据;
延时结束后重置状态,解锁按钮,完成一次投掷流程。
5.4 动画配置说明

.rotate({ angle: this.isRolling ? 360 : 0 })
.animation({ duration: 500, curve: Curve.EaseOut })

rotate:旋转属性,动画期间旋转 360 度,模拟骰子滚动效果;
duration:动画时长 500ms,节奏符合移动端交互习惯;
Curve.EaseOut:缓动动画,先快后慢,视觉效果更自然。
5.5 数据重置方法

resetGame() {
  this.diceNum = 1
  this.totalCount = 0
  this.countArr = [0, 0, 0, 0, 0, 0, 0]
  this.isRolling = false
}

一键将所有状态变量恢复为初始值,清空数据、重置界面,保证状态闭环,无残留异常。
在这里插入图片描述

六、功能测试与适配验证
6.1 运行步骤
确保 SDK 版本为 API 12,代码粘贴完成后点击运行按钮;
选择模拟器或真机部署应用;
逐项测试所有功能,验证逻辑是否正常。
6.2 测试用例
初始状态:骰子默认 1 点,所有统计数据为 0;
投掷测试:点击按钮,骰子旋转并生成随机点数,统计数据同步更新;
防重测试:快速连续点击按钮,按钮处于禁用状态,逻辑不会重复执行;
重置测试:点击重置按钮,界面与数据全部恢复初始状态;
适配测试:切换不同尺寸模拟器,页面布局完整,无挤压、溢出问题。
七、开发常见问题及解决方案
结合实际开发中遇到的高频问题,整理排错方案,帮你快速避坑:
修改变量后界面不刷新
原因:动态变量未添加 @State 装饰器。
解决:所有驱动 UI 变化的变量,必须使用 @State 托管。
随机数出现数字 0
原因:随机算法缺少 +1,取值范围错误。
解决:固定使用 Math.floor(Math.random() * 6) + 1 生成 1~6 点数。
动画完全不生效
原因:动画绑定的状态变量没有发生改变。
解决:保证动画依赖的 isRolling 状态正常切换。
连续点击导致统计数据错乱
原因:未做按钮拦截,短时间多次触发逻辑。
解决:增加状态标记,动画期间禁用按钮。
小屏设备布局错乱
原因:大量使用固定宽高,未使用百分比布局。
解决:页面尺寸优先使用百分比,配合弹性布局完成适配。
八、项目优化与拓展方向
基础功能完成后,可以从优化、拓展两个维度升级项目,提升项目完整度:
8.1 体验优化
美化按钮样式、增加点击反馈;
调整颜色搭配,打造个性化 UI 界面;
优化动画时长与缓动效果,提升流畅度。
8.2 功能拓展
多骰子模式:同时生成 2~3 个骰子,计算点数总和;
图片替换:使用本地图片替代 Emoji 表情,实现高清骰子样式;
音频音效:接入鸿蒙音频 API,添加摇骰子、结果提示音效;
历史记录:数组存储每一次投掷结果,实现对战日志;
趣味玩法:新增猜大小、猜点数等互动小游戏。
九、学习总结
通过本款骰子小游戏的完整开发,我们综合掌握了鸿蒙开发多项核心技能:
理解声明式 UI与数据驱动视图的核心思想,分清新旧开发模式差异;
熟练掌握 @State 状态管理的使用场景与底层刷新逻辑;
学会 Column、Row 弹性布局,实现全设备自适应页面;
掌握 ArkUI 属性动画、组件事件、随机数算法、列表渲染等常用 API;
建立基础的代码规范思维,学会方法封装、逻辑解耦、异常拦截。
学习是一个循序渐进的过程,吃透基础案例后,大家可以尝试拆分自定义组件、学习跨组件状态通信、页面路由等进阶知识点,逐步向中型、大型项目过渡。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐