鸿蒙NEXT开发案例:九宫格随机

【引言】

在鸿蒙NEXT开发中,九宫格抽奖是一个常见且有趣的应用场景。通过九宫格抽奖,用户可以随机获得不同奖品,增加互动性和趣味性。本文将介绍如何使用鸿蒙开发框架实现九宫格抽奖功能,并通过代码解析展示实现细节。

【环境准备】

• 操作系统:Windows 10

• 开发工具:DevEco Studio NEXT Beta1 Build Version: 5.0.3.806

• 目标设备:华为Mate60 Pro

• 开发语言:ArkTS

• 框架:ArkUI

• API版本:API 12

【思路】

本案例中的“九宫格随机”应用旨在模拟一个简单的抽奖场景,用户点击抽奖按钮后,程序会从预先定义好的九个奖品中随机选择一个作为最终奖品。整个应用采用了响应式编程模式,结合鸿蒙NEXT提供的组件化开发方式,实现了交互流畅、视觉效果良好的用户体验。

1. Prize类设计 应用首先定义了一个Prize类,用于表示奖品信息。该类使用了@ObservedV2装饰器,使得奖品属性(如标题、颜色、描述)的变化可以被自动追踪,从而实现UI的实时更新。构造函数允许创建具有特定属性值的奖品实例,便于后续管理。

2. MyPrizeUpdate结构组件 为了提供奖品信息的编辑功能,我们创建了MyPrizeUpdate结构组件。它通过接收外部传入的数据(当前选中的奖品索引、抽奖顺序数组及所有奖品的数组),构建了一个包含文本输入框的界面,用户可以在其中修改奖品的标题、描述和颜色。任何对这些属性的更改都会即时反映到对应的奖品对象上,并触发UI的相应更新。

3. LotteryPage入口组件LotteryPage是整个抽奖应用的核心组件,负责组织页面布局和处理用户交互逻辑。它初始化了一系列必要的状态变量,比如保存所有奖品的数组prizeArray、定义抽奖顺序的selectionOrder以及控制动画状态的isAnimating等。此外,该组件实现了抽奖过程的关键方法——startLottery(开始抽奖)、runAtConstantSpeed(匀速运行)和slowDown(减速),它们共同协作以模拟真实的抽奖体验。当用户点击抽奖按钮时,这些方法按照预定的速度模式依次调用,直到最终确定一个奖品为止。最后,通过弹出对话框的方式向用户展示抽奖结果。

4. UI布局与样式 在构建UI方面,应用充分利用了鸿蒙NEXT提供的布局容器(如Column、Row、Flex)和样式属性(如宽度、高度、边距、背景色、圆角、阴影),精心设计了每个奖品项的外观。特别地,对于抽奖按钮,不仅设置了独特的背景颜色,还在点击事件中添加了动画效果,增强了用户的参与感。同时,考虑到不同设备屏幕尺寸的差异,所有布局元素均采用相对单位进行设置,确保了应用在各种终端上的良好适配性。

5. 动画与交互优化 为了让抽奖过程看起来更加生动有趣,应用引入了加速、匀速、减速三个阶段的动画效果,使选中的奖品项能够以逐渐加快然后缓慢停止的方式出现在用户面前。这种变化不仅增加了悬念感,也提升了整体的娱乐性。此外,通过对点击事件的监听和处理,确保了即使是在动画过程中,用户的交互也不会受到影响,保证了良好的用户体验。

【完整代码】

// 定义一个可观察的Prize类,用于表示奖品信息。
@ObservedV2
class Prize {
  @Trace title: string // 奖品标题属性,使用@Trace进行追踪以便响应式更新UI
  @Trace color: string // 奖品颜色属性
  @Trace description: string // 奖品描述属性

  // 构造函数,用来初始化新的奖品实例
  constructor(title: string, color: string, description: string = "") {
    this.title = title // 设置奖品标题
    this.color = color // 设置奖品颜色
    this.description = description // 设置奖品描述,默认为空字符串
  }
}

