看到論壇裏又有挑戰賽了,前來交個作業。
【創意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;