HarmonyOS 3.1上實現「遊戲萬能卡片」

51cto 發佈 2023-06-01T15:24:04.406620+00:00

舒爾特方格遊戲,是注意力訓練方法之一,可以幫助孩子糾正上課分心走神、回家做作業拖拉毛病,但不能貪玩哦,玩多了,對眼睛,視力不好。①消息通知欄,通知用戶當前最優成績,也就是當前最快時間。

舒爾特方格遊戲,是注意力訓練方法之一,可以幫助孩子糾正上課分心走神、回家做作業拖拉毛病,但不能貪玩哦,玩多了,對眼睛,視力不好。

①消息通知欄,通知用戶當前最優成績,也就是當前最快時間。

②元服務卡片,在桌面上添加 2x2 或 2x4 或 2x4 規格元服務卡片,能看到不同布局隨機數字,根據左上角紅色字提示,快速完成點擊,用時最少為最優成績。

③1x2 規格元服務卡片,只顯示當前最優成績,點擊可以查看 2x2 或 2x4 或 2x4 規格元服務卡片最快用時遊戲記錄。

關係型資料庫,用於查詢,添加,更新,刪除元服務卡片信息和各卡片遊戲用時成績數據。

效果圖如下:

知識點

消息通知:提供通知管理的能力,包括發布、取消發布通知,創建、獲取、移除通知通道,訂閱、取消訂閱通知,獲取通知的使能狀態、角標使能狀態,獲取通知的相關信息等。

關係型資料庫:關係型資料庫基於 SQLite 組件提供了一套完整的對本地資料庫進行管理的機制,對外提供了一系列的增、刪、改、查等接口,也可以直接運行用戶輸入的 SQL 語句來滿足複雜的場景需要。

元服務卡片開發:卡片是一種界面展示形式,可以將應用的重要信息或操作前置到卡片,以達到服務直達、減少體驗層級的目的。

卡片提供方:顯示卡片內容,控制卡片布局以及控制項點擊事件。

卡片使用方:顯示卡片內容的宿主應用,控制卡片在宿主中展示的位置。

卡片管理服務:用於管理系統中所添加卡片的常駐代理服務,包括卡片對象的管理與使用,以及卡片周期性刷新等。

軟體要求:

  • DevEco Studio 版本:DevEco Studio 3.1 Release 及以上版本。
  • HarmonyOS SDK 版本:API version 9 及以上版本。

硬體要求:

  • 設備類型:華為手機 3.1 系統或運行在 DevEco Studio 上的遠程模擬器API9。
  • HarmonyOS 系統:3.1.0 Developer Release 及以上版本。

卡片講解

1x2 卡片主要顯示所有卡片最優成績,也就是用時最少的,同時點擊卡片,跳轉到主界面,查看卡片遊戲記錄。


2x2 卡片顯示的是 3x3 布局隨機生成 1~9 數字,正上方標題顯示挑戰成功或失敗提示,左上角紅色字提示下一個要點擊的數字按鈕,右上角顯示當次完成後用時和此卡片用時最少成績。

當此次的用時少於最好用時,挑戰成功,並更新資料庫此卡片記錄,如果此次用時大於最好用時,提示挑戰失敗,不用更新資料庫。

2x4 卡片顯示的是 7x2 布局隨機生成 1~14 數字,顯示內容和遊戲規則與 2x2 卡片一樣。

4x4 卡片顯示的是 6x6 布局隨機生成 1~36 數字,顯示內容和遊戲規則與 2x2 卡片一樣。

首次啟動或點擊 1x2 卡片進入到主界面,主界面顯示各卡片遊戲成績記錄。

通知顯示效果:

代碼講解

資料庫操作後端項目結構圖:

FormData.ets 實體類代碼如下:

export default class FormData {
  // 卡片ID
  formId: string;
  // 距陣數 3x3
  matrixNum: string;
  // 最優成績
  bestScore: number;
  // 總最優成績
  totalBestScore: number;
}


Form.ets 資料庫卡片表如下:

export default class Form {
  // 卡片ID
  formId: string;
  // 卡片名稱
  formName: string;
  // 卡片描述
  dimension: number;
  /**
   * 封裝卡片數據
   * @returns
   */
  toValuesBucket() {
    return {
      'formId': this.formId,
      'formName': this.formName,
      'dimension': this.dimension
    };
  }
}

ScoreData.ets 遊戲記錄成績表如下:

export default class ScoreData {
  // 卡片
  formId: string;
  // 距陣數 3x3
  matrixNum: string;
  // 最優成績
  bestScore: number;
  /**
   * 獲取插入成績記錄數
   * @returns
   */
  toValuesBucket() {
    return {
      'formId': this.formId,
      'matrixNum': this.matrixNum,
      'bestScore': this.bestScore
    };
  }
}

DatabaseUtils.ets 資料庫操作類部分代碼如下:

export class DatabaseUtils {

