鴻蒙NEXT開發案例:隨機數生成

zhongcx發表於2024-11-20

【引言】

本專案是一個簡單的隨機數生成器應用,使用者可以透過設定隨機數的範圍和個數,並選擇是否允許生成重複的隨機數,來生成所需的隨機數列表。生成的結果可以透過點選“複製”按鈕複製到剪貼簿。

【環境準備】

• 作業系統: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));
}

3. 剪貼簿功能

為了方便使用者使用,程式提供了將生成的隨機數複製到剪貼簿的功能。具體實現如下:

private copyToClipboard(text: string): void {
  const pasteboardData = pasteboard.createData(pasteboard.MIMETYPE_TEXT_PLAIN, text);
  const systemPasteboard = pasteboard.getSystemPasteboard();
  systemPasteboard.setData(pasteboardData);
  promptAction.showToast({ message: '已複製' });
}

【完整程式碼】

// 匯入剪貼簿服務模組,用於後續實現複製功能
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"); // 設定背景顏色
  }
}

  

相關文章