// 定义MyPrizeUpdate结构组件,用于显示和编辑选中的奖品信息
@Component
struct MyPrizeUpdate {
  @Consume selectedIndex: number // 当前选中的奖品索引
  @Consume private selectionOrder: number[] // 保存抽奖顺序的数组
  @Consume private prizeArray: Prize[] // 保存所有奖品的数组

  build() {
    Column({ space: 20 }) { // 创建列布局容器,设置子元素之间的间距为20px
      Row() { // 创建行布局容器
        Text('标题:') // 显示“标题”文本
        TextInput({ text: this.prizeArray[this.selectionOrder[this.selectedIndex%this.selectionOrder.length]].title })
          .width('300lpx') // 设置输入框宽度
          .onChange((value) => { // 监听输入框内容变化
            this.prizeArray[this.selectionOrder[this.selectedIndex%this.selectionOrder.length]].title = value // 更新奖品标题
          })
      }
      Row() {
        Text('描述:')
        TextInput({
          text: `${this.prizeArray[this.selectionOrder[this.selectedIndex%this.selectionOrder.length]].description}`
        }).width('300lpx').onChange((value) => { // 同上,但针对奖品描述
          this.prizeArray[this.selectionOrder[this.selectedIndex%this.selectionOrder.length]].description = value
        })
      }
      Row() {
        Text('颜色:')
        TextInput({
          text: `${this.prizeArray[this.selectionOrder[this.selectedIndex%this.selectionOrder.length]].color}`
        }).width('300lpx').onChange((value) => { // 同上,但针对奖品颜色
          this.prizeArray[this.selectionOrder[this.selectedIndex%this.selectionOrder.length]].color = value
        })
      }
    }
    .justifyContent(FlexAlign.Start) // 设置内容左对齐
    .padding(40) // 设置内边距
    .width('100%') // 设置宽度为100%
    .backgroundColor(Color.White) // 设置背景颜色为白色
  }
}

// 定义抽奖页面入口组件
@Entry
@Component
struct LotteryPage {
  @Provide private selectedIndex: number = 0 // 提供当前选中的索引,初始值为0
  private isAnimating: boolean = false // 标记是否正在进行动画,初始值为false
  @Provide private selectionOrder: number[] = [0, 1, 2, 5, 8, 7, 6, 3] // 定义抽奖顺序
  private cellWidth: number = 200 // 单元格宽度
  private baseMargin: number = 10 // 单元格边距
  @Provide private prizeArray: Prize[] = [
    new Prize("红包", "#ff9675", "10元"), // 初始化奖品数组,创建各种奖品对象
    new Prize("话费", "#ff9f2e", "5元"),
    new Prize("红包", "#8e7fff", "50元"),
    new Prize("红包", "#48d1ea", "30元"),
    new Prize("开始抽奖", "#fffdfd"), // 抽奖按钮,没有具体奖品描述
    new Prize("谢谢参与", "#5f5f5f"),
    new Prize("谢谢参与", "#5f5f5f"),
    new Prize("超市红包", "#5f5f5f", "100元"),
    new Prize("鲜花", "#75b0fe"),
  ]
  private intervalID: number = 0 // 定时器ID,用于控制抽奖速度
  @State isSheetVisible: boolean = false // 控制底部弹出表单的可见性

  // 开始抽奖逻辑
  startLottery(speed: number = 500) {
    setTimeout(() => { // 设置延时执行
      if (speed > 50) { // 如果速度大于50,则递归调用startLottery以逐渐加速
        speed -= 50
        this.startLottery(speed)
      } else {
        this.runAtConstantSpeed() // 达到最高速度后进入匀速阶段
        return
      }
      this.selectedIndex++ // 每次调用时更新选中索引
    }, speed)
  }

  // 以恒定速度运行抽奖
  runAtConstantSpeed() {
    let speed = 40 + Math.floor(Math.random() * this.selectionOrder.length) // 随机生成一个速度值
    clearInterval(this.intervalID) // 清除之前的定时器
    this.intervalID = setInterval(() => { // 设置新的定时器来更新选中索引
      if (this.selectedIndex >= speed) { // 如果选中索引达到速度值,停止并进入减速阶段
        clearInterval(this.intervalID)
        this.slowDown()
        return
      }
      this.selectedIndex++
    }, 50)
  }

