【共创季稿事节】HarmonyOS7 互动卡片开发实践:音乐卡片收藏状态如何同步到所有卡片
文章目录
前言
收藏按钮不是改一下心形图标那么简单。桌面上可能有多张音乐卡片,应用页面里也可能正在显示歌曲详情。用户在一张卡片上点收藏,其他地方也要同步。
当前项目用 SongRdbHelper + FormUtils + eventHub 把这件事串起来。
效果图
收藏状态虽然只是一个小图标,但它要同步到所有音乐卡片和应用页面,不能只改当前这一张。

先看这些文件
entry/src/main/ets/widget/pages/MusicCard.etsentry/src/main/ets/utils/ActionUtils.etsentry/src/main/ets/utils/CardActionHandler.etsentry/src/main/ets/database/SongRdbHelper.etsentry/src/main/ets/utils/FormUtils.ets
卡片上的收藏按钮
MusicCard.ets 里有:
SymbolGlyph(this.isCollected ? $r('sys.symbol.heart_fill') : $r('sys.symbol.heart'))
.fontSize(IconSize.MEDIUM)
.symbolEffect(new ReplaceSymbolEffect(EffectScope.WHOLE), true)
.onClick(() => {
if (this.isCollected) {
ActionUtils.collectAction(this, CollectAction.UNCOLLECTED, this.formId, this.songId);
} else {
ActionUtils.collectAction(this, CollectAction.COLLECTED, this.formId, this.songId);
}
});
这段代码不直接改 isCollected。
它只是根据当前状态,发一个收藏或取消收藏的动作。真正状态由应用侧处理后再刷新回来。

collectAction 发了什么
ActionUtils.ets 里:
public collectAction(component: object, type: string, formId: string, songId: string): void {
postCardAction(component, {
action: FormCarAction.CALL,
abilityName: ENTRY_ABILITY,
params: {
method: 'cardAction',
actionType: CardActionType.COLLECT_ACTION,
collectActionType: type,
formId: formId,
songId: songId,
},
});
}
收藏是应用业务,所以这里用 CALL 发给 ENTRY_ABILITY。
参数里最重要的是:
collectActionType:收藏还是取消收藏。songId:哪首歌。formId:从哪张卡片发起。
CardActionHandler 更新数据库
CardActionHandler.ets 里:
private handleCollectAction(params: Record<string, string>): void {
if (params.collectActionType && this.context) {
let songRdbHelper = SongRdbHelper.getInstance(this.context);
if (params.collectActionType === CollectAction.COLLECTED) {
songRdbHelper.updateCollected(params.songId, CollectAction.COLLECTED);
FormUtils.updateCardCollectStatus(this.context, true);
this.context.eventHub.emit('collected', params.songId, CollectAction.COLLECTED);
} else {
songRdbHelper.updateCollected(params.songId, CollectAction.UNCOLLECTED);
FormUtils.updateCardCollectStatus(this.context, false);
this.context.eventHub.emit('collected', params.songId, CollectAction.UNCOLLECTED);
}
}
}

这段代码做了三件事。
第一,更新 RDB:
songRdbHelper.updateCollected(params.songId, CollectAction.COLLECTED);
这保证收藏状态有持久化结果。
第二,刷新所有音乐卡片:
FormUtils.updateCardCollectStatus(this.context, true);
第三,通知应用内页面:
this.context.eventHub.emit('collected', params.songId, CollectAction.COLLECTED);
为什么要同步所有音乐卡
用户桌面可能放了多张 MusicCard。如果只更新当前 formId,其他音乐卡还是旧状态,就会出现“同一首歌,有的显示收藏,有的没收藏”。
项目里 FormUtils.updateCardCollectStatus() 会查出所有 MusicCard:
let formList: FormInfo[] = await FormRdbHelper.getInstance(context).queryFormByName('MusicCard');
formList.forEach((formInfo) => {
this.updateForm(formInfo.formId, updateData);
});
这就是批量同步。
为什么还要 eventHub
updateForm() 只能刷新桌面卡片。应用内页面不会自动收到桌面卡片的更新。
所以项目用:
this.context.eventHub.emit('collected', params.songId, CollectAction.COLLECTED);
应用页面可以监听这个事件,刷新自己的 UI。
小白可以这样记
卡片点击收藏
-> ActionUtils 发 COLLECT_ACTION
-> CardActionHandler 接收
-> SongRdbHelper 更新数据库
-> FormUtils 刷新所有桌面音乐卡
-> eventHub 通知应用页面
这就是收藏同步的完整闭环。
常见坑
1. 只改当前卡片 UI
这样会导致其他卡片不同步。收藏状态必须回到应用侧统一处理。
2. 不写数据库
应用重启后收藏状态丢失。
3. 忘记通知应用页面
桌面卡片更新了,但应用内详情页还是旧状态。
写在最后
收藏功能看起来小,但它是非常典型的多端同步问题。
小白记住:最终状态放 RDB,桌面卡片靠 FormUtils 刷新,应用页面靠事件通知。
更多推荐



所有评论(0)