介紹
介紹如何使用后臺(tái)代理提醒,實(shí)現(xiàn)一個(gè)簡(jiǎn)易鬧鐘。要求完成以下功能:
- 展示指針表盤或數(shù)字時(shí)間。
- 添加、修改和刪除鬧鐘。
- 展示鬧鐘列表,并可打開和關(guān)閉單個(gè)鬧鐘。
- 鬧鐘到設(shè)定的時(shí)間后彈出提醒。
- 將鬧鐘的定時(shí)數(shù)據(jù)保存到輕量級(jí)數(shù)據(jù)庫(kù)。
相關(guān)概念
- [Canvas]:提供畫布組件,用于自定義繪制圖形。
- [CanvasRenderingContext2D]:使用RenderingContext在Canvas組件上進(jìn)行繪制,繪制對(duì)象可以是矩形、文本、圖片等。
- [后臺(tái)代理提醒]:開發(fā)應(yīng)用時(shí),開發(fā)者可以調(diào)用后臺(tái)提醒發(fā)布的接口創(chuàng)建定時(shí)提醒,包括倒計(jì)時(shí)、日歷、鬧鐘三種提醒類型。使用后臺(tái)代理提醒能力后,應(yīng)用可以被凍結(jié)或退出,計(jì)時(shí)和彈出提醒的功能將被后臺(tái)系統(tǒng)服務(wù)代理。本應(yīng)用中主要使用到后臺(tái)代理提醒的兩個(gè)接口:
相關(guān)權(quán)限
本篇Codelab需要在module.json5中配置如下權(quán)限:
"requestPermissions": [
{
"name": "ohos.permission.PUBLISH_AGENT_REMINDER"
}
]
環(huán)境搭建
軟件要求
- [DevEco Studio]版本:DevEco Studio 3.1 Release。
- OpenHarmony SDK版本:API version 9。
硬件要求
- 開發(fā)板類型:[潤(rùn)和RK3568開發(fā)板]。
- OpenHarmony系統(tǒng):3.2 Release。
環(huán)境搭建
完成本篇Codelab我們首先要完成開發(fā)環(huán)境的搭建,本示例以RK3568開發(fā)板為例,參照以下步驟進(jìn)行:
- [獲取OpenHarmony系統(tǒng)版本]:標(biāo)準(zhǔn)系統(tǒng)解決方案(二進(jìn)制)。以3.2 Release版本為例:
- 搭建燒錄環(huán)境。
- [完成DevEco Device Tool的安裝]
- [完成RK3568開發(fā)板的燒錄]
- 搭建開發(fā)環(huán)境。
- 開始前請(qǐng)參考[工具準(zhǔn)備],完成DevEco Studio的安裝和開發(fā)環(huán)境配置。
- 開發(fā)環(huán)境配置完成后,請(qǐng)參考[使用工程向?qū)創(chuàng)建工程(模板選擇“Empty Ability”)。
- 工程創(chuàng)建完成后,選擇使用[真機(jī)進(jìn)行調(diào)測(cè)]。
代碼結(jié)構(gòu)解讀
本篇Codelab只對(duì)核心代碼進(jìn)行講解,對(duì)于完整代碼,我們會(huì)在gitee中提供。
`HarmonyOS與OpenHarmony鴻蒙文檔籽料:mau123789是v直接拿`
├──entry/src/main/ets // 代碼區(qū)
│ ├──common
│ │ ├──constants
│ │ │ ├──AlarmSettingType.ets // 鬧鐘設(shè)置類型枚舉
│ │ │ ├──CommonConstants.ets // 公共常量類
│ │ │ ├──DetailConstant.ets // 詳情頁(yè)常量類
│ │ │ └──MainConstant.ets // 首頁(yè)常量類
│ │ └──utils
│ │ ├──DataTypeUtils.ets // 數(shù)據(jù)類型工具類
│ │ ├──DimensionUtil.ets // 屏幕適配工具類
│ │ └──GlobalContext.ets // 全局變量工具類
│ ├──entryability
│ │ └──EntryAbility.ets // 程序入口類
│ ├──model
│ │ ├──database
│ │ │ ├──PreferencesHandler.ets // 輕量級(jí)數(shù)據(jù)庫(kù)操作類
│ │ │ └──PreferencesListener.ets // 輕量級(jí)數(shù)據(jù)庫(kù)回調(diào)接口
│ │ └──ReminderService.ets // 系統(tǒng)后臺(tái)提醒服務(wù)類
│ │──pages
│ │ ├──DetailIndex.ets // 詳情頁(yè)入口文件
│ │ └──MainIndex.ets // 主頁(yè)入口文件
│ │──view
│ │ ├──Detail
│ │ │ ├──dialog
│ │ │ │ ├──CommonDialog.ets // 公共Dialog組件
│ │ │ │ ├──DurationDialog.ets // 鬧鈴時(shí)長(zhǎng)選擇Dialog組件
│ │ │ │ ├──IntervalDialog.ets // 鬧鈴間隔選擇Dialog組件
│ │ │ │ ├──RenameDialog.ets // 鬧鈴名設(shè)置Dialog組件
│ │ │ │ └──RepeatDialog.ets // 鬧鈴重復(fù)設(shè)置Dialog組件
│ │ │ ├──DatePickArea.ets // 詳情頁(yè)時(shí)間選擇組件
│ │ │ └──SettingItem.ets // 詳情頁(yè)設(shè)置組件
│ │ ├──Main
│ │ │ ├──AlarmList.ets // 主頁(yè)鬧鐘列表組件
│ │ │ ├──AlarmListItem.ets // 主頁(yè)鬧鐘列表子項(xiàng)組件
│ │ │ └──ClockArea.ets // 主頁(yè)時(shí)鐘組件
│ │ └──BackContainer.ets // 自定義頭部組件
│ └──viewmodel
│ ├──AlarmItemBean.ets // 鬧鐘屬性類
│ ├──AlarmSettingBean.ets // 鬧鐘設(shè)置屬性類
│ ├──DayDateBean.ets // 日期屬性類
│ ├──DetailViewModel.ets // 詳情模塊邏輯功能類
│ ├──MainViewModel.ets // 主頁(yè)邏輯功能類
│ └──ReminderItemBean.ets // 后臺(tái)提醒屬性類
└──entry/src/main/resources // 資源文件目錄
鬧鐘主界面
鬧鐘界面包括當(dāng)前時(shí)間、鬧鐘列表、添加鬧鐘子組件,具體包括以下模塊:
- 展示當(dāng)前時(shí)間。
- 展示鬧鐘列表。
- 添加鬧鐘。
- 后臺(tái)代理提醒。
展示當(dāng)前時(shí)間
當(dāng)前時(shí)間使用了Canvas組件繪制,默認(rèn)展示指針表盤,點(diǎn)擊表盤區(qū)域切換為數(shù)字時(shí)鐘。
在主頁(yè)的ClockArea組件中初始化Canvas畫布,并綁定指針表盤和數(shù)字時(shí)鐘切換事件。
// ClockArea.ets
@Component
export default struct ClockArea {
...
build() {
Canvas(this.renderContext)
.width(this.canvasSize)
.aspectRatio(CommonConstants.DEFAULT_LAYOUT_WEIGHT)
.onReady(() = > {
if (this.drawInterval === CommonConstants.DEFAULT_NUMBER_NEGATIVE) {
this.startDrawTask();
}
})
.onClick(() = > {
this.showClock = !this.showClock;
})
}
// 啟動(dòng)繪畫任務(wù)
private startDrawTask() {
let that = this;
that.renderContext.translate(
this.canvasSize / CommonConstants.DEFAULT_DOUBLE,
this.canvasSize / CommonConstants.DEFAULT_DOUBLE);
that.drawClockArea();
this.drawInterval = setInterval(() = > {
that.drawClockArea();
}, MainConstant.DEFAULT_ONE_SECOND_MS);
}
...
}
繪畫任務(wù)是使用CanvasRenderingContext2D對(duì)象在Canvas畫布組件上繪制指針表盤和數(shù)字時(shí)鐘。
// ClockArea.ets
// 開始繪制時(shí)鐘區(qū)域
private drawClockArea(): void{
this.renderContext.clearRect(
-this.canvasSize,
-this.canvasSize / CommonConstants.DEFAULT_DOUBLE,
this.canvasSize * CommonConstants.DEFAULT_DOUBLE,
this.canvasSize);
let date = new Date();
let hours = date.getHours();
let minutes = date.getMinutes();
let seconds = date.getSeconds();
if (this.showClock) {
// 繪制表盤時(shí)鐘
} else {
// 繪制數(shù)字時(shí)鐘
}
}
展示鬧鐘列表
鬧鐘列表組件,展示已添加的鬧鐘信息,可對(duì)鬧鐘進(jìn)行啟停操作,點(diǎn)擊鬧鐘可跳轉(zhuǎn)到鬧鐘操作界面(修改和刪除鬧鐘)。主頁(yè)啟動(dòng)后獲取輕量級(jí)數(shù)據(jù)庫(kù)中的鬧鐘定時(shí)數(shù)據(jù),并監(jiān)控?cái)?shù)據(jù)庫(kù)數(shù)據(jù)變化。
// MainViewModel.ets
public queryAlarmsTasker(callback: (alarms: Array< AlarmItem >) = > void) {
let that = this;
that.queryDatabaseAlarms(callback);
let preference = GlobalContext.getContext().getObject('preference') as PreferencesHandler;
preference.addPreferencesListener({
onDataChanged() {
that.queryDatabaseAlarms(callback);
}
} as PreferencesListener)
}
在AlarmList.ets中添加鬧鐘列表子組件,并綁定啟停、跳轉(zhuǎn)事件。
// AlarmList.ets
@Component
export default struct AlarmList {
@Link alarmItems: Array< AlarmItem >;
build() {
List({ space: DimensionUtil.getVp($r('app.float.alarm_list_space')) }) {
ForEach(this.alarmItems, (item: AlarmItem) = > {
ListItem() {
AlarmListItem({ alarmItem: item })
}.onClick(() = > {
router.pushUrl({ url: "pages/DetailIndex", params: { alarmItem: item } });
})
}, (item: AlarmItem) = > JSON.stringify(item))
}
.padding({
left: DimensionUtil.getVp($r('app.float.alarm_list_content_distance')),
right: DimensionUtil.getVp($r('app.float.alarm_list_content_distance'))
})
.listDirection(Axis.Vertical)
.layoutWeight(CommonConstants.DEFAULT_LAYOUT_WEIGHT)
.margin({ top: DimensionUtil.getVp($r('app.float.alarm_list_content_distance')) })
}
}
添加鬧鐘
添加鬧鐘,點(diǎn)擊界面底部鬧鐘添加按鈕,跳轉(zhuǎn)到鬧鐘操作界面(新增鬧鐘)。
在MainIndex.ets中為添加按鈕綁定跳轉(zhuǎn)事件。
// MainIndex.ets
@Entry
@Component
struct MainIndex {
...
build() {
Column() {
...
Button() {
Image($r('app.media.ic_add')).objectFit(ImageFit.Fill)
}
...
.onClick(() = > {
router.pushUrl({ url: "pages/DetailIndex" });
})
}
...
}
}
后臺(tái)代理提醒
后臺(tái)代理提醒,根據(jù)鬧鐘列表中的數(shù)據(jù)來(lái)設(shè)置(啟停)鬧鐘實(shí)例。
// MainViewModel.ets
// 開啟/關(guān)閉鬧鐘
public openAlarm(id: number, isOpen: boolean) {
for (let i = 0; i < this.alarms.length; i++) {
if (this.alarms[i].id === id) {
this.alarms[i].isOpen = isOpen;
if (isOpen) {
this.reminderService.addReminder(this.alarms[i]);
} else {
this.reminderService.deleteReminder(this.alarms[i].id);
}
let preference = GlobalContext.getContext().getObject('preference') as PreferencesHandler;
preference.set(CommonConstants.ALARM_KEY, JSON.stringify(this.alarms));
break;
}
}
}
鬧鐘詳情界面
鬧鐘操作界面分為新增和修改界面,其中在修改界面可刪除鬧鐘。具體包括以下模塊:
- 退出或保存詳情。
- 設(shè)置鬧鐘時(shí)間。
- 設(shè)置鬧鐘詳情。
- 提供后臺(tái)代理提醒能力
退出或保存詳情
點(diǎn)擊左上角“x”圖標(biāo)關(guān)閉操作界面,關(guān)閉鬧鐘操作界面子組件,點(diǎn)擊右上角“√”圖標(biāo),保存當(dāng)前設(shè)置并關(guān)閉操作界面。
在DetailIndex.ets入口頁(yè)面中引入頭部組件BackContainer,自定義了返回按鈕和返回邏輯操作,添加確定(“√”)子組件,并綁定點(diǎn)擊事件。
// DetailIndex.ets
build()
{
Column() {
...
Button() {
Image($r('app.media.ic_confirm')).objectFit(ImageFit.Fill)
}
.backgroundColor($r('app.color.trans_parent'))
.width(DimensionUtil.getVp($r('app.float.title_button_size')))
.height(DimensionUtil.getVp($r('app.float.title_button_size')))
.onClick(() = > {
this.viewModel.setAlarmRemind(this.alarmItem);
router.back();
})
...
}
}
// BackContainer.ets
build() {
Row() {
...
Text(this.header)
.fontSize(DimensionUtil.getFp($r('app.float.detail_title_font_size')))
.lineHeight(DimensionUtil.getVp($r('app.float.title_line_height')))
.margin({ left: DimensionUtil.getVp($r('app.float.title_margin')) })
.fontColor($r('app.color.grey_divider'))
.fontWeight(FontWeight.Bold)
Blank()
if (this.closer) {
this.closer();
}
}
.padding({
left: DimensionUtil.getVp($r('app.float.title_horizon_margin')),
right: DimensionUtil.getVp($r('app.float.title_horizon_margin'))
})
.height(DimensionUtil.getVp($r('app.float.page_title_height')))
.width(CommonConstants.FULL_LENGTH)
}
設(shè)置鬧鐘時(shí)間
設(shè)置鬧鐘提醒時(shí)間,在鬧鐘操作界面可通過(guò)滑動(dòng)選擇器設(shè)置鬧鐘的提醒時(shí)間(包括:時(shí)段、小時(shí)、分鐘)。詳情頁(yè)DetailIndex.ets中添加鬧鐘時(shí)間選擇器子組件DatePickArea.ets。
// DatePickArea.ets
@Component
export default struct DatePickArea {
build() {
Stack({ alignContent: Alignment.Center }) {
Row() {
ForEach(DetailConstant.DAY_DATA, (item: DayDataBean) = > {
TextPicker({ range: item.data, selected: item.delSelect })
.layoutWeight(CommonConstants.DEFAULT_LAYOUT_WEIGHT)
.backgroundColor($r('app.color.grey_light'))
.onChange((value: string, index: number) = > {
item.delSelect = index;
})
}, (item: DayDataBean) = > JSON.stringify(item))
}
}
.height(DimensionUtil.getVp($r('app.float.date_picker_height')))
.padding({
left: DimensionUtil.getVp($r('app.float.date_picker_padding_horizon')),
right: DimensionUtil.getVp($r('app.float.date_picker_padding_horizon'))
})
}
}
設(shè)置鬧鐘詳情
點(diǎn)擊詳情頁(yè)DetailIndex.ets設(shè)置條目組件SettingItem.ets,支持設(shè)置鬧鐘重復(fù)時(shí)間、鬧鐘名稱、重復(fù)次數(shù)和鬧鈴時(shí)長(zhǎng)。
// SettingItem.ets
build() {
Column() {
ForEach(this.settingInfo, (item: AlarmSettingBean, index: number | undefined) = > {
Divider()
...
Row() {
Text(item.title)...
Text(item.content)...
Image($r('app.media.ic_right'))...
}
...
.onClick(() = > {
this.showSettingDialog(item.sType);
})
}, (item: AlarmSettingBean, index: number | undefined) = > JSON.stringify(item) + index)
}
...
}
提供后臺(tái)代理提醒能力
導(dǎo)入系統(tǒng)提醒服務(wù)類ReminderService.ets,它由系統(tǒng)后臺(tái)代理提醒能力封裝,支持新增、修改、刪除系統(tǒng)鬧鐘功能,在設(shè)置、刪除鬧鐘后同步更新到輕量級(jí)數(shù)據(jù)庫(kù)中并刷新主頁(yè)頁(yè)面。
// DetailViewModel.ets
public async setAlarmRemind(alarmItem: AlarmItem) {
alarmItem.hour = this.getAlarmTime(CommonConstants.DEFAULT_SINGLE);
alarmItem.minute = this.getAlarmTime(CommonConstants.DEFAULT_DATA_PICKER_HOUR_SELECTION);
let index = await this.findAlarmWithId(alarmItem.id);
if (index !== CommonConstants.DEFAULT_NUMBER_NEGATIVE) { // 已存在,刪除原有提醒
this.reminderService.deleteReminder(alarmItem.id);
} else { // 不存在,以數(shù)據(jù)長(zhǎng)度為notificationId新增鬧鐘數(shù)據(jù)
index = this.alarms.length;
alarmItem.notificationId = index;
this.alarms.push(alarmItem);
}
this.reminderService.addReminder(alarmItem, (newId: number) = > {
alarmItem.id = newId;
alarmItem.isOpen = true;
this.alarms[index] = alarmItem;
let preference = GlobalContext.getContext().getObject('preference') as PreferencesHandler;
preference.set(CommonConstants.ALARM_KEY, JSON.stringify(this.alarms));
})
}
public async removeAlarmRemind(id: number) {
this.reminderService.deleteReminder(id);
let index = await this.findAlarmWithId(id);
if (index !== CommonConstants.DEFAULT_NUMBER_NEGATIVE) {
this.alarms.splice(index, CommonConstants.DEFAULT_SINGLE);
}
let preference = GlobalContext.getContext().getObject('preference') as PreferencesHandler;
preference.set(CommonConstants.ALARM_KEY, JSON.stringify(this.alarms));
}
審核編輯 黃宇
-
鴻蒙
+關(guān)注
關(guān)注
59文章
2500瀏覽量
43748 -
HarmonyOS
+關(guān)注
關(guān)注
79文章
2027瀏覽量
32086 -
OpenHarmony
+關(guān)注
關(guān)注
26文章
3818瀏覽量
18094
發(fā)布評(píng)論請(qǐng)先 登錄
鴻蒙OS開發(fā)實(shí)例:【頁(yè)面傳值跳轉(zhuǎn)】