  // 减速逻辑
  slowDown(speed = 50) {
    setTimeout(() => { // 设置延时执行
      if (speed < 500) { // 如果速度小于500,则递归调用slowDown以逐渐减速
        speed += 50
        this.slowDown(speed)
      } else {
        this.selectedIndex %= this.selectionOrder.length // 确保索引在有效范围内
        let index = this.selectionOrder[this.selectedIndex] // 获取最终选中的奖品索引
        this.isAnimating = false // 动画结束
        this.getUIContext().showAlertDialog({ // 显示结果对话框
          title: '结果',
          message: `${this.prizeArray[index].title}${this.prizeArray[index].description}`, // 显示奖品信息
          confirm: {
            defaultFocus: true,
            value: '我知道了', // 确认按钮文本
            action: () => {} // 点击确认后的操作
          },
          alignment: DialogAlignment.Center,
        });
        return
      }
      this.selectedIndex++
    }, speed)
  }

  // 构建UI方法
  build() {
    Column() { // 使用Column布局容器
      Flex({ wrap: FlexWrap.Wrap }) { // 使用弹性布局,允许换行
        ForEach(this.prizeArray, (item: Prize, index: number) => { // 遍历奖品数组,创建每个奖品的UI
          Column() { // 使用Column布局容器为每个奖品项
            Text(`${item.title}`) // 显示奖品标题
              .fontColor(index == 4 ? Color.White : item.color) // 设置字体颜色,对于抽奖按钮特殊处理
              .fontSize(16)
            Text(`${item.description}`) // 显示奖品描述
              .fontColor(index == 4 ? Color.White : item.color) // 设置字体颜色
              .fontSize(20)
          }
          .clickEffect({ level: ClickEffectLevel.LIGHT, scale: 0.8 }) // 添加点击效果
          .onClick(() => { // 处理点击事件
            if (this.isAnimating) { // 如果正在动画中,忽略点击
              return
            }
            if (index == 4) { // 如果点击的是抽奖按钮,开始抽奖
              this.isAnimating = true
              this.startLottery()
            } else {
              for (let i = 0; i < this.selectionOrder.length; i++) {
                if (this.selectionOrder[i] == index) {
                  this.selectedIndex = i // 更新选中索引到对应位置
                }
              }
            }
          })
          .alignItems(HorizontalAlign.Center) // 设置水平居中对齐
          .justifyContent(FlexAlign.Center) // 设置垂直居中对齐
          .width(`${this.cellWidth}lpx`) // 设置单元格宽度
          .height(`${this.cellWidth}lpx`) // 设置单元格高度
          .margin(`${this.baseMargin}lpx`) // 设置单元格边距
          .backgroundColor(index == 4 ? "#ff5444" : // 抽奖按钮背景颜色特殊处理
            (this.selectionOrder[this.selectedIndex % this.selectionOrder.length] == index ? Color.Gray : Color.White))
          .borderRadius(10) // 设置圆角
          .shadow({ // 设置阴影效果
            radius: 10,
            color: "#f98732",
            offsetX: 0,
            offsetY: 20
          })
        })
      }.width(`${this.cellWidth * 3 + this.baseMargin * 6}lpx`) // 设置整体宽度
      .margin({ top: 30 }) // 设置顶部边距
      MyPrizeUpdate().margin({top:20}) // 插入MyPrizeUpdate组件,并设置其上边距
    }
    .height('100%') // 设置高度为100%
    .width('100%') // 设置宽度为100%
    .backgroundColor("#ffb350") // 设置页面背景颜色
  }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/935779.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

idea 配置 git .gitignore文件配置

.gitignore 内容 .idea/ *.iml target/ *.class *.log .iml在idea项目里面创建一个.gitignore名字的文件&#xff0c;然后把这个文件提交到git上。我一般是放到.idea同级目录。 我遇到了几种情况这个文件配置了但是不生效的情况 第一种 Git的缓存可能会导致配置不生效。尝试…

H.323音视频协议

概述 H.323是国际电信联盟&#xff08;ITU&#xff09;的一个标准协议栈&#xff0c;该协议栈是一个有机的整体&#xff0c;根据功能可以将其分为四类协议&#xff0c;也就是说该协议从系统的总体框架&#xff08;H.323&#xff09;、视频编解码&#xff08;H.263&#xff09;、…

快速掌握Quartz.Net计划任务调度框架,轻松实现定时任务

前言 Quartz.Net是一个开源的作业调度框架&#xff0c;可以用于管理计划任务和定期执行。Quartz.Net提供了丰富的作业计划选项&#xff0c;例如精确或模糊时间表达式、日期和时间限制等。Quartz.Net采用分布式架构&#xff0c;允许在多个计算机上运行任务。 Quartz.Net架构设…

【开源大屏】玩转开源积木BI,从0到1设计一个大屏

积木 BI 重磅推出免费大屏设计器&#xff01;功能超强大&#xff0c;操作超流畅&#xff0c;体验超酷炫。快来体验一下吧。 让我们一起来看一下如何从0到1设计一个大屏。 一、积木BI大屏介绍 积木BI可视化数据大屏 是一站式数据可视化展示平台&#xff0c;旨在帮助用户快速通…

【JVM】JVM基础教程(四)

上一章&#xff1a;【JVM】JVM基础教程&#xff08;三&#xff09;-CSDN博客 目录 自动垃圾回收 方法区的回收 方法区回收条件 手动触发回收 堆回收 如何判断堆上的对象可以回收&#xff1f; 可以给对象引用赋值null&#xff0c;切断引用 引用计数法 循环引用缺点 查…

数据结构(Queue队列)

前言&#xff1a; 在计算机科学中&#xff0c;数据结构是构建高效算法和程序的基础&#xff0c;而队列&#xff08;Queue&#xff09;作为一种经典的线性数据结构&#xff0c;具有重要的地位。与栈&#xff08;Stack&#xff09;不同&#xff0c;队列遵循“先进先出”&#xf…

DevExpress WPF中文教程:Grid - 如何移动和调整列大小?(一)

DevExpress WPF拥有120个控件和库&#xff0c;将帮助您交付满足甚至超出企业需求的高性能业务应用程序。通过DevExpress WPF能创建有着强大互动功能的XAML基础应用程序&#xff0c;这些应用程序专注于当代客户的需求和构建未来新一代支持触摸的解决方案。 无论是Office办公软件…

语言模型(序列模型)

终于快要毕业了&#xff0c;乘着还在还在研究室&#xff0c;把最后一章sequence模型也学完吧。 Sequence Model 一&#xff1a;基础知识1&#xff1a;符号的定义2&#xff1a;词典(Vocabulary) 与编码(Encoding) 二&#xff1a;RNN(Recurrent Neural Networks) 循环神经网络1&…

MySQL(表的约束)

目录 1. 空属性(NULL) 2. 默认值(default) 3. 列描述(comment) 4. zerofill 5. 主键(primary_key) 6. 自增长(auto_increment) 7. 唯一键(uniqie) 8. 外键(foreign key (字段名) references 主表(列) 表的约束&#xff1a;表中有各种类型&#xff0c;每个类型插入的数据…

笔记本电脑升级硬盘存储、Windows10系统安装及后续步骤(以联想ThinkPad X1 Carbon Gen10为例)

文章目录 1.前言2.材料准备3.Win10系统安装盘制作3.1 系统下载3.2 系统启动U盘刻录 4.拆机更换硬盘5.开机启动项修改6.系统安装&#xff08;以Win10为例&#xff09;7.系统安装后可能需要的步骤7.1 缺少WIFI等网络驱动7.2 系统激活7.3 办公软件安装 8.旧硬盘变废为宝参考文献 1…

高效利用资源:分布式有状态服务的高可靠性设计

在分布式系统设计中&#xff0c;实现有状态服务的高可靠性通常采用主备切换的方式。当主服务停止工作时&#xff0c;备服务接管任务&#xff0c;例如通过Keepalive实现VIP的切换以保证可用性。然而&#xff0c;这种方式存在资源浪费的问题&#xff0c;因为备服务始终处于空转状…

解读数据资产管理实践白皮书(5.0版)深入学习掌握数据资产管理知识体系。

本文介绍了数据资产管理的重要性及其概述&#xff0c;详细阐述了数据资产管理的活动职能包括数据模型管理、数据标准管理、数据质量管理等&#xff0c;并强调了数据安全管理的重要性。文章还讨论了数据资产管理的保障措施和实践步骤&#xff0c;以及发展趋势和总结展望。 重点内…

使用IP自签名SSL证书

最近需要创建WebSocket服务器并使用SSL证书&#xff0c;由于是内网测试&#xff0c;所以需要使用指定IP的自签SSL证书。 其实笔者前面博文 使用nexus3作为Docker镜像仓库 解决nexus3登录x509: certificate has expired or is not yet valid 中有创建过相应的证书&#xff0c;这…

location和重定向、代理

location匹配的规则和优先级 在nginx当中&#xff0c;匹配的对象一般是URI来匹配 http://192.168.233.62/usr/local/nginx/html/index.html 182.168.233.61/ location匹配的分类&#xff1a; 多个location一旦匹配其中之一&#xff0c;不在匹配其他location 1、精确匹配 …

Maven学习(传统Jar包管理、Maven依赖管理(导入坐标)、快速下载指定jar包)

目录 一、传统Jar包管理。 &#xff08;1&#xff09;基本介绍。 &#xff08;2&#xff09;传统的Jar包导入方法。 1、手动寻找Jar包。并放置到指定目录下。 2、使用IDEA的库管理功能。 3、配置环境变量。 &#xff08;3&#xff09;传统的Jar包管理缺点。 二、Maven。 &#…

【Java笔记】LinkedList 底层结构

一、LinkedList 的全面说明 LinkedList底层实现了双向链表和双端队列特点可以添加任意元素(元素可以重复)&#xff0c;包括null线程不安全&#xff0c;没有实现同步 二、LinkedList 的底层操作机制 三、LinkedList的增删改查案例 public class LinkedListCRUD { public stati…

Linux中的线程

目录 线程的概念 进程与线程的关系 线程创建 线程终止 线程等待 线程分离 原生线程库 线程局部存储 自己实现线程封装 线程的优缺点 多线程共享与独占资源 线程互斥 互斥锁 自己实现锁的封装 加锁实现互斥的原理 死锁 线程同步 线程的概念 回顾进程相关概念 …

django应用JWT(JSON Web Token)实战

文章目录 一、什么是JWT二、为什么使用JWT三、在django项目中如何应用JWT 1、安装djangorestframework-simplejwt库&#xff1a;2、在settings.py中配置JWT认证&#xff1a;3、在urls.py中配置JWT的获取和刷新路由&#xff1a; 四、JWT如何使用 1、调用生成JWT的接口获取JWT2、…

如何借助5G网关实现油罐车安全在线监测

油罐车是常见的特种运输车辆&#xff0c;用以运送各种汽油、柴油、原油等油品&#xff0c;运输危险系数大&#xff0c;而且由于油罐车需要经常行驶在城区道路&#xff0c;为城市各个加油站点、企业工厂运输补充所需油料&#xff0c;因此也是危化品运输车辆的重点监测和管控对象…

EXCEL数据清洗的几个功能总结备忘

目录 0 参考教材 1 用EXCEL进行数据清洗的几个功能 2 删除重复值&#xff1a; 3 找到缺失值等 4 大小写转换 5 类型转化 6 识别空格 0 参考教材 精通EXCEL数据统计与分析&#xff0c;中国&#xff0c;李宗璋用EXCEL学统计学&#xff0c;日EXCEL统计分析与决策&#x…