#HarmonyOS NEXT体验官# 体验HarmonyOS开发流程,开发一个记步app(三) 原创

RandomBoolean
发布于 2024-8-21 18:22
浏览
0收藏

上一篇都是对首页的介绍和展示,接下来介绍记步详情页面。

详情页面

首页的核心是使用位置服务LocationKit和AbilityKit、SensorServiceKit、LocalizationKit。

AbilityKit

  requestPermissions(): void {
    let atManager = abilityAccessCtrl.createAtManager();
    try {
      atManager.requestPermissionsFromUser(this.context, CommonConstants.REQUEST_PERMISSIONS).then((data) => {
        if (data.authResults[0] !== 0 || data.authResults[1] !== 0) {
          return;
        }
        const that = this;
        try {
          sensor.on(sensor.SensorId.PEDOMETER, this.sensorCallback, { interval: CommonConstants.SENSOR_INTERVAL });

        } catch (err) {
          console.error('On fail, errCode: ' + JSON.stringify(err));
        }

        LocationUtil.geolocationOn(this.getGeolocation);
      }).catch((err: Error) => {
        Logger.error(TAG, 'requestPermissionsFromUser err' + JSON.stringify(err));
      })
    } catch (err) {
      Logger.error(TAG, 'requestPermissionsFromUser err' + JSON.stringify(err));
    }
  }

SensorServiceKit

页面隐藏的时候,取消监听。

  onPageHide() {
    sensor.off(sensor.SensorId.PEDOMETER);

    emitter.emit("refresh")
  }

LocationKit

  private getGeolocation: (location: geoLocationManager.Location) => void = (location: geoLocationManager.Location) => {
    if (this.latitude === location.latitude && this.longitude === location.longitude) {
      return;
    }
    this.latitude = location.latitude;
    this.longitude = location.longitude;
    let reverseGeocodeRequest: geoLocationManager.ReverseGeoCodeRequest = {
      'locale': this.locale.toString().includes('zh') ? 'zh' : 'en',
      'latitude': this.latitude,
      'longitude': this.longitude,
      'maxItems': 1
    };
    geoLocationManager.getAddressesFromLocation(reverseGeocodeRequest).then(data => {
      if (data[0].placeName) {
        this.currentLocation = data[0].placeName;
      }
    }).catch((err: Error) => {
      Logger.error(TAG, 'GetAddressesFromLocation err ' + JSON.stringify(err));
    });
  }

LocalizationKit

@State locale: string = new intl.Locale().language;

整体源码如下:

import { abilityAccessCtrl } from '@kit.AbilityKit';
import { common } from '@kit.AbilityKit';
import { geoLocationManager } from '@kit.LocationKit';
import { promptAction, router } from '@kit.ArkUI';
import { sensor } from '@kit.SensorServiceKit';
import { intl } from '@kit.LocalizationKit';
import { BackgroundUtil } from '../common/utils/BackgroundUtil';
import { CompletionStatus } from '../view/CompletionStatus';
import { CommonConstants } from '../common/constant/CommonConstant';
import { CurrentSituation } from '../view/CurrentSituation';
import LocationUtil from '../common/utils/LocationUtil';
import Logger from '../common/utils/Logger';
import NumberUtil from '../common/utils/NumberUtil';
import StepsUtil from '../common/utils/StepsUtil';
import { getRdbStore, saveDB2LocalFile } from '../db/DBUtils';
import { relationalStore } from '@kit.ArkData';
import { taskModel } from '../db/TaskModel';
import { emitter } from '@kit.BasicServicesKit';

const TAG: string = 'HomePage';

@Entry
@Component
struct HomePage {
  @State currentSteps: string = '0';
  @Provide stepGoal: string = '';
  @State oldSteps: string = '';
  @State startPosition: string = '';
  @State currentLocation: string = '';
  @State locale: string = new intl.Locale().language;
  @State latitude: number = 0;
  @State longitude: number = 0;
  @State progressValue: number = 0;
  @State isStart: boolean = false;

  @Provide store: relationalStore.RdbStore | undefined = undefined
  private params: ESObject = router.getParams();
  private taskItem: ESObject = this.params?.taskItem

