鸿蒙NEXT开发案例:随机数生成

zhongcx
发布于 2024-12-1 10:19
浏览
0收藏

鸿蒙NEXT开发案例:随机数生成-鸿蒙开发者社区

【引言】
本项目是一个简单的随机数生成器应用,用户可以通过设置随机数的范围和个数,并选择是否允许生成重复的随机数,来生成所需的随机数列表。生成的结果可以通过点击“复制”按钮复制到剪贴板。
【环境准备】
• 操作系统:Windows 10
• 开发工具:DevEco Studio NEXT Beta1 Build Version: 5.0.3.806
• 目标设备:华为Mate60 Pro
• 开发语言:ArkTS
• 框架:ArkUI
• API版本:API 12
【关键技术点】

  1. 用户界面设计
    用户界面主要包括以下几个部分:
    • 标题栏:显示应用名称。
    • 输入框:用户可以输入随机数的起始值、结束值和生成个数。
    • 开关:用户可以选择生成的随机数是否允许重复。
    • 生成按钮:点击后生成随机数。
    • 结果显示区:显示生成的随机数,并提供复制功能。
  2. 随机数生成算法
    随机数生成是本项目的重点。根据用户是否允许生成重复的随机数,算法分为两种情况:
    2.1 不允许重复
    当用户选择不允许生成重复的随机数时,程序使用一个 Set 来存储生成的随机数,利用 Set 的特性自动去重。具体步骤如下:
    1)计算范围:计算用户指定的随机数范围 range = endValue - startValue + 1。
    2)生成随机数:使用一个临时数组 tempArray 来辅助生成不重复的随机数。每次生成一个随机索引 randomIndex,从 tempArray 中取出或计算一个新的随机数 randomNum,并将其添加到 Set 中。
    3)更新临时数组:将 tempArray 中末尾的元素移动到随机位置,以确保下次生成的随机数仍然是唯一的。
