【HarmonyOS第一課:運動健康實戰】運動特種兵開發實踐

看到論壇裏又有挑戰賽了,前來交個作業。 

【創意Demo挑戰賽】HarmonyOS運動健康實戰——等你來戰!-華爲開發者論壇 | 華爲開發者聯盟 (huawei.com)

設計理念來源於最近很火的特種兵旅遊。基於"特種 兵不怕挑戰"的理念,設計了運動的app。

首頁在設計上分爲上下兩塊。

上面是特色的每日挑戰:

用做任務的形式來運動打卡,由服務器每天刷新一個健身任務發給用戶,每個任務都有三種難度可以選擇,用戶根據自己的實際能力選擇一個難度完成打卡,系統會根據每個人完成的難度和次數進行每月的排名【注排名功能未開發、僅演示效果】。

        由於用戶不知道明天會接到什麼任務,可以增加一些運動的趣味性和參與感。

下方是比較傳統的自選項目鍛鍊:

方便有具體需求的用戶安排自己的訓練。


第二屏用來展示用戶的所有訓練記錄,無論是挑戰、還是日常運動,挑戰任務是否達標,總之所有進行過的運動數據都會存入本地數據庫,並按照運動的時間依次排序展示,方便用戶查看。

第三屏說個人信息界面,不過時間有限,具體功能都沒做,只是界面展示。

下面展示一下已經開發完成的功能:

可跳過的啓動頁,以便第一次打開時先在後臺加載數據,以免用戶進入主頁時發現一片空白。

可切換查看不同日期和難度的挑戰任務

每日挑戰的打卡功能。

爲了方便演示功能,做了一個演示用的輸入器,可以手動錄入本次運動的運動量、熱量消耗、具體時間。

輸入器可以識別測試人員點擊的運動項目,以正確寫入數據庫。

進行每日挑戰時,如果運動量達標自動判定爲“當日運動已達標”,修改按鈕樣式,同時往數據庫寫入本次的運動信息。如果運動量不達標則僅往數據庫寫入運動信息,不改變UI界面,用戶可以再次發起挑戰。

用戶自選運動的打卡測試。

輸入器可以識別具體的運動項目,並在相關單位上做調整。

所有的數據存入數據庫,可在第二屏查看。

加上了一個簡單的滑動彈性效果。

最後是一個簡單的個人信息頁,由於時間有限,功能都沒開發,只是靜態展示。

遇到的困難:

  如何製作能倒計時的啓動頁、

  如何讓app在啓動時先進入啓動頁而不是Index頁、

  如何構建本地數據庫,並順利讀寫數據、

  組件間的數據同步、

  改變Array<自定義數據類型>的值無法觸發UI刷新

部分解決方案:

  • 改動頁面的啓動順序

需要在EntryAbility中進行修改。windowStage.loadContent函數的第一個值是最優先被啓動的頁面。

  onWindowStageCreate(windowStage: window.WindowStage) {
    // Main window is created, set main page for this ability
    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate');

    windowStage.loadContent('pages/SplashPage', (err, data) => {
      if (err.code) {
        hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
        return;
      }
      hilog.info(0x0000, 'testTag', 'Succeeded in loading the content. Data: %{public}s', JSON.stringify(data) ?? '');
    });
  }複製
  • 改變Array<自定義數據類型>的值無法觸發UI刷新

前期嘗試對Array<自定義數據類型>中某一項的具體屬性做修改時,能檢測到被修改後的數值,但是始終無法觸發UI刷新。

反覆嘗試研究幾天後終於找到了原因所在。

”自定義數據類型”其實跟常規的“number“、”string“等數據類型一樣,都是一個完整且獨立存在的數據類型。可以認爲Array<自定義數據類型>是由多個<自定義數據類型>所組成的一維數組,而不是由“自定義數據類型”的一堆屬性先組成一個一維數組、一維數組再二次組合形成。

因此需要把<自定義數據類型>作爲一個值整體賦值給Array<自定義數據類型>中的某一項,纔是合理且有效的做法。

舉個栗子:

class ChallengList {//自定義數據類型
  SportsType: string;
  SportsTarget: number;
  SportsUnit: string;
  SportsData:string;
  SportsFinished:number
}

let X:Array<ChallengList>=[new ChallengList('',0,'','',0),new ChallengList('',0,'','',0)]//有效的新建數組
challengInfo[1].SportsFinished=2//無效操作,只改變數組中某一項的某一個屬性
challengInfo[1]=new ChallengList('C',2,'C','C',2)//有效操作,用完整的數據類型去賦值複製
  • 數據庫的建立與存取

demo中的每日挑戰任務,本來應該是去服務器查詢,不過因爲不知道怎麼做,加上這只是個Demo,就決定把任務寫在本地。加上用戶的打卡數據,總共需要兩張表。

參考了健康生活應用(ArkTS) (huawei.com)中的很多理念和思路後。

首先爲兩張數據表分別設計數據類型和讀寫數據表用的接口,在展示啓動頁的時候後臺建立空數據表,並將提前準備好的挑戰任務信息寫入數據庫,當倒計時,進入主頁時再對挑戰任務的數據表進行讀取,取出相關信息展示。

在用戶完成打卡後,把用戶的本次相關信息打包成之前建立好的‘運動類型’的格式,寫入打卡數據表,同時修改挑戰任務數據表中,這一次任務的完成情況。另外重寫主頁的任務信息變量,以觸發刷新UI的“開始調整”按鈕。


export default class ChallengList {
  SportsType: string;
  SportsTarget: number;
  SportsUnit: string;
  SportsData:string;
  SportsFinished:number