  private context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext;
  private sensorCallback: (data: sensor.PedometerResponse) => void = (data: sensor.PedometerResponse) => {
    try {
      if (this.isStart) {
        if (StepsUtil.checkStrIsEmpty(this.oldSteps)) {
          this.oldSteps = data.steps - this.taskItem.type < 0 ? "0" : String(data.steps - this.taskItem.type) ;
          StepsUtil.putStorageValue(CommonConstants.OLD_STEPS, this.oldSteps);
        } else {
          this.currentSteps = (data.steps - NumberUtil._parseInt(this.oldSteps, 10)).toString();
        }
      } else {
        this.currentSteps = String(this.taskItem.type);
      }

      if (StepsUtil.checkStrIsEmpty(this.stepGoal) || !this.isStart) {
        return;
      }
      StepsUtil.putStorageValue(CommonConstants.CURRENT_STEPS, this.currentSteps);
      this.progressValue = StepsUtil.getProgressValue(NumberUtil._parseInt(this.stepGoal, 10),
        NumberUtil._parseInt(this.currentSteps, 10)) >= 100 ? 100 : StepsUtil.getProgressValue(NumberUtil._parseInt(this.stepGoal, 10),
        NumberUtil._parseInt(this.currentSteps, 10));
      StepsUtil.putStorageValue(CommonConstants.PROGRESS_VALUE_TAG, String(this.progressValue));

      if (this.store) {
        taskModel.updateTask(this.store, {
          "progressValue": this.progressValue,
          "type": this.currentSteps
        }, this.taskItem.id)
      }

    } catch (err) {
      Logger.error(TAG, 'Sensor on err' + JSON.stringify(err));
    }
  }
  private getGeolocation: (location: geoLocationManager.Location) => void = (location: geoLocationManager.Location) => {
    if (this.latitude === location.latitude && this.longitude === location.longitude) {
      return;
    }
    this.latitude = location.latitude;
    this.longitude = location.longitude;
    let reverseGeocodeRequest: geoLocationManager.ReverseGeoCodeRequest = {
      'locale': this.locale.toString().includes('zh') ? 'zh' : 'en',
      'latitude': this.latitude,
      'longitude': this.longitude,
      'maxItems': 1
    };
    geoLocationManager.getAddressesFromLocation(reverseGeocodeRequest).then(data => {
      if (data[0].placeName) {
        this.currentLocation = data[0].placeName;
      }
    }).catch((err: Error) => {
      Logger.error(TAG, 'GetAddressesFromLocation err ' + JSON.stringify(err));
    });
  }

  onPageShow() {
    this.init();
    this.requestPermissions();
  }

  onPageHide() {
    sensor.off(sensor.SensorId.PEDOMETER);

    emitter.emit("refresh")
  }

  init() {
    StepsUtil.getStorageValue(CommonConstants.IS_START).then((res: string) => {
      if (res === CommonConstants.TRUE) {
        this.isStart = true;
        // StepsUtil.getStorageValue(CommonConstants.CURRENT_STEPS).then((res: string) => {
        //   if (StepsUtil.checkStrIsEmpty(res)) {
        //     return;
        //   }
        //   this.currentSteps = res;
        // });

        StepsUtil.getStorageValue(CommonConstants.PROGRESS_VALUE_TAG).then((res: string) => {
          if (StepsUtil.checkStrIsEmpty(res)) {
            return;
          }
          this.progressValue = NumberUtil._parseInt(res, 10);
        });

        StepsUtil.getStorageValue(CommonConstants.START_POSITION).then((res: string) => {
          if (StepsUtil.checkStrIsEmpty(res)) {
            return;
          }
          this.startPosition = res;
        });

        StepsUtil.getStorageValue(CommonConstants.OLD_STEPS).then((res: string) => {
          if (StepsUtil.checkStrIsEmpty(res)) {
            return;
          }
          this.oldSteps = res;
        });
      } else {
        this.isStart = false;
      }
    })

    StepsUtil.getStorageValue(CommonConstants.STEP_GOAL).then((res: string) => {
      if (StepsUtil.checkStrIsEmpty(res)) {
        return;
      }
      // this.stepGoal = res;
    });
  }

