回复
#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)
}
}
©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
标签
赞
收藏
回复
相关推荐