  /**
   * 創建RDB資料庫
   *
   * @param{context}上下文
   * @return{globalThis.rdbStore}return rdbStore RDB資料庫
   */
  async createRdbStore(context: Context) {
    console.info(CommonConstants.DATABASE_TAG, 'xx DatabaseUtils-createRdbStore 開始...')
    // 如果全局變量rdbStore不存在,創建
    if (!globalThis.rdbStore) {
      console.info(CommonConstants.DATABASE_TAG, 'xx DatabaseUtils-createRdbStore 新創建!')

      await DataRdb.getRdbStore(context, CommonConstants.RDB_STORE_CONFIG)
        .then((rdbStore) => {
          console.info(CommonConstants.DATABASE_TAG, 'xx RDB Store回調')
          if (rdbStore) {
            // 創建卡片表
            rdbStore.executeSql(CommonConstants.CREATE_TABLE_FORM).catch((error) => {
              console.error(CommonConstants.DATABASE_TAG, 'xx DatabaseUtils 創建卡片表失敗:' + JSON.stringify(error))
              Logger.error(CommonConstants.DATABASE_TAG, 'executeSql Form error ' + JSON.stringify(error));
            });
            // 創建成績表
            rdbStore.executeSql(CommonConstants.CREATE_TABLE_SCORE_DATA).catch((error) => {
              console.error(CommonConstants.DATABASE_TAG, 'xx DatabaseUtils 創建成績表失敗:' + JSON.stringify(error))
              Logger.error(CommonConstants.DATABASE_TAG, 'executeSql Sensor error ' + JSON.stringify(error));
            });
            // 存儲RDBStore到全局變量
            globalThis.rdbStore = rdbStore;
            console.info(CommonConstants.DATABASE_TAG, 'xx DatabaseUtils-createRdbStore 創建成功!')
          }
        }).catch((error) => {
          console.error(CommonConstants.DATABASE_TAG, 'xx DatabaseUtils 創建RDB資料庫失敗:' + JSON.stringify(error))
          Logger.error(CommonConstants.DATABASE_TAG, 'createRdbStore error ' + JSON.stringify(error));
        });
    }else {
      console.info(CommonConstants.DATABASE_TAG, 'xx DatabaseUtils-createRdbStore 已經存在!')
    }

    console.info(CommonConstants.DATABASE_TAG, 'xx DatabaseUtils-createRdbStore 結束...')
    return globalThis.rdbStore;
  }

  /**
   * 插入卡片數據。
   *
   * @param{Form}Form表單實體。
   * @param{DataRdb.RdbStore}RDB存儲RDB資料庫。
   * @return返回操作信息。
   */
  insertForm(form: Form, rdbStore: DataRdb.RdbStore) {
    rdbStore.insert(CommonConstants.TABLE_FORM, form.toValuesBucket()).catch((error) => {
      Logger.error(CommonConstants.DATABASE_TAG, 'insertForm error ' + JSON.stringify(error));
    });
  }

    /**
   * 將成績插入資料庫。
   *
   * @param{ScoreData}scoreData。
   * @param{DataRdb.RdbStore}RDB存儲RDB資料庫。
   */
  insertValues(scoreData: ScoreData, rdbStore: DataRdb.RdbStore) {
    rdbStore.insert(CommonConstants.TABLE_SCORE, scoreData.toValuesBucket()).catch((error) => {
      Logger.error(CommonConstants.DATABASE_TAG, 'insertValues error ' + JSON.stringify(error));
    });
  }

    /**
   *  更新成績到資料庫
   * @param scoreData
   * @param rdbStore
   */
  updateValues(scoreData: ScoreData, rdbStore: DataRdb.RdbStore) {}

  /**
   * 刪除卡片數據。
   *
   * @param{string}formId表單ID。
   * @param{DataRdb.RdbStore}RDB存儲RDB資料庫。
   */
  deleteFormData(formId: string, rdbStore: DataRdb.RdbStore) {}

  /**
   * 更新卡片
   *
   * @param{DataRdb.RdbStore}RDB存儲RDB資料庫。
   */
  updateForms(rdbStore: DataRdb.RdbStore) {}

  /**
   * 發送通知
   *
   * @param{string}Steps顯示的值步數。
   */
  async sendNotifications(score: number) {}

}

卡片前端項目結構圖:

EntryAbility.ets 程序入口初始化資料庫代碼如下:

onCreate(want, launchParam) {
    // 資料庫初始化
    globalThis.abilityWant = want;
    globalThis.abilityParam = launchParam;
    console.info(CommonConstants.ENTRY_ABILITY_TAG, 'xx onCreate 創建RDB資料庫')
    // 創建RDB資料庫
    DatabaseUtils.createRdbStore(this.context).then((rdbStore) => {
        console.info(CommonConstants.ENTRY_ABILITY_TAG, 'xx onCreate RDB成功')
    }).catch((error) => {
        console.error(CommonConstants.ENTRY_ABILITY_TAG, 'xx onCreate 創建資料庫失敗:' + JSON.stringify(error))
        Logger.error(CommonConstants.ENTRY_ABILITY_TAG, 'onCreate rdb error ' + JSON.stringify(error));
    });
}