  requestPermissions(): void {
    let atManager = abilityAccessCtrl.createAtManager();
    try {
      atManager.requestPermissionsFromUser(this.context, CommonConstants.REQUEST_PERMISSIONS).then((data) => {
        if (data.authResults[0] !== 0 || data.authResults[1] !== 0) {
          return;
        }
        const that = this;
        try {
          sensor.on(sensor.SensorId.PEDOMETER, this.sensorCallback, { interval: CommonConstants.SENSOR_INTERVAL });

        } catch (err) {
          console.error('On fail, errCode: ' + JSON.stringify(err));
        }

        LocationUtil.geolocationOn(this.getGeolocation);
      }).catch((err: Error) => {
        Logger.error(TAG, 'requestPermissionsFromUser err' + JSON.stringify(err));
      })
    } catch (err) {
      Logger.error(TAG, 'requestPermissionsFromUser err' + JSON.stringify(err));
    }
  }


  aboutToAppear(): void {

    saveDB2LocalFile(this.context, (isSuccess: boolean) => {
      if (isSuccess) {
        getRdbStore(this.context, (store: relationalStore.RdbStore | undefined) => {
          this.store = store
        })
      }
    })

    this.currentSteps = String(this.taskItem.type)
    this.stepGoal = String(this.taskItem.type)
    this.progressValue = parseInt(String(Number(this.taskItem.type) / Number(this.taskItem.remark) * 100))
    if (this.progressValue > 100) {
      this.progressValue = 100
    }

  }

  build() {
    Navigation() {
      Stack({ alignContent: Alignment.TopStart }) {
        CompletionStatus({
          progressValue: $progressValue
        })

        CurrentSituation({
          currentSteps: this.currentSteps,
          startPosition: this.startPosition,
          currentLocation: this.currentLocation
        })

        Row() {
          Button(this.isStart ? $r('app.string.stop') : $r('app.string.start'))
            .width($r('app.float.start_button_width'))
            .height($r('app.float.start_button_height'))
            .borderRadius($r('app.float.start_button_radius'))
            .backgroundColor('#F87B31')
            .fontSize($r('app.float.start_font_size'))
            .fontColor(Color.White)
            .fontWeight(CommonConstants.BIG_FONT_WEIGHT)
            .onClick(() => {
              if (this.isStart) {
                this.isStart = false;
                this.oldSteps = '';
                StepsUtil.CleanStepsData();
                BackgroundUtil.stopContinuousTask(this.context);
              } else {
                if (this.stepGoal === '' || this.currentLocation === '') {
                  promptAction.showToast({ message: CommonConstants.WAIT });
                } else {
                  this.isStart = true;
                  this.startPosition = this.currentLocation;
                  StepsUtil.putStorageValue(CommonConstants.START_POSITION, this.startPosition);
                  BackgroundUtil.startContinuousTask(this.context);
                }
              }
              StepsUtil.putStorageValue(CommonConstants.IS_START, String(this.isStart));
            })
        }
        .width(CommonConstants.FULL_WIDTH)
        .height($r('app.float.button_height'))
        .margin({ top: $r('app.float.button_margin_top') })
        .justifyContent(FlexAlign.Center)
      }
      .width(CommonConstants.FULL_WIDTH)
      .height(CommonConstants.FULL_HEIGHT)
      .backgroundColor(Color.White)
    }
    .title(this.taskItem.taskName)
    .hideBackButton(false)
    .titleMode(NavigationTitleMode.Mini)

  }
}

CompletionStatus

组件包含环形进度条和设置目标的按钮


import { CommonConstants } from '../common/constant/CommonConstant';
import InputDialog from '../view/InputDialog';
import Logger from '../common/utils/Logger';
import StepsUtil from '../common/utils/StepsUtil';
import { router } from '@kit.ArkUI';

const TAG: string = 'CompletionStatus';

@Component
export struct CompletionStatus {
  @Link progressValue: number;
  @Consume stepGoal: string;

  private params: ESObject = router.getParams();
  private taskItem: ESObject = this.params?.taskItem

