回复
#HarmonyOS NEXT体验官# 体验HarmonyOS开发流程,开发一个记步app(一) 原创
RandomBoolean
发布于 2024-8-21 17:37
浏览
0收藏
首先介绍一下要开发的app整体内容。
随行记步
首页顶部调用一言的接口,随机输出几句话,app封装了一个公共接口方法,方便接口的调用。
中间是一个类似代办事项的todo列表。可以通过增删改的方式进行变更。
底部是一个添加目标按钮。
整体如图所示。
点击其中某一项,进入记步详情页。
顶部是一个`
环形进度条,可以单独设置目标步数。
底部是当前步行的数据,起始位置和当前位置。
下方是一个开始、暂停的按钮。效果如图所示。
DB数据库
在开始介绍整体app时,因为项目使用本地数据库存储的方式,需要先完善数据库的封装。
在项目目录创建db文件夹
创建文件DBUtils.ets,源码如下。此为读写数据库的核心方法。
import { common } from '@kit.AbilityKit';
import fs, { ReadOptions } from '@ohos.file.fs';
import { relationalStore } from '@kit.ArkData';
import { Constant } from '../common/constant/Constant';
/**
* 将rawfile下的xcz_150.db 写入到手机本地文件中
* @param context
*/
export async function saveDB2LocalFile(context: common.UIAbilityContext, copyCallback: (isSuccess: boolean) => void) {
let dirPath = context.getApplicationContext().databaseDir + "/entry"
if (!fs.accessSync(dirPath)) {
fs.mkdirSync(dirPath);
}
dirPath = dirPath + "/rdb"
if (!fs.accessSync(dirPath)) {
fs.mkdirSync(dirPath);
}
let dbName: string = Constant.DB_NAME
if (!fs.accessSync(dirPath + "/" + dbName)) {
try {
context.resourceManager.getRawFd('rdb/' + dbName, (error, value) => {
if (error != null) {
console.log(`callback getRawFd failed error code: ${error.code}, message: ${error.message}.`);
} else {
console.info(value.length.toString() + "DWSD")
// saveFileToCache(context,value, dbName)
let cFile = context.getApplicationContext().databaseDir + "/entry/rdb/" + dbName
let cacheFile = fs.openSync(cFile, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE)
// 读取缓冲区大小
let bufferSize = 30000
let buffer = new ArrayBuffer(bufferSize); //创建buffer缓冲区
// 要copy的文件的offset和length
let currentOffset = value.offset;
let lengthNeedToReed = value.length;
let readOption: ReadOptions = {
offset: currentOffset, //期望读取文件的位置。可选,默认从当前位置开始读
length: bufferSize //每次期望读取数据的长度。可选,默认缓冲区长度
}
while (true) {
// 读取buffer容量的内容
let readLength = fs.readSync(value.fd, buffer, readOption);
// 写入buffer容量的内容
fs.writeSync(cacheFile.fd, buffer, { length: readLength }) //写到cacheFile里
// 判断后续内容 修改读文件的参数
// buffer没读满代表文件读完了
if (readLength < bufferSize) {
break;
}
if (readOption.offset) {
readOption.offset += readLength
}
console.log("===============size:" + readOption.offset)
}
console.log("Copy Success!!!")
fs.close(cacheFile);
copyCallback(true)
}
});
} catch (error) {
console.error(`callback getRawFd failed, error code: ${error.code}, message: ${error.message}.`)
}
}else {
copyCallback(true)
}
}
export async function getRdbStore(context: Context, onGetStore: (store: relationalStore.RdbStore | undefined) => void) {
relationalStore.getRdbStore(context, {
name: Constant.DB_NAME,
securityLevel: relationalStore.SecurityLevel.S1
}, (err, store) => {
if (err) {
console.error(`Failed to get RdbStore. Code:${err.code}, message:${err.message}`);
return;
} else {
console.info(`Succeeded in getting RdbStore.`);
onGetStore(store)
console.log("=======version:" + store.version)
}
// this.rdbStore = store;
})
}
Constant如下
export class Constant {
static readonly DB_NAME = "task.db"
}
实现一个增删改的方法。附带分页的实现。
import { BusinessError } from '@kit.BasicServicesKit'
import { Task } from '../model/Task'
import { relationalStore, ValuesBucket } from '@kit.ArkData'
class TaskModel {
/**任务列表,带分页
*/
getTasks(store: relationalStore.RdbStore, onGetTasks: (res: ESObject) => void, onError?: (err: string) => void, limit?: number, skip?: number) {
let predicates = new relationalStore.RdbPredicates("task")
predicates.limitAs(limit)
predicates.offsetAs(skip)
predicates.orderByDesc("id")
store.query(predicates, (err: BusinessError, resultSet) => {
if (err == undefined) {
let tasks = Array<Task>()
while (resultSet.goToNextRow()) {
tasks.push(this.parseTask(resultSet))
}
onGetTasks(tasks)
console.log("=====" + JSON.stringify(tasks))
} else {
onError!(err.message)
}
})
}
addTask(store: relationalStore.RdbStore, valueBucket: ValuesBucket, onAddSuccess?: (res: ESObject) => void, onError?: (err: ESObject) => void) {
store.insert("task", valueBucket, (err: BusinessError, rows: number) => {
if (err) {
if (onError) {
onError("")
}
return
}
onAddSuccess!("")
})
}
//https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-data-rdb-0000001860176021
updateTask(store: relationalStore.RdbStore, valueBucket: ValuesBucket, id: number) {
let predicates = new relationalStore.RdbPredicates("task")
predicates.equalTo('id', id)
store.update(valueBucket, predicates, (err: BusinessError, rows: number) => {
if (err) {
console.info("Updated failed, err: " + err)
return
}
console.log("Updated row count: " + rows)
})
}
deleteTask(store: relationalStore.RdbStore) {
let predicates = new relationalStore.RdbPredicates("task")
predicates.equalTo("id", 123)
store.delete(predicates)
}
parseTask(resultSet: relationalStore.ResultSet): Task {
let task = new Task()
task.id = this.parseLong("id", resultSet)
task.progressValue = this.parseLong("progressValue", resultSet)
task.type = this.parseLong("type", resultSet)
task.taskName = this.parseString("taskName", resultSet)
task.remark = this.parseString("remark", resultSet)
task.createDate = this.parseString("createDate", resultSet)
task.updateDate = this.parseString("updateDate", resultSet)
task.cu_id = this.parseString("cu_id", resultSet)
task.pic = this.parseString("pic", resultSet)
return task
}
parseLong(column: string, resultSet: relationalStore.ResultSet): number {
if (resultSet.getColumnIndex(column) != -1) {
return resultSet.getLong(resultSet.getColumnIndex(column));
}
return 0
}
parseString(column: string, resultSet: relationalStore.ResultSet): string {
if (resultSet.getColumnIndex(column) != -1) {
return resultSet.getString(resultSet.getColumnIndex(column));
}
return ""
}
}
export let taskModel = new TaskModel()
使用方式,通过引入taskModel调用其实现的方法。在aboutToAppear初始化数据库,赋值store。
import { taskModel } from '../db/TaskModel';
import { getRdbStore, saveDB2LocalFile } from '../db/DBUtils';
@Provide store: relationalStore.RdbStore | undefined = undefined
context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext
aboutToAppear(): void {
saveDB2LocalFile(this.context, (isSuccess: boolean) => {
if (isSuccess) {
getRdbStore(this.context, (store: relationalStore.RdbStore | undefined) => {
this.store = store
})
}
})
}
const valueBucket: ValuesBucket = {
"taskName": taskName,
"remark": runNum,
"progressValue": 0,
"updateDate": getCurrentTime()
};
taskModel.addTask(this.store, valueBucket, () => {
emitter.emit("addTaskSuccess")
this.dialogController.close()
})
这里还用到了emitter事件的触发。this.store为必传参数。
app首页的完整源码如下
/*
* Copyright (c) 2023 Huawei Device Co., Ltd.
* Licensed under the Apache License,Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { promptAction } from '@kit.ArkUI';
import TargetInformation from '../view/TargetInformation';
import AddTargetDialog from '../view/AddTargetDialog';
import TargetList from '../view/TargetList';
import TaskItemModel from '../viewmodel/TaskItemModel';
import DataModel from '../viewmodel/DataModel';
import { CommonConstants } from '../common/constant/CommonConstant';
import getCurrentTime from '../common/utils/DateUtil';
import { relationalStore, ValuesBucket } from '@kit.ArkData';
import { taskModel } from '../db/TaskModel';
import { emitter } from '@kit.BasicServicesKit';
import { common } from '@kit.AbilityKit';
import { getRdbStore, saveDB2LocalFile } from '../db/DBUtils';
@Entry
@Component
export struct MainPage {
@State targetData: Array<TaskItemModel> = DataModel.getData();
@State totalTasksNumber: number = 0;
@State completedTasksNumber: number = 0;
@State latestUpdateDate: string = CommonConstants.DEFAULT_PROGRESS_VALUE;
@Provide @Watch('onProgressChanged') overAllProgressChanged: boolean = false;
@State limit: number = 20
@State skip: number = 0
dialogController: CustomDialogController = new CustomDialogController({
builder: AddTargetDialog({
onClickOk: (value: string, runNum): void => this.saveTask(value, runNum)
}),
alignment: DialogAlignment.Bottom,
offset: {
dx: CommonConstants.DIALOG_OFFSET_X,
dy: $r('app.float.dialog_offset_y')
},
customStyle: true,
autoCancel: false
});
@Provide store: relationalStore.RdbStore | undefined = undefined
context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext
/**
* Listening targetData.
*/
onProgressChanged() {
this.totalTasksNumber = this.targetData.length;
this.completedTasksNumber = this.targetData.filter((item: TaskItemModel) => {
return item.progressValue === CommonConstants.SLIDER_MAX_VALUE;
}).length;
this.latestUpdateDate = getCurrentTime();
}
aboutToAppear(): void {
saveDB2LocalFile(this.context, (isSuccess: boolean) => {
if (isSuccess) {
getRdbStore(this.context, (store: relationalStore.RdbStore | undefined) => {
this.store = store
})
}
})
}
build() {
Column() {
this.titleBar()
TargetInformation({
latestUpdateDate: this.latestUpdateDate,
totalTasksNumber: this.totalTasksNumber,
completedTasksNumber: this.completedTasksNumber
})
TargetList({
targetData: $targetData,
onAddClick: (): void => this.dialogController.open()
})// .height(CommonConstants.LIST_BOARD_HEIGHT)
.layoutWeight(1)
}
.width(CommonConstants.FULL_WIDTH)
.height(CommonConstants.FULL_HEIGHT)
.backgroundColor($r('app.color.index_background'))
}
@Builder
titleBar() {
Text('随行记步')
.width(CommonConstants.TITLE_WIDTH)
.height($r('app.float.title_height'))
.fontSize($r('app.float.title_font'))
.fontWeight(CommonConstants.FONT_WEIGHT_LARGE)
.textAlign(TextAlign.Start)
.margin({
top: $r('app.float.title_margin'),
bottom: $r('app.float.title_margin')
})
}
/**
* Save the progress value and update time after you click OK in the dialog box.
*
* @param taskName Latest Progress Value.
*/
saveTask(taskName: string, runNum: number) {
if (taskName === '') {
promptAction.showToast({
message: $r('app.string.cannot_input_empty'),
duration: CommonConstants.TOAST_TIME,
bottom: CommonConstants.TOAST_MARGIN_BOTTOM
});
return;
}
if (!runNum) {
promptAction.showToast({
message: '请设置步数',
duration: CommonConstants.TOAST_TIME,
bottom: CommonConstants.TOAST_MARGIN_BOTTOM
});
return;
}
const valueBucket: ValuesBucket = {
"taskName": taskName,
"remark": runNum,
"progressValue": 0,
"updateDate": getCurrentTime()
};
if (this.store) {
taskModel.addTask(this.store, valueBucket, () => {
emitter.emit("addTaskSuccess")
this.dialogController.close()
})
}
// DataModel.addData(new TaskItemModel(taskName, 0, getCurrentTime()));
// this.targetData = DataModel.getData();
// this.overAllProgressChanged = !this.overAllProgressChanged;
// this.dialogController.close();
}
}
重要
使用数据库之前需要先创建好表结构,然后将db文件放到如下图所示的位置中。
核心除了接口的调用,就是对于数据库的使用。篇幅所限,本文介绍了数据库相关的使用,因为本app依赖于此所以介绍的有些多。后续就是其他的一些实现。
©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
标签
赞
收藏
回复
相关推荐