HarmonyOS開發(fā)案例:【排行榜頁(yè)面】

HarmonyOS開發(fā)案例:【UIAbility內(nèi)和UIAbility間頁(yè)面的跳轉(zhuǎn)】

HarmonyOS應(yīng)用開發(fā)之頁(yè)面開發(fā)
【HarmonyOS應(yīng)用開發(fā)】Hello HarmonyOS到Hi HarmonyOS(3)
【資料】HarmonyOS應(yīng)用開發(fā)之頁(yè)面開發(fā)
#HarmonyOS征文# HarmonyOS實(shí)戰(zhàn)—實(shí)現(xiàn)注冊(cè)登錄和修改密碼頁(yè)面
HarmonyOS/OpenHarmony應(yīng)用開發(fā)-DevEco Studio新建項(xiàng)目的整體說(shuō)明
華為開發(fā)者HarmonyOS零基礎(chǔ)入門:系統(tǒng)能力調(diào)用示例

華為開發(fā)者分論壇HarmonyOS學(xué)生公開課-如何學(xué)習(xí)HarmonyOS應(yīng)用開發(fā)?

HarmonyOS 應(yīng)用開發(fā)-ClickableImageJsDome體驗(yàn)

面向HarmonyOS開發(fā)者的HarmonyOS 3.0 Beta介紹
小白指南:手把手教你用低代碼開發(fā)一個(gè)應(yīng)用頁(yè)面
HarmonyOS開發(fā)案例:【Ability內(nèi)頁(yè)面間的跳轉(zhuǎn)】

HarmonyOS Web頁(yè)面加載的原理和優(yōu)化方法

評(píng)論