  inputDialogController: CustomDialogController = new CustomDialogController({
    builder: InputDialog({
      cancel: this.inputDialogCancel,
      confirm: this.inputDialogConfirm
    }),
    autoCancel: false,
    customStyle: true,
    alignment: DialogAlignment.Bottom,
    offset: { dx: CommonConstants.OFFSET_DX, dy: CommonConstants.OFFSET_DY }
  })

  aboutToAppear(): void {
    this.stepGoal = this.taskItem.remark
    this.inputDialogConfirm()
  }

  inputDialogCancel() {
    Logger.info(TAG, 'Callback when the cancel button is clicked');
  }

  inputDialogConfirm() {
    if (StepsUtil.checkStrIsEmpty(this.stepGoal)) {
      return;
    }
    StepsUtil.putStorageValue(CommonConstants.STEP_GOAL, this.stepGoal);
  }

  build() {
    Stack({ alignContent: Alignment.TopStart }) {
      Column() {
        Progress({
          value: 0,
          total: CommonConstants.PROGRESS_TOTAL,
          type: ProgressType.Ring
        })
          .color(Color.White)
          .value(this.progressValue)
          .width($r('app.float.progress_width'))
          .style({
            strokeWidth: CommonConstants.PROGRESS_STROKE_WIDTH,
            scaleCount: CommonConstants.PROGRESS_SCALE_COUNT,
            scaleWidth: CommonConstants.PROGRESS_SCALE_WIDTH
          })
          .margin({ top: $r('app.float.progress_margin_top') })
          .borderRadius(CommonConstants.PROGRESS_BORDER_RADIUS)

        Button($r('app.string.set_target'))
          .width($r('app.float.target_button_width'))
          .height($r('app.float.target_button_height'))
          .borderRadius($r('app.float.target_button_radius'))
          .fontSize($r('app.float.target_button_font'))
          .fontColor($r('app.color.button_font'))
          .fontWeight(CommonConstants.SMALL_FONT_WEIGHT)
          .backgroundColor(Color.White)
          .margin({
            top: $r('app.float.target_margin_top'),
            bottom: $r('app.float.target_margin_bottom')
          })
          .onClick(() => {
            this.inputDialogController.open();
          })
      }
      .width(CommonConstants.FULL_WIDTH)
      .backgroundImage($r('app.media.ic_orange'))

      Row() {
        Text(this.progressValue.toString())
          .borderRadius($r('app.float.progress_text_radius'))
          .fontSize($r('app.float.progress_text_font'))
          .fontColor(Color.White)
          .fontWeight(CommonConstants.BIG_FONT_WEIGHT)
          .textAlign(TextAlign.Center)
          .margin({ top: $r('app.float.value_margin_top') })

        Text(CommonConstants.PERCENT_SIGN)
          .borderRadius($r('app.float.percent_text_radius'))
          .fontSize($r('app.float.percent_text_font'))
          .fontColor(Color.White)
          .fontWeight(CommonConstants.SMALL_FONT_WEIGHT)
          .textAlign(TextAlign.Center)
          .margin({ top: $r('app.float.percent_margin_top') })
      }
      .width(CommonConstants.FULL_WIDTH)
      .justifyContent(FlexAlign.Center)
    }
  }
}

CurrentSituation

这个组件是当前位置、步数的封装

import { CommonConstants } from '../common/constant/CommonConstant';

@Component
export struct CurrentSituation {
  @Prop currentSteps: string = '';
  @Prop startPosition: string = '';
  @Prop currentLocation: string = '';

  @Styles descriptionTextStyle(){
    .width($r('app.float.description_text_width'))
    .height($r('app.float.description_text_height'))
  }
  @Styles displayTextStyle(){
    .width($r('app.float.display_text_width'))
    .height($r('app.float.display_text_height'))
  }

