HarmonyOS NEXT API12 实战:ArkTS 结合状态管理与动画实现骰子游戏
本文介绍了一个基于HarmonyOS NEXT API 12开发的摇骰子小游戏案例,使用ArkTS语言和ArkUI声明式布局实现。项目涵盖了弹性布局、@State状态管理、随机数算法、属性动画等核心技术点,适合鸿蒙开发者学习实践。文章从环境搭建、需求分析到代码实现(含完整源码)进行了详细讲解,重点展示了数据驱动视图的设计模式和组件化开发思路。案例无需第三方依赖,可直接编译运行,具有教学和实战参考价
前言
如今 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;
建立基础的代码规范思维,学会方法封装、逻辑解耦、异常拦截。
学习是一个循序渐进的过程,吃透基础案例后,大家可以尝试拆分自定义组件、学习跨组件状态通信、页面路由等进阶知识点,逐步向中型、大型项目过渡。
更多推荐




所有评论(0)