if (!this.isUnique) {
  if (countValue > range) {
    // 显示错误提示
    this.getUIContext().showAlertDialog({
      title: '错误提示',
      message: `请求的随机数数量超过了范围内的总数`,
      confirm: {
        defaultFocus: true,
        value: '我知道了',
        fontColor: Color.White,
        backgroundColor: this.primaryColor,
        action: () => {}
      },
      onWillDismiss: () => {},
      alignment: DialogAlignment.Center,
    });
    return;
  }
  for (let i = 0; i < countValue; i++) {
    let randomIndex = Math.floor(Math.random() * (range - i));
    let randomNum = 0;
    if (tempArray[randomIndex] !== undefined) {
      randomNum = tempArray[randomIndex];
    } else {
      randomNum = startValue + randomIndex;
    }
    generatedNumbers.add(randomNum);
    if (tempArray[range - 1 - i] === undefined) {
      tempArray[range - 1 - i] = startValue + range - 1 - i;
    }
    tempArray[randomIndex] = tempArray[range - 1 - i];
  }
  this.generatedNumbers = JSON.stringify(Array.from(generatedNumbers));
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.

2.2 允许重复
当用户选择允许生成重复的随机数时,程序直接在指定范围内生成随机数,并将其添加到结果数组中。

else {
  for (let i = 0; i < this.countValue; i++) {
    let randomNumber = this.startValue + Math.floor(Math.random() * (this.endValue - this.startValue));
    tempArray.push(randomNumber);
  }
  this.generatedNumbers = JSON.stringify(tempArray);
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  1. 剪贴板功能
    为了方便用户使用,程序提供了将生成的随机数复制到剪贴板的功能。具体实现如下:
private copyToClipboard(text: string): void {
  const pasteboardData = pasteboard.createData(pasteboard.MIMETYPE_TEXT_PLAIN, text);
  const systemPasteboard = pasteboard.getSystemPasteboard();
  systemPasteboard.setData(pasteboardData);
  promptAction.showToast({ message: '已复制' });
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.

【完整代码】

// 导入剪贴板服务模块,用于后续实现复制功能
import { pasteboard } from '@kit.BasicServicesKit';
// 导入用于显示提示信息的服务
import { promptAction } from '@kit.ArkUI';

// 使用装饰器定义一个入口组件,这是应用的主界面
@Entry
@Component
struct RandomNumberGenerator {
  // 定义基础间距,用于布局中的间距设置
  @State private baseSpacing: number = 30;
  // 存储生成的随机数字符串
  @State private generatedNumbers: string = '';
  // 应用的主题色
  @State private primaryColor: string = '#fea024';
  // 文本的颜色
  @State private fontColor: string = "#2e2e2e";
  // 输入框是否获取焦点的状态变量
  @State private isFocusStart: boolean = false;
  @State private isFocusEnd: boolean = false;
  @State private isFocusCount: boolean = false;
  // 是否允许生成的随机数重复
  @State private isUnique: boolean = true;
  // 随机数生成的起始值
  @State private startValue: number = 0;
  // 随机数生成的结束值
  @State private endValue: number = 0;
  // 要生成的随机数个数
  @State private countValue: number = 0;

  // 生成随机数的方法
  private generateRandomNumbers(): void {
    const startValue = this.startValue; // 获取当前设定的起始值
    const endValue = this.endValue; // 获取当前设定的结束值
    const countValue = this.countValue; // 获取当前设定的生成个数
    const range: number = endValue - startValue + 1; // 计算生成范围


    // 用于存储生成的随机数
    const generatedNumbers = new Set<number>(); // 使用Set来自动去重
    const tempArray: number[] = []; // 临时数组,用于辅助生成不重复的随机数

    // 如果不允许重复,则使用去重算法生成随机数
    if (!this.isUnique) {
      // 如果请求的随机数数量超过了范围内的总数,则显示错误提示
      if (countValue > range) {
        this.getUIContext().showAlertDialog({
          title: '错误提示',
          message: `请求的随机数数量超过了范围内的总数`,
          confirm: {
            defaultFocus: true,
            value: '我知道了',
            fontColor: Color.White,
            backgroundColor: this.primaryColor,
            action: () => {} // 点击确认后的回调
          },
          onWillDismiss: () => {}, // 对话框即将关闭时的回调
          alignment: DialogAlignment.Center, // 对话框的对齐方式
        });
        return;
      }

      for (let i = 0; i < countValue; i++) {
        let randomIndex = Math.floor(Math.random() * (range - i)); // 在剩余范围内选择一个随机索引
        let randomNum = 0;
        if (tempArray[randomIndex] !== undefined) { // 如果索引位置已有值,则使用该值
          randomNum = tempArray[randomIndex];
        } else {
          randomNum = startValue + randomIndex; // 否则计算新的随机数
        }
        generatedNumbers.add(randomNum); // 添加到Set中,自动去重
        if (tempArray[range - 1 - i] === undefined) { // 更新末尾元素的位置
          tempArray[range - 1 - i] = startValue + range - 1 - i;
        }
        tempArray[randomIndex] = tempArray[range - 1 - i]; // 将末尾元素移到随机位置
      }
      // 将生成的随机数转换成JSON格式的字符串
      this.generatedNumbers = JSON.stringify(Array.from(generatedNumbers));
    } else {
      // 如果允许重复,则直接生成随机数
      for (let i = 0; i < this.countValue; i++) {
        let randomNumber = this.startValue + Math.floor(Math.random() * (this.endValue - this.startValue));
        tempArray.push(randomNumber);
      }
      // 将生成的随机数转换成JSON格式的字符串
      this.generatedNumbers = JSON.stringify(tempArray);
    }
  }

  // 将生成的随机数复制到剪贴板的方法
  private copyToClipboard(text: string): void {
    const pasteboardData = pasteboard.createData(pasteboard.MIMETYPE_TEXT_PLAIN, text); // 创建剪贴板数据
    const systemPasteboard = pasteboard.getSystemPasteboard(); // 获取系统剪贴板
    systemPasteboard.setData(pasteboardData); // 设置剪贴板数据
    // 显示复制成功的提示信息
    promptAction.showToast({ message: '已复制' });
  }

  // 构建页面布局的方法
  build() {
    Column() {
      // 标题栏,展示应用名
      Text("随机数生成")
        .width('100%') // 设置宽度为100%
        .height(54) // 设置高度为54
        .fontSize(18) // 设置字体大小为18
        .fontWeight(600) // 设置字体粗细为600
        .backgroundColor(Color.White) // 设置背景颜色为白色
        .textAlign(TextAlign.Center) // 设置文本居中对齐
        .fontColor(this.fontColor); // 设置文本颜色

      // 随机数范围设置区域
      Column() {
        Row() {
          Text(`随机数范围`)
            .fontWeight(600) // 设置字体粗细为600
            .fontSize(18) // 设置字体大小为18
            .fontColor(this.fontColor); // 设置文本颜色
        }
        .margin({ top: `${this.baseSpacing}lpx`, left: `${this.baseSpacing}lpx` }); // 设置边距

        // 输入随机数范围的两个值
        Row() {
          TextInput({ placeholder: '开始(>=)' }) // 输入框,显示占位符
            .layoutWeight(1) // 设置布局权重为1
            .type(InputType.Number) // 设置输入类型为数字
            .placeholderColor(this.isFocusStart ? this.primaryColor : Color.Gray) // 设置占位符颜色
            .fontColor(this.isFocusStart ? this.primaryColor : this.fontColor) // 设置文本颜色
            .borderColor(this.isFocusStart ? this.primaryColor : Color.Gray) // 设置边框颜色
            .borderWidth(1) // 设置边框宽度
            .borderRadius(10) // 设置圆角半径
            .backgroundColor(Color.White) // 设置背景颜色
            .showUnderline(false) // 不显示下划线
            .onBlur(() => this.isFocusStart = false) // 输入框失去焦点时的处理
            .onFocus(() => this.isFocusStart = true) // 输入框获得焦点时的处理
            .onChange((value: string) => this.startValue = Number(value)); // 输入值变化时的处理

          // 分隔符
          Line().width(10) // 设置分隔符宽度

          TextInput({ placeholder: '结束(<=)' }) // 输入框,显示占位符
            .layoutWeight(1) // 设置布局权重为1
            .type(InputType.Number) // 设置输入类型为数字
            .placeholderColor(this.isFocusEnd ? this.primaryColor : Color.Gray) // 设置占位符颜色
            .fontColor(this.isFocusEnd ? this.primaryColor : this.fontColor) // 设置文本颜色
            .borderColor(this.isFocusEnd ? this.primaryColor : Color.Gray) // 设置边框颜色
            .borderWidth(1) // 设置边框宽度
            .borderRadius(10) // 设置圆角半径
            .backgroundColor(Color.White) // 设置背景颜色
            .showUnderline(false) // 不显示下划线
            .onBlur(() => this.isFocusEnd = false) // 输入框失去焦点时的处理
            .onFocus(() => this.isFocusEnd = true) // 输入框获得焦点时的处理
            .onChange((value: string) => this.endValue = Number(value)); // 输入值变化时的处理
        }
        .margin({
          left: `${this.baseSpacing}lpx`, // 左边距
          right: `${this.baseSpacing}lpx`, // 右边距
          top: `${this.baseSpacing}lpx`, // 上边距
        });

        // 输入生成随机数的个数
        Text('生成随机数个数')
          .fontWeight(600) // 设置字体粗细为600
          .fontSize(18) // 设置字体大小为18
          .fontColor(this.fontColor) // 设置文本颜色
          .margin({ left: `${this.baseSpacing}lpx`, top: `${this.baseSpacing}lpx` }); // 设置边距

        Row() {
          TextInput({ placeholder: '' }) // 输入框,显示占位符
            .layoutWeight(1) // 设置布局权重为1
            .type(InputType.Number) // 设置输入类型为数字
            .placeholderColor(this.isFocusCount ? this.primaryColor : Color.Gray) // 设置占位符颜色
            .fontColor(this.isFocusCount ? this.primaryColor : this.fontColor) // 设置文本颜色
            .borderColor(this.isFocusCount ? this.primaryColor : Color.Gray) // 设置边框颜色
            .borderWidth(1) // 设置边框宽度
            .borderRadius(10) // 设置圆角半径
            .backgroundColor(Color.White) // 设置背景颜色
            .showUnderline(false) // 不显示下划线
            .onBlur(() => this.isFocusCount = false) // 输入框失去焦点时的处理
            .onFocus(() => this.isFocusCount = true) // 输入框获得焦点时的处理
            .onChange((value: string) => this.countValue = Number(value)); // 输入值变化时的处理
        }
        .margin({
          left: `${this.baseSpacing}lpx`, // 左边距
          right: `${this.baseSpacing}lpx`, // 右边距
          top: `${this.baseSpacing}lpx`, // 上边距
        });

        // 设置数字是否可重复的开关
        Row() {
          Text('数字是否可重复')
            .fontWeight(400) // 设置字体粗细为400
            .fontSize(16) // 设置字体大小为16
            .fontColor(this.fontColor) // 设置文本颜色
            .layoutWeight(1); // 设置布局权重为1

          Toggle({ type: ToggleType.Checkbox, isOn: this.isUnique }) // 切换按钮
            .width('100lpx') // 设置宽度
            .height('50lpx') // 设置高度
            .borderColor(Color.Gray) // 设置边框颜色
            .selectedColor(this.primaryColor) // 设置选中时的颜色
            .onChange((isOn: boolean) => this.isUnique = isOn) // 切换状态变化时的处理
            .align(Alignment.End); // 设置对齐方式为右对齐
        }
        .margin({
          top: `${this.baseSpacing}lpx`, // 上边距
        })
        .width('100%') // 设置宽度为100%
        .padding({
          left: `${this.baseSpacing}lpx`, // 左内边距
          right: `${this.baseSpacing}lpx`, // 右内边距
          top: `${this.baseSpacing / 3}lpx`, // 上内边距
        })
        .hitTestBehavior(HitTestMode.Block) // 设置点击测试行为
        .onClick(() => this.isUnique = !this.isUnique); // 点击时切换状态

        // 生成随机数的按钮
        Text('开始生成')
          .fontColor(Color.White) // 设置文本颜色为白色
          .backgroundColor(this.primaryColor) // 设置背景颜色为主题色
          .height(54) // 设置高度为54
          .textAlign(TextAlign.Center) // 设置文本居中对齐
          .borderRadius(10) // 设置圆角半径
          .fontSize(18) // 设置字体大小为18
          .width(`${650 - this.baseSpacing * 2}lpx`) // 设置宽度
          .margin({
            top: `${this.baseSpacing}lpx`, // 上边距
            left: `${this.baseSpacing}lpx`, // 左边距
            right: `${this.baseSpacing}lpx`, // 右边距
            bottom: `${this.baseSpacing}lpx` // 下边距
          })
          .clickEffect({ level: ClickEffectLevel.HEAVY, scale: 0.8 }) // 设置点击效果
          .onClick(() => this.generateRandomNumbers()); // 点击时生成随机数
      }
      .width('650lpx') // 设置宽度
      .margin({ top: 20 }) // 设置上边距
      .backgroundColor(Color.White) // 设置背景颜色为白色
      .borderRadius(10) // 设置圆角半径
      .alignItems(HorizontalAlign.Start); // 设置水平对齐方式为左对齐

      // 显示生成的随机数
      Column() {
        Text(`生成的随机数为:`)
          .fontWeight(600) // 设置字体粗细为600
          .fontSize(18) // 设置字体大小为18
          .fontColor(this.fontColor) // 设置文本颜色
          .margin({
            top: `${this.baseSpacing}lpx`, // 上边距
            left: `${this.baseSpacing}lpx`, // 左边距
          });

        Text(`${this.generatedNumbers}`) // 显示生成的随机数
          .width('650lpx') // 设置宽度
          .fontColor(this.primaryColor) // 设置文本颜色为主题色
          .fontSize(18) // 设置字体大小为18
          .textAlign(TextAlign.Center) // 设置文本居中对齐
          .padding({ left: 5, right: 5 }) // 设置内边距
          .margin({
            top: `${this.baseSpacing / 3}lpx` // 上边距
          });

        // 复制生成的随机数到剪贴板的按钮
        Text('复制')
          .enabled(this.generatedNumbers ? true : false) // 按钮是否可用
          .fontColor(Color.White) // 设置文本颜色为白色
          .backgroundColor(this.primaryColor) // 设置背景颜色为主题色
          .height(54) // 设置高度为54
          .textAlign(TextAlign.Center) // 设置文本居中对齐
          .borderRadius(10) // 设置圆角半径
          .fontSize(18) // 设置字体大小为18
          .width(`${650 - this.baseSpacing * 2}lpx`) // 设置宽度
          .margin({
            top: `${this.baseSpacing}lpx`, // 上边距
            left: `${this.baseSpacing}lpx`, // 左边距
            right: `${this.baseSpacing}lpx`, // 右边距
            bottom: `${this.baseSpacing}lpx` // 下边距
          })
          .clickEffect({ level: ClickEffectLevel.HEAVY, scale: 0.8 }) // 设置点击效果
          .onClick(() => this.copyToClipboard(this.generatedNumbers)); // 点击时复制随机数
      }
      .width('650lpx') // 设置宽度
      .backgroundColor(Color.White) // 设置背景颜色为白色
      .borderRadius(10) // 设置圆角半径
      .margin({ top: `${this.baseSpacing}lpx` }) // 设置上边距
      .alignItems(HorizontalAlign.Start); // 设置水平对齐方式为左对齐
    }
    .height('100%') // 设置高度为100%
    .width('100%') // 设置宽度为100%
    .backgroundColor("#f2f3f5"); // 设置背景颜色
  }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.
  • 96.
  • 97.
  • 98.
  • 99.
  • 100.
  • 101.
  • 102.
  • 103.
  • 104.
  • 105.
  • 106.
  • 107.
  • 108.
  • 109.
  • 110.
  • 111.
  • 112.
  • 113.
  • 114.
  • 115.
  • 116.
  • 117.
  • 118.
  • 119.
  • 120.
  • 121.
  • 122.
  • 123.
  • 124.
  • 125.
  • 126.
  • 127.
  • 128.
  • 129.
  • 130.
  • 131.
  • 132.
  • 133.
  • 134.
  • 135.
  • 136.
  • 137.
  • 138.
  • 139.
  • 140.
  • 141.
  • 142.
  • 143.
  • 144.
  • 145.
  • 146.
  • 147.
  • 148.
  • 149.
  • 150.
  • 151.
  • 152.
  • 153.
  • 154.
  • 155.
  • 156.
  • 157.
  • 158.
  • 159.
  • 160.
  • 161.
  • 162.
  • 163.
  • 164.
  • 165.
  • 166.
  • 167.
  • 168.
  • 169.
  • 170.
  • 171.
  • 172.
  • 173.
  • 174.
  • 175.
  • 176.
  • 177.
  • 178.
  • 179.
  • 180.
  • 181.
  • 182.
  • 183.
  • 184.
  • 185.
  • 186.
  • 187.
  • 188.
  • 189.
  • 190.
  • 191.
  • 192.
  • 193.
  • 194.
  • 195.
  • 196.
  • 197.
  • 198.
  • 199.
  • 200.
  • 201.
  • 202.
  • 203.
  • 204.
  • 205.
  • 206.
  • 207.
  • 208.
  • 209.
  • 210.
  • 211.
  • 212.
  • 213.
  • 214.
  • 215.
  • 216.
  • 217.
  • 218.
  • 219.
  • 220.
  • 221.
  • 222.
  • 223.
  • 224.
  • 225.
  • 226.
  • 227.
  • 228.
  • 229.
  • 230.
  • 231.
  • 232.
  • 233.
  • 234.
  • 235.
  • 236.
  • 237.
  • 238.
  • 239.
  • 240.
  • 241.
  • 242.
  • 243.
  • 244.
  • 245.
  • 246.
  • 247.
  • 248.
  • 249.
  • 250.
  • 251.
  • 252.
  • 253.
  • 254.
  • 255.
  • 256.
  • 257.
  • 258.
  • 259.
  • 260.
  • 261.
  • 262.
  • 263.
  • 264.
  • 265.
  • 266.
  • 267.
  • 268.
  • 269.
  • 270.
  • 271.
  • 272.
  • 273.
  • 274.
  • 275.
  • 276.
  • 277.
  • 278.
  • 279.
  • 280.
  • 281.
  • 282.
  • 283.
  • 284.
  • 285.
  • 286.
  • 287.
  • 288.
  • 289.
  • 290.
  • 291.

分类
标签
收藏
回复
举报


回复