【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;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章