  constructor(  SportsType: string,  SportsTarget: number,  SportsUnit: string,  SportsData: string,SportsFinished:number) {
    this.SportsType = SportsType;
    this.SportsTarget = SportsTarget;
    this.SportsUnit = SportsUnit;
    this.SportsData = SportsData;
    this.SportsFinished=SportsFinished

  }
}

複製

export default class SportsHistory {
  SportsType: string;
  SportsTimeLong: number;
  SportsCalorie: number;
  SportsData: string;


  constructor(  SportsType: string,  SportsTimeLong: number, SportsCalorie: number,  SportsData: string) {
    this.SportsType = SportsType;
    this.SportsTimeLong = SportsTimeLong;
    this.SportsCalorie = SportsCalorie;
    this.SportsData = SportsData;

  }
}
複製
class ChallengListApi {
  insertData(challengListInfo: ChallengList): void {
    const valueBucket = generateBucket(challengListInfo);
    RdbUtils.insert(Challeng_List.tableName, valueBucket).then(result => {
      console.log('數據寫入成功'+challengListInfo.SportsType)
    })
      .catch(() => {
        console.log('寫入數據失敗')
      })
    Logger.info('challengListInfoTable', 'Insert challengLisrInfoInfo finished.');
  }


  queryList(): Promise<Array<ChallengList>> {
    return new Promise((resolve, reject) => {

      let predicates = new dataRdb.RdbPredicates(Challeng_List.tableName);
      predicates.orderByDesc("id");
      console.log('2')

      RdbUtils.query(predicates).then(resultSet => {
        console.log('1')
        let count = resultSet.rowCount;
        if (count === 0) {
          console.log('3')
          Logger.info('sportHistoryInfoTable', 'query no results.');
          console.log('查詢失敗,沒有數據');
          reject('查詢失敗,沒有數據')
        } else {
          console.log('4')
          resultSet.goToFirstRow();
          let result:Array<ChallengList> = [];
          for (let i = 0; i < count; i++) {
            let tmp = new ChallengList('',0, '','', 0);
            tmp.SportsType = resultSet.getString(resultSet.getColumnIndex('SportsType'));
            tmp.SportsTarget = resultSet.getDouble(resultSet.getColumnIndex('SportsTarget'));
            tmp.SportsUnit = resultSet.getString(resultSet.getColumnIndex('SportsUnit'));
            tmp.SportsData = resultSet.getString(resultSet.getColumnIndex('SportsData'));
            tmp.SportsFinished = resultSet.getDouble(resultSet.getColumnIndex('SportsFinished'));
            result[i] = tmp;
            resultSet.goToNextRow();
            console.log('5')
          }
          console.log('6'+result[0])
          console.log(result[0].SportsType)
          resolve(result)
        }
      })
      console.log('7')
    })
  }
}


function generateBucket(sportsHistoryInfo: ChallengList): dataRdb.ValuesBucket {
  let valueBucket = {};
  Challeng_List.columns.forEach((item: string) => {
    valueBucket[item] = sportsHistoryInfo[item];
  });
  return valueBucket;
}

let challengListApi = new ChallengListApi();

export default challengListApi as ChallengListApi;複製


class SportsHistoryApi {
  insertData(sportsHistoryInfo: SportsHistory): void {
    const valueBucket = generateBucket(sportsHistoryInfo);
    RdbUtils.insert(Sports_History.tableName, valueBucket).then(result => {

      console.log('數據寫入成功')
    })
      .catch((rej) => {
        console.log('寫入數據失敗')
      })
    Logger.info('sportsHistoryInfoTable', 'Insert sportsHistoryInfo finished.');
  }
  
  queryList(): Promise<Array<SportsHistory>> {
    return new Promise((resolve, reject) => {

      let predicates = new dataRdb.RdbPredicates(Sports_History.tableName);
      predicates.orderByDesc("id");
      console.log('2')

      // predicates.in('date', dates);
      RdbUtils.query(predicates).then(resultSet => {
        console.log('1')
        let count = resultSet.rowCount;
        if (count === 0) {
          console.log('3')
          Logger.info('sportHistoryInfoTable', 'query no results.');
          console.log('查詢失敗,沒有數據');
          reject('查詢失敗,沒有數據')
          //  callback([]);
        } else {
          console.log('4')
          resultSet.goToFirstRow();
          let result:Array<SportsHistory> = [];
          for (let i = 0; i < count; i++) {
            let tmp = new SportsHistory('', 0, 0, '')//'', 0,'', 0, '');
            tmp.SportsType = resultSet.getString(resultSet.getColumnIndex('SportsType'));
            tmp.SportsTimeLong = resultSet.getDouble(resultSet.getColumnIndex('SportsTimeLong'));
            tmp.SportsCalorie = resultSet.getDouble(resultSet.getColumnIndex('SportsCalorie'));
            tmp.SportsData = resultSet.getString(resultSet.getColumnIndex('SportsData'));
            result[i] = tmp;
            resultSet.goToNextRow();
            console.log('5')
          }
          console.log('6'+result[0])
          console.log(result[0].SportsType)
          resolve(result)
        }
      })
      console.log('7')
    })
  }
}



function generateBucket(sportsHistoryInfo: SportsHistory): dataRdb.ValuesBucket {
  let valueBucket = {};
  Sports_History.columns.forEach((item: string) => {
    valueBucket[item] = sportsHistoryInfo[item];
  });
  return valueBucket;
}

let sportsHistoryApi = new SportsHistoryApi();

export default sportsHistoryApi as SportsHistoryApi;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章