今日事笔记的亮点是桌面组件。
在鸿蒙系统上桌面组件是服务卡片,流程跟安卓类似。
安卓:
WidgetProvider -> RemoveView -> 桌面渲染
鸿蒙:
EntryFormAbility -> formProvider -> Component -> 桌面渲染
不同的地方:
1:数据提供者:
安卓通过RemoveViewService可以做到数据懒加载驱动RemoveView刷新
鸿蒙通过formBindingData驱动数据刷新组件。期间还需要用到LocalStorageProp,这是个单向驱动数据模型,可以装载数据对象。
还要通过dataPreferences持久化保存formId,即卡片id,以便在程序内实时刷新卡片。
这里formId的保存试过其他方式,比如PersistentStorage, MMKV,但好像都不太好使。
2:添加卡片方式:
安卓是的组件是同意从桌面入口桌面小组件添加。华为是直接可见式添加,即添加过程中会模拟添加一次,在EntryFormAbility中打印日志可以看出,预览添加时会生成formId,添加后会移除formId。实际添加到桌面后会最终onAddForm
下面是EntryFormAbility的代码:
import { formBindingData, FormExtensionAbility, formInfo } from '@kit.FormKit';
import { Want } from '@kit.AbilityKit';
import TodoModel from '../db/TodoModel';
import { CardManager, FormDataClass } from './CardManager';
import LogUtils from '../common/LogUtils';
import { DbManager } from '../db/DbManager';
import TodoStatus from '../net/model/TodoStatus';
import { CardConfigParameter } from '../todaytodo/viewmodel/CardConfigParameter';
export default class EntryFormAbility extends FormExtensionAbility {
onAddForm(want: Want) {
this.parseFormId(want)
let data = new FormDataClass()
data.itemNumber = 0
data.cardListData = []
data.cardConfig = new CardConfigParameter()
return formBindingData.createFormBindingData(data);
}
private parseFormId(want: Want) {
let formId: string = '';
if (want.parameters) {
formId = JSON.stringify(want.parameters[formInfo.FormParam.IDENTITY_KEY]);
CardManager.get().saveCardId(this.context, formId)
}
}
onCastToNormalForm(formId: string) {
CardManager.get().saveCardId(this.context, formId)
CardManager.get().updateCard(this.context, formId)
}
onUpdateForm(formId: string) {
CardManager.get().updateCard(this.context, formId)
}
onFormEvent(formId: string, message: string) {
LogUtils.debug("on from event: " + message)
if (message) {
let data: object = JSON.parse(message)
// 处理待办
if (data['type'] == CardManager.EVENT_DONE) {
LogUtils.debug("done todo")
let model: TodoModel = JSON.parse(JSON.stringify(data["item"]))
DbManager.getInstanceAsync(this.context, (db)=> {
model.status = TodoStatus.HISTORY
db.update(model)
CardManager.get().updateCard(this.context, formId)
LogUtils.debug("update card success")
})
} else if (data['type'] == CardManager.EVENT_ADD) {
// 添加待办
LogUtils.debug("add todo event")
} else {
LogUtils.debug("enter todo main page")
}
}
}
onRemoveForm(formId: string) {
CardManager.get().clearCardId(this.context)
}
onAcquireFormState(want: Want) {
this.parseFormId(want)
return formInfo.FormState.READY;
}
};
这里主要捕捉到卡片添加,并初始化一些数据,另一个是接收卡片中的点击事件:
postCardAction(this, {
action: this.ACTION_TYPE,
abilityName: this.ABILITY_NAME,
params: {
message: this.MESSAGE
}
});
postCardAction是卡片组件中特有的一个接口。
对卡片的一些信息持久化和数据更新,写在CardManager中:
import { DbManager } from '../db/DbManager'
import { formBindingData, formProvider } from '@kit.FormKit'
import TodoModel from '../db/TodoModel'
import TodoStatus from '../net/model/TodoStatus'
import LogUtils from '../common/LogUtils'
import dataPreferences from '@ohos.data.preferences';
import EntryContext from 'LibUser/src/main/ets/commons/EntryContext'
import { CardConfigParameter } from '../todaytodo/viewmodel/CardConfigParameter'
import JSON from '@ohmos/json-bigint';
export class FormDataClass {
title: string = "今日事"
itemNumber: number = 0
cardListData: TodoModel[] = []
cardConfig: CardConfigParameter = new CardConfigParameter()
}
export class CardManager {
private static instance: CardManager = new CardManager()
static get(): CardManager {
return CardManager.instance
}
static EVENT_DONE = "event_done"
static EVENT_ADD = "event_add"
static EVENT_MAIN = "event_main"
static KEY_CARD_ID = "card_id"
static KEY_CARD_CONFIG = "card_config"
saveCardConfig(context: Context, config: CardConfigParameter) {
let pref = dataPreferences.getPreferencesSync(context, { name: CardManager.KEY_CARD_ID });
pref.putSync(CardManager.KEY_CARD_CONFIG, JSON.stringify(config, undefined, undefined))
pref.flush()
}
getCardConfig(context: Context): CardConfigParameter {
let pref = dataPreferences.getPreferencesSync(context, { name: CardManager.KEY_CARD_ID });
let cardConfigStr = pref.getSync(CardManager.KEY_CARD_CONFIG, '') as string
if (cardConfigStr == '') {
return new CardConfigParameter()
}
let cardConfig: CardConfigParameter = JSON.parse(cardConfigStr)
return cardConfig
}
saveCardId(context: Context, formId: string) {
let pref = dataPreferences.getPreferencesSync(context, { name: CardManager.KEY_CARD_ID });
pref.putSync(CardManager.KEY_CARD_ID, formId)
pref.flush()
}
clearCardId(context: Context) {
let pref = dataPreferences.getPreferencesSync(context, { name: CardManager.KEY_CARD_ID });
pref.putSync(CardManager.KEY_CARD_ID, '')
pref.flush()
}
getFormId(context: Context): string {
let pref = dataPreferences.getPreferencesSync(context, { name: CardManager.KEY_CARD_ID });
let cardId = pref.getSync(CardManager.KEY_CARD_ID, '') as string
return cardId
}
updateCard(context?: Context, formId?: string) {
if (context) {
DbManager.getInstanceAsync(context, (db) => {
this.updateCardData(context, db, formId)
})
} else {
this.updateCardData(EntryContext.getContext() as Context, DbManager.getInstance(), formId)
}
}
updateCardData(context: Context, db: DbManager, cardId?: string) {
if (!cardId) {
cardId = this.getFormId(context)
}
if (cardId) {
db.getRecordByStatus(TodoStatus.TODAY).then(list => {
let dataClass = new FormDataClass()
dataClass.cardListData = Array.from(list)
dataClass.itemNumber = list.length
dataClass.cardConfig = this.getCardConfig(context)
let formInfo: formBindingData.FormBindingData = formBindingData.createFormBindingData(dataClass);
formProvider.updateForm(cardId, formInfo)
LogUtils.debug("update card form complete.")
})
this.saveCardId(context, cardId)
} else {
LogUtils.error("card id is empty")
}
}
}
FormDataClass用来做数据通信,把需要配置和动态刷新的数据用FormDataClass来传递。
整个流程就这么多,具体可看源码:github
这里还有个遗留问题:
卡片背景透明度不知道怎么设置,看FA模型的网上有方案,但Stage不知道咋处理。直接设置Component的最外层组件背景色不生效。应该是外面还有一层包裹。