  build() {
    Row() {
      Column() {
        Text($r('app.string.walking_data'))
          .width(CommonConstants.FULL_WIDTH)
          .height($r('app.float.walling_height'))
          .fontSize($r('app.float.walling_text_font'))
          .fontColor($r('app.color.step_text_font'))
          .fontWeight(CommonConstants.SMALL_FONT_WEIGHT)
          .textAlign(TextAlign.Start)
          .margin({
            top: $r('app.float.walling_margin_top'),
            bottom: $r('app.float.walling_margin_bottom'),
            left: $r('app.float.walling_margin_left')
          })

        Row() {
          Text($r('app.string.current_steps'))
            .descriptionTextStyle()
            .fontSize($r('app.float.current_steps_font'))
            .fontColor($r('app.color.steps_text_font'))
            .fontWeight(CommonConstants.BIG_FONT_WEIGHT)
            .textAlign(TextAlign.Start)

          Text($r('app.string.step', this.currentSteps))
            .displayTextStyle()
            .fontSize($r('app.float.record_steps_font'))
            .fontColor($r('app.color.step_text_font'))
            .fontWeight(CommonConstants.BIG_FONT_WEIGHT)
            .textAlign(TextAlign.Start)
        }
        .width(CommonConstants.FULL_WIDTH)
        .height($r('app.float.current_row_height'))
        .margin({
          top: $r('app.float.current_margin_top'),
          bottom: $r('app.float.current_margin_bottom'),
          left: $r('app.float.current_margin_left')
        })

        Divider()
          .vertical(false)
          .color($r('app.color.divider'))
          .strokeWidth(CommonConstants.DIVIDER_STROKE_WIDTH)
          .margin({
            left: $r('app.float.divider_margin_left'),
            right: $r('app.float.divider_margin_right')
          })

        Row() {
          Text($r('app.string.start_position'))
            .descriptionTextStyle()
            .fontSize($r('app.float.start_position_font'))
            .fontColor($r('app.color.steps_text_font'))
            .fontWeight(CommonConstants.BIG_FONT_WEIGHT)
            .textAlign(TextAlign.Start)

          Text(this.startPosition)
            .fontSize($r('app.float.position_font_size'))
            .fontColor($r('app.color.step_text_font'))
            .fontWeight(CommonConstants.BIG_FONT_WEIGHT)
            .textAlign(TextAlign.Start)
            .width('80%')
            // .textOverflow({ overflow: TextOverflow.Ellipsis })
            // .maxLines(CommonConstants.MAX_LINE)
        }
        .width(CommonConstants.FULL_WIDTH)
        .margin({
          top: $r('app.float.position_margin_top'),
          bottom: $r('app.float.position_margin_bottom'),
          left: $r('app.float.position_margin_left')
        })

        Divider()
          .vertical(false)
          .color($r('app.color.divider'))
          .strokeWidth(CommonConstants.DIVIDER_STROKE_WIDTH)
          .margin({
            left: $r('app.float.divider_margin_left'),
            right: $r('app.float.divider_margin_right')
          })

        Row() {
          Text($r('app.string.current_location'))
            .descriptionTextStyle()
            .fontSize($r('app.float.location_font_size'))
            .fontColor($r('app.color.steps_text_font'))
            .fontWeight(CommonConstants.BIG_FONT_WEIGHT)
            .textAlign(TextAlign.Start)

          Text(this.currentLocation)
            .fontSize($r('app.float.current_font_size'))
            .fontColor($r('app.color.step_text_font'))
            .fontWeight(CommonConstants.BIG_FONT_WEIGHT)
            .textAlign(TextAlign.Start)
            .width('80%')
            // .textOverflow({ overflow: TextOverflow.Ellipsis })
            // .maxLines(CommonConstants.MAX_LINE)
        }
        .width(CommonConstants.FULL_WIDTH)
        .margin({
          top: $r('app.float.location_margin_top'),
          bottom: $r('app.float.location_margin_bottom'),
          left: $r('app.float.location_margin_left')
        })
      }
      .width(CommonConstants.SITUATION_WIDTH)
      .borderRadius($r('app.float.situation_border_radius'))
      .backgroundColor(Color.White)
    }
    .width(CommonConstants.FULL_WIDTH)
    .height($r('app.float.current_situation_height'))
    .margin({ top: $r('app.float.situation_margin_top') })
    .justifyContent(FlexAlign.Center)
  }
}

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
标签
收藏
回复
举报
回复
    相关推荐