介紹
本篇Codelab基于Stage模型實現帶有卡片的計步應用,用于介紹卡片的開發及生命周期實現。需要完成以下功能:
- 消息通知欄,通知用戶今天所行走步數。
- 元服務卡片,在桌面上添加2x2或2x4規格元服務卡片,能看到步數變化,也能看到當天所行走的進度。
- 關系型數據庫,用于查詢,添加用戶行走的數據。
相關概念
- [消息通知]:提供通知管理的能力,包括發布、取消發布通知,創建、獲取、移除通知通道,訂閱、取消訂閱通知,獲取通知的使能狀態、角標使能狀態,獲取通知的相關信息等。
- [關系型數據庫]:關系型數據庫基于SQLite組件提供了一套完整的對本地數據庫進行管理的機制,對外提供了一系列的增、刪、改、查等接口,也可以直接運行用戶輸入的SQL語句來滿足復雜的場景需要。
- [元服務卡片開發]:卡片是一種界面展示形式,可以將應用的重要信息或操作前置到卡片,以達到服務直達、減少體驗層級的目的。
- 卡片提供方:顯示卡片內容,控制卡片布局以及控件點擊事件。
- 卡片使用方:顯示卡片內容的宿主應用,控制卡片在宿主中展示的位置。
- 卡片管理服務:用于管理系統中所添加卡片的常駐代理服務,包括卡片對象的管理與使用,以及卡片周期性刷新等。
環境搭建
軟件要求
- [DevEco Studio]版本:DevEco Studio 3.1 Release。
- OpenHarmony SDK版本:API version 9。
硬件要求
- 開發板類型:[潤和RK3568開發板]。
- OpenHarmony系統:3.2 Release。
環境搭建
完成本篇Codelab我們首先要完成開發環境的搭建,本示例以RK3568開發板為例,參照以下步驟進行:
- [獲取OpenHarmony系統版本]:標準系統解決方案(二進制)。以3.2 Release版本為例:
- 搭建燒錄環境。
- [完成DevEco Device Tool的安裝]
- [完成RK3568開發板的燒錄]
- 搭建開發環境。
- 開始前請參考[工具準備][
qr23.cn/AKFP8k
]點擊或者復制轉到,完成DevEco Studio的安裝和開發環境配置。 - 開發環境配置完成后,請參考[使用工程向導]創建工程(模板選擇“Empty Ability”)。
- 工程創建完成后,選擇使用[真機進行調測]。
- 開始前請參考[工具準備][
代碼結構解讀
本篇Codelab只對核心代碼進行講解,對于完整代碼,我們會在gitee中提供。
HarmonyOS與OpenHarmony鴻蒙開發文檔
+mau123789是v直接領取。
├──entry/src/main/ets // 代碼區
│ ├──common
│ │ ├──constants
│ │ │ └──CommonConstants.ets // 常量類
│ │ ├──database
│ │ │ ├──Form.ets // 數據庫卡片操作
│ │ │ └──SensorData.ets // 數據庫行走步數操作
│ │ └──utils
│ │ ├──ChartDataUtils.ets // 圖表數據操作工具類
│ │ ├──DatabaseUtils.ets // 數據庫工具類
│ │ ├──DateUtils.ets // 日期工具類
│ │ ├──GlobalContext.ets // 項目工具類
│ │ └──Logger.ets // 日志打印工具類
│ ├──entryability
│ │ └──EntryAbility.ets // 程序入口類
│ ├──entryformability
│ │ └──EntryFormAbility.ets // 卡片創建,更新,刪除操作類
│ ├──pages
│ │ └──MainPage.ets // 主界面
│ └──viewmodel
│ ├──ChartPoint.ets // 圖表點類
│ ├──ChartValues.ets // 圖表值類
│ ├──FormData.ets // 表單數據類
│ └──PointStyle.ets // 圖表點樣式類
├──entry/src/main/js // js代碼區
│ ├──card2x2 // 2x2卡片目錄
│ ├──card2x4 // 2x4卡片目錄
│ ├──common // 卡片資源目錄
│ └──i18n // 卡片國際化目錄
└──entry/src/main/resources // 資源文件目錄
關系型數據庫
元服務卡片需要用數據庫保存不同時間、不同卡片的數據,而且在添加多張卡片情況下,需要保持數據同步刷新。因此需要創建兩張表,一張是保存卡片信息,另一張是記錄當天行走步數。
- 數據庫創建使用的SQLite。
// CommonConstants.ets
// 表單SQLite
static readonly CREATE_TABLE_FORM: string = 'CREATE TABLE IF NOT EXISTS Form ' +
'(id INTEGER PRIMARY KEY AUTOINCREMENT, formId TEXT NOT NULL, formName TEXT NOT NULL, dimension INTEGER)';
// 行走步數SQLite
static readonly CREATE_TABLE_SENSOR_DATA: string = 'CREATE TABLE IF NOT EXISTS SensorData ' +
'(id INTEGER PRIMARY KEY AUTOINCREMENT, date TEXT NOT NULL, stepsValue INTEGER)';
- 在EntryAbility的onCreate方法通過DatabaseUtils.createRdbStore方法創建數據庫,并創建相應的表。
// EntryAbility.ets
onCreate(want: Want, param: AbilityConstant.LaunchParam): void {
GlobalContext.getContext().setObject('abilityWant', want);
GlobalContext.getContext().setObject('abilityParam', param);
DatabaseUtils.createRdbStore(this.context).then((rdbStore: Object | undefined) = > {
// 添加前三天行走模擬數據
DatabaseUtils.addSimulationData(rdbStore as DataRdb.RdbStore);
}).catch((error: Error) = > {
...
});
}
消息通知
需要在MainPage的aboutToAppear調用requestNotification方法申請通知欄權限,效果如圖所示:
// MainPage.ets
aboutToAppear() {
// 申請通知欄權限
this.requestNotification();
...
}
requestNotification() {
Notification.requestEnableNotification().then(() = > {
...
}).catch((err: Error) = > {
...
});
}
通過aboutToAppear的setInterval方法開啟定時器,當定時器到10秒后,通過DatabaseUtils.sendNotifications方法發送消息到通知欄。效果如圖所示:
// DatabaseUtils.ets
// 發送通知
sendNotifications(stepsValue: string, notificationId: number) {
// 獲取當前系統語言
let notificationBarTitle: string;
let Language: string = I18n.System.getSystemLanguage();
// 判斷是否為中文
if (Language.match(CommonConstants.CHINESE_LANGUAGE)) {
notificationBarTitle = CommonConstants.NOTIFICATIONS_TITLE_GONE_TODAY_ZH +
stepsValue + CommonConstants.NOTIFICATIONS_TITLE_STEPS_ZH;
} else {
notificationBarTitle = CommonConstants.NOTIFICATIONS_TITLE_GONE_TODAY_EN +
stepsValue + CommonConstants.NOTIFICATIONS_TITLE_STEPS_EN;
}
// 發布NotificationRequest.
Notification.publish({
id: CommonConstants.NOTIFICATIONS_ID,
content: {
contentType: Notification.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT,
normal: {
title: notificationBarTitle,
text: ''
}
}
}).then(() = > {
...
});
}
元服務卡片
使用元服務卡片分為四步:創建、初始化、更新、刪除。
創建元服務卡片目錄
- 在main目錄下,點擊鼠標右鍵 > New > Service Widget。
- 然后選擇第一個選項下面帶有Hello World字樣,點擊下一步Next。
- 填寫卡片名字(Service widget name)、卡片介紹(Description)、是否開啟低代碼開發(Enable Super Visual)、開發語言(ArkTS和JS)、支持卡片規格(Support dimension)、關聯表單(Ability name)點擊Finish完成創建。如需創建多個卡片目錄重新按照步驟1執行。
- 創建完卡片后,同級目錄出現js目錄,然后開發者在js目錄下使用hml+css+json開發js卡片頁面。
初始化元服務卡片
應用選擇添加元服務卡片到桌面后,在EntryFormAbility的onAddForm方法進行卡片初始化操作,效果如圖所示:
// EntryFormAbility.ets
onAddForm(want: Want) {
let formId: string = want.parameters !== undefined ?
want.parameters[CommonConstants.FORM_PARAM_IDENTITY_KEY] as string : '';
let formName: string = want.parameters !== undefined ?
want.parameters[CommonConstants.FORM_PARAM_NAME_KEY] as string : '';
let dimensionFlag: number = want.parameters !== undefined ?
want.parameters[CommonConstants.FORM_PARAM_DIMENSION_KEY] as number : 0;
// 創建數據庫
DatabaseUtils.createRdbStore(this.context).then((rdbStore: Object | undefined) = > {
// 存儲卡片信息
let form: Form = new Form();
form.formId = formId;
form.formName = formName;
form.dimension = dimensionFlag;
...
DatabaseUtils.insertForm(form, rdbStore as DataRdb.RdbStore);
getToDaySteps(rdbStore as DataRdb.RdbStore, dimensionFlag, formId);
}).catch((error: Error) = > {
...
});
...
// 初始化卡片數據
let formData: FormData = new FormData();
formData.percent = 0;
formData.steps = 0;
return FormBindingData.createFormBindingData(formData);
};
更新元服務卡片
- 初始化加載主頁面布局之前,在MainPage的aboutToAppear方法中,調用setInterval方法開啟定時器。時間到則先通過DatabaseUtils.insertValues方法把步數插入到數據庫,再通過DatabaseUtils.updateForms方法更新卡片步數。
// MainPage.ets
aboutToAppear() {
...
DatabaseUtils.getSensorData(rdbStoreValue, DateUtils.getDate(0))
.then((sensorData: SensorData) = > {
if (sensorData) {
this.stepsValue = sensorData.stepsValue;
}
// 開啟定時器
this.intervalId = setInterval(() = > {
...
DatabaseUtils.insertValues(this.stepsValue, rdbStoreValue);
DatabaseUtils.updateForms(this.stepsValue, rdbStoreValue);
}, CommonConstants.INTERVAL_DELAY_TIME);
...
});
}
// DatabaseUtils.ets
updateForms(stepValue: number, rdbStore: DataRdb.RdbStore) {
let predicates: DataRdb.RdbPredicates =
new DataRdb.RdbPredicates(CommonConstants.TABLE_FORM);
// 查詢卡片
rdbStore.query(predicates).then((resultSet: DataRdb.ResultSet) = > {
...
// 查詢第一行
resultSet.goToFirstRow();
do {
let formId: string = resultSet.getString(resultSet.getColumnIndex(CommonConstants.FIELD_FORM_ID));
let dimension: number = resultSet.getLong(resultSet.getColumnIndex(CommonConstants.FIELD_DIMENSION));
ChartDataUtils.getFormData(formId, stepValue, dimension, rdbStore)
.then((formData: FormData) = > {
// 更新多張卡片
FormProvider.updateForm(formData.formId, FormBindingData.createFormBindingData(formData))
.catch((error: Error) = > {
...
});
}).catch((error: Error) = > {
...
});
} while (resultSet.goToNextRow());
resultSet.close();
}).catch((error: Error) = > {
...
});
}
- 卡片添加到桌面后,在EntryFormAbility的onAddForm方法中,調用formProvider.setFormNextRefreshTime方法設置倒計時。時間到了則通過updateSensorData方法更新卡片步數。
// EntryFormAbility.ets
onAddForm(want: Want) {
...
// 五分鐘倒計時
formProvider.setFormNextRefreshTime(formId, CommonConstants.FIVE_MINUTES, (error, data) = > {
...
});
}
onUpdateForm(formId: string) {
// 更新步數
this.updateSensorData();
...
}
updateSensorData() {
DatabaseUtils.createRdbStore(this.context).then((rdbStore: Object | undefined) = > {
...
// 獲取今天步數
let getSensorData: Promise< SensorData > =
DatabaseUtils.getSensorData(rdbStore as DataRdb.RdbStore, DateUtils.getDate(0));
getSensorData.then((sensorData: SensorData) = > {
let stepValue: number = 0;
if (sensorData) {
stepValue = sensorData.stepsValue;
}
// 更新卡片數據
DatabaseUtils.updateForms(stepValue, rdbStore);
}).catch((error: Error) = > {
...
});
}).catch((error: Error) = > {
...
});
}
- 通過src/main/resources/base/profile/form_config.json配置文件,根據updateDuration或者scheduledUpdateTime字段配置刷新時間。updateDuration優先級高于scheduledUpdateTime,兩者同時配置時,以updateDuration配置的刷新時間為準。當配置的刷新時間到了,系統調用onUpdateForm方法進行更新。
// form_config.json
{
// 卡片的類名
"name": "card2x2",
// 卡片的描述
"description": "This is a service widget.",
// 卡片對應完整路徑
"src": "./js/card2x2/pages/index/index",
// 定義與顯示窗口相關的配置
"window": {
"designWidth": 720,
"autoDesignWidth": true
},
// 卡片的主題樣式
"colorMode": "auto",
// 是否為默認卡片
"isDefault": true,
// 卡片是否支持周期性刷新
"updateEnabled": true,
// 采用24小時制,精確到分鐘
"scheduledUpdateTime": "00:00",
// 當取值為0時,表示該參數不生效,當取值為正整數N時,表示刷新周期為30*N分鐘。
"updateDuration": 1,
// 卡片默認外觀規格
"defaultDimension": "2*2",
// 卡片支持外觀規格
"supportDimensions": [
"2*2"
]
}
// EntryFormAbility.ets
onUpdateForm(formId: string) {
// 更新步數
updateSensorData();
...
}
刪除元服務卡片
當用戶需要刪除元服務卡片時,可以在EntryFormAbility的onRemoveForm方法中,通過DatabaseUtils.deleteFormData方法刪除數據庫中對應的卡片信息。
// EntryFormAbility.ets
onRemoveForm(formId: string) {
DatabaseUtils.createRdbStore(this.context).then((rdbStore: Object | undefined) = > {
...
// 刪除數據庫中對應的卡片信息
DatabaseUtils.deleteFormData(formId, rdbStore as DataRdb.RdbStore);
}).catch((error: Error) = > {
...
});
}
// DatabaseUtils.ets
deleteFormData(formId: string, rdbStore: DataRdb.RdbStore) {
let predicates: DataRdb.RdbPredicates = new DataRdb.RdbPredicates(CommonConstants.TABLE_FORM);
predicates.equalTo(CommonConstants.FIELD_FORM_ID, formId);
rdbStore.delete(predicates).catch((error: Error) = > {
...
});
}
審核編輯 黃宇
-
數據庫
+關注
關注
7文章
3846瀏覽量
64685 -
計步器
+關注
關注
4文章
76瀏覽量
19983 -
HarmonyOS
+關注
關注
79文章
1982瀏覽量
30575 -
OpenHarmony
+關注
關注
25文章
3744瀏覽量
16578
發布評論請先 登錄
相關推薦
評論