EntryFormAbility.ets 卡片生命周期代碼如下:

onAddForm(want) {
    // 獲取卡片ID:ohos.extra.param.key.form_identity
    let formId: string = want.parameters[CommonConstants.FORM_PARAM_IDENTITY_KEY] as string;
    // 獲取卡片名稱:ohos.extra.param.key.form_name
    let formName: string = want.parameters[CommonConstants.FORM_PARAM_NAME_KEY] as string;
    // 獲取卡片規格:ohos.extra.param.key.form_dimension
    let dimensionFlag: number = want.parameters[CommonConstants.FORM_PARAM_DIMENSION_KEY] as number;

    console.info(CommonConstants.ENTRY_FORM_ABILITY_TAG, `xx 添加卡片是:${formId} ${dimensionFlag} ${dimensionFlag}`)
    DatabaseUtils.createRdbStore(this.context).then((rdbStore) => {
      // 卡片信息
      let form: Form = new Form();
      form.formId = formId;
      form.formName = formName;
      form.dimension = dimensionFlag;
      console.info(CommonConstants.ENTRY_FORM_ABILITY_TAG, 'xx onAddForm 新增卡片信息:' + JSON.stringify(form))
      // 保存卡片信息到資料庫
      DatabaseUtils.insertForm(form, rdbStore);
      // 獲取最優成績
      getBestScore(rdbStore, dimensionFlag, formId);
    }).catch((error) => {
      console.error(CommonConstants.ENTRY_FORM_ABILITY_TAG, 'xx onAddForm 添加卡片失敗:' + JSON.stringify(error))
      Logger.error(CommonConstants.ENTRY_FORM_ABILITY_TAG, 'onAddForm rdb error ' + JSON.stringify(error));
    });

    // 每五分鐘刷新一次
    formProvider.setFormNextRefreshTime(formId, CommonConstants.FORM_NEXT_REFRESH_TIME, (error, data) => {
      if (error) {
        console.error(CommonConstants.ENTRY_FORM_ABILITY_TAG, 'xx onAddForm 更新卡片失敗:' + JSON.stringify(error))
        Logger.error(CommonConstants.ENTRY_FORM_ABILITY_TAG, 'refreshTime, error:' + JSON.stringify(error));
      } else {
        console.info(CommonConstants.ENTRY_FORM_ABILITY_TAG, 'xx onAddForm 更新卡片成功')
        Logger.info(CommonConstants.ENTRY_FORM_ABILITY_TAG, 'refreshTime success ' + JSON.stringify(data));
      }
    });

    // 返回初始化卡片數據
    let formData: FormData = new FormData();
    formData.formId = formId;
    formData.bestScore = 0;
    formData.matrixNum = '1x1';
    formData.totalBestScore = 0;
    return formBindingData.createFormBindingData(formData);
  }


卡片頁面部分代碼,這裡就顯示 2x2 卡片代碼如下:

build() {
    Column(){
      Text(this.message)
        .width('100%')
        .fontSize(12)
        .textAlign(TextAlign.Center)
        .fontWeight(700)
        .margin({top: 6, bottom: 6})
      Row(){
        Text(`下一個:${this.flagNum == 0 ? 1 : this.flagNum}`)
          .fontSize(10).fontWeight(400)
          .fontColor(Color.Red)
        Row(){
          Text(`此次:${this.currentScore}`)
            .fontSize(10).fontWeight(400)
          Text(`最好:${this.bestScore}`)
            .fontSize(10).fontWeight(400)
        }
      }
      .width('100%')
      .padding({left: 10, right: 10})
      .alignItems(VerticalAlign.Center)
      .justifyContent(FlexAlign.SpaceBetween)

      Flex({justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center, wrap: FlexWrap.Wrap}){
        // 循環顯示數字按鈕
        ForEach(this.numArray, (day: string) => {
          Button(day, { type: ButtonType.Circle, stateEffect: true })
            .width(40)
            .height(40)
            .padding(1)
            .margin(4)
            .fontSize(12)
            .backgroundColor(Color.Gray)
            .stateStyles({
              normal: this.normalStyles,
              pressed: this.pressedStyles
            })
            .onClick(() => { this.startGame(Number(day)) })
        }, day => day)
      }
      .width('100%')
      .height('100%')
      .padding({ top: 2, left: 5, right: 5 })
    }
    .width('100%')
    .height('100%')
  }

總結

通過開發這個小遊戲元服務,學習到不少知識,其實我有嘗試過把資料庫操作類寫到動態共享包里,這樣元服務打包後不就更小了,然而啟動後白屏了,進步問題,等華為相關技術人員回復,想學習動態共享包的,可以參考關係型資料庫-動態共享包開發​​​。

總結這個項目用到以下知識點:

  • 使用 notification 發布通知。
  • 使用關係型資料庫插入、更新、刪除卡片數據。
  • 使用 FormExtensionAbility 創建、更新、刪除元服務卡片。

作者:狼哥Army

關鍵字: