在HarmonyOS上使用ArkUI实现计步器应用

原创
2023/11/17 09:44
阅读数 30
 

介绍

本篇Codelab使用ArkTS语言实现计步器应用,应用主要包括计步传感器、定位服务和后台任务功能:

  1. 通过订阅计步器传感器获取计步器数据,处理后显示。
  2. 通过订阅位置服务获取位置数据,处理后显示。
  3. 通过服务开发实现后台任务功能。

相关概念

计步传感器:订阅计步器传感器数据,系统返回相关数据。

后台任务管理:应用中存在用户能够直观感受到的且需要一直在后台运行的业务时(如,后台播放音乐),可以使用长时任务机制。

位置服务:位置服务提供GNSS定位、网络定位、地理编码、逆地理编码、国家码和地理围栏等基本功能。

相关权限

本篇Codelab用到了计步传感器、后台任务及位置服务功能,需要在配置文件module.json5里添加权限:

  • ohos.permission.ACTIVITY_MOTION
  • ohos.permission.KEEP_BACKGROUND_RUNNING
  • ohos.permission.APPROXIMATELY_LOCATION
  • ohos.permission.LOCATION
  • ohos.permission.LOCATION_IN_BACKGROUND

完整示例

gitee源码地址

 

源码下载

计步器应用(ArkTS).zip

环境搭建

我们首先需要完成HarmonyOS开发环境搭建,可参照如下步骤进行。

软件要求

DevEco Studio版本:DevEco Studio 3.1 Release。

HarmonyOS SDK版本:API version 9。

硬件要求

设备类型:华为手机或运行在DevEco Studio上的华为手机设备模拟器。

HarmonyOS系统:3.1.0 Developer Release。

环境搭建

安装DevEco Studio,详情请参考下载和安装软件

设置DevEco Studio开发环境,DevEco Studio开发环境需要依赖于网络环境,需要连接上网络才能确保工具的正常使用,可以根据如下两种情况来配置开发环境:如果可以直接访问Internet,只需进行下载HarmonyOS SDK操作。

如果网络不能直接访问Internet,需要通过代理服务器才可以访问,请参考配置开发环境

开发者可以参考以下链接,完成设备调试的相关配置:使用真机进行调试

使用模拟器进行调试

 

代码结构解读

本篇Codelab只对核心代码进行讲解,对于完整代码,我们会在源码下载或gitee中提供。

 

├──entry/src/main/ets               // 代码区
│  ├──common
│  │  ├──constants
│  │  │  └──CommonConstants.ets     // 公共常量
│  │  └──utils                      // 日志类
│  │     ├──BackgroundUtil.ets      // 后台任务工具类
│  │     ├──GlobalContext.ets       // 首选项工具类
│  │     ├──LocationUtil.ets        // 位置服务工具类
│  │     ├──Logger.ets              // 日志工具类
│  │     ├──NumberUtil.ets          // 数字处理工具类
│  │     └──StepsUtil.ets           // 计步器工具类
│  ├──entryability
│  │  └──EntryAbility.ets           // 程序入口类
│  ├──pages
│  │  └──HomePage.ets               // 应用首页
│  └──view
│     ├──CompletionStatus.ets       // 目标设置页
│     ├──CurrentSituation.ets       // 计步信息页
│     └──InputDialog.ets            // 自定义弹窗
└──entry/src/main/resources         // 资源文件夹

 

构建应用界面

计步器页面主要由Stack堆叠容器组件、Component自定义组件和CustomDialog自定义弹窗组件完成页面布局,效果如图所示:

 

 

// HomePage.ets
build() {
  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'))
        ...
    }
    ...
  }
  ...
}

 

计步传感器

应用启动后申请计步传感器权限,获取权限后订阅计步器传感器。通过订阅获取到计步传感器数据,解析处理后在页面显示。效果如图所示:

 

 

// HomePage.ets
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, (data) => {
          try {
            if (that.isStart) {
              if (StepsUtil.checkStrIsEmpty(that.oldSteps)) {
                that.oldSteps = data.steps.toString();
                StepsUtil.putStorageValue(CommonConstants.OLD_STEPS, that.oldSteps);
              } else {
                that.currentSteps = (data.steps - NumberUtil._parseInt(that.oldSteps, 10)).toString();
              }
            } else {
              that.currentSteps = data.steps.toString();
            }

            if (StepsUtil.checkStrIsEmpty(that.stepGoal) || !that.isStart) {
              return;
            }
            StepsUtil.putStorageValue(CommonConstants.CURRENT_STEPS, that.currentSteps);
            that.progressValue = StepsUtil.getProgressValue(NumberUtil._parseInt(that.stepGoal, 10),
              NumberUtil._parseInt(that.currentSteps, 10));
            StepsUtil.putStorageValue(CommonConstants.PROGRESS_VALUE_TAG, String(that.progressValue));
          } catch (err) {
            Logger.error(TAG, 'Sensor on err' + JSON.stringify(err));
          }
        }, { interval: CommonConstants.SENSOR_INTERVAL });
        ...
}

 

位置服务

应用启动后申请位置服务权限,获取权限后启动服务,启动服务后订阅位置服务。通过订阅获取到位置服务数据,解析处理后在页面显示。效果如图所示:

 

// HomePage.ets
requestPermissions(): void {
  ...
  LocationUtil.geolocationOn((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 = {
      'latitude': this.latitude,
      'longitude': this.longitude
    };
    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));
    });
  });
  ...
}

 

将位置服务相关的函数封装到工具类中。

 

// LocationUtil.ets
class LocationUtil {
  geolocationOn(locationChange: (location: geoLocationManager.Location) => void): void {
    let requestInfo: geoLocationManager.LocationRequest = {
      'priority': 0x203,
      'scenario': 0x300,
      'timeInterval': 0,
      'distanceInterval': 0,
      'maxAccuracy': 0
    }
    try {
      geoLocationManager.on('locationChange', requestInfo, locationChange);
    } catch (err) {
      console.error("locationChange error:" + JSON.stringify(err));
    }
  }

  geolocationOff(): void {
    geoLocationManager.off('locationChange');
  }
}

 

后台任务

点击开始按钮开启后台任务,通过后台任务管理方法配置申请的后台模式等参数启动后台任务。

 

// HomePage.ets
build() {
  Stack({ alignContent: Alignment.TopStart }) {
    ...
    Row() {
      Button(this.isStart ? $r('app.string.stop') : $r('app.string.start'))
        ...
        .onClick(() => {
          if (this.isStart) {
            ...
            BackgroundUtil.stopContinuousTask(this.context);
          } else {
            if (this.stepGoal === '' || this.currentLocation === '') {
              promptAction.showToast({ message: CommonConstants.WAIT });
            } else {
              ...
              BackgroundUtil.startContinuousTask(this.context);
            }
          }
          StepsUtil.putStorageValue(CommonConstants.IS_START, String(this.isStart));
        })
    }
    ...
}

// BackgroundUtil.ets
export class BackgroundUtil {
  public static startContinuousTask(context: common.UIAbilityContext): void {
    let wantAgentInfo: wantAgent.WantAgentInfo = {
      wants: [
        {
          bundleName: context.abilityInfo.bundleName,
          abilityName: context.abilityInfo.name
        }
      ],
      operationType: wantAgent.OperationType.START_ABILITY,
      requestCode: 0,
      wantAgentFlags: [wantAgent.WantAgentFlags.UPDATE_PRESENT_FLAG]
    };

    wantAgent.getWantAgent(wantAgentInfo).then((wantAgentObj) => {
      try {
        backgroundTaskManager.startBackgroundRunning(context,
          backgroundTaskManager.BackgroundMode.LOCATION, wantAgentObj).then(() => {
          Logger.info(TAG, 'startBackgroundRunning succeeded');
        }).catch((err: Error) => {
          Logger.error(TAG, `startBackgroundRunning failed Cause:  ${JSON.stringify(err)}`);
        });
      } catch (error) {
        Logger.error(TAG, `stopBackgroundRunning failed. error: ${JSON.stringify(error)} `);
      }
    });
  }

  public static stopContinuousTask(context: common.UIAbilityContext): void {
    try {
      backgroundTaskManager.stopBackgroundRunning(context).then(() => {
        Logger.info(TAG, 'stopBackgroundRunning succeeded');
      }).catch((err: Error) => {
        Logger.error(TAG, `stopBackgroundRunning failed Cause:  ${JSON.stringify(err)}`);
      });
    } catch (error) {
      Logger.error(TAG, `stopBackgroundRunning failed. error: ${JSON.stringify(error)} `);
    }
  }
}

 

总结

您已经完成了本次Codelab的学习,并了解到以下知识点:

  1. 计步器传感器的功能实现。
  2. 位置服务的功能实现。
  3. 后台任务的功能实现。

 

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部