【共创季稿事节】 HarmonyOS7 互动卡片开发实践:音乐卡片播放按钮从点击到刷新完整流程
文章目录
前言
音乐卡片的播放按钮看起来只是一个图标,但背后串了很多东西:普通卡片发动作、应用侧播放音乐、必要时展开 LiveForm、最后还要刷新桌面卡片状态。
这篇就按项目代码,把播放按钮这条链路讲清楚。
效果图

音乐卡片的核心就是播放、暂停、上一首、下一首这一组反馈。先看效果,再拆它背后怎么从点击一路走到刷新卡片。

先看这些文件
entry/src/main/ets/widget/pages/MusicCard.etsentry/src/main/ets/utils/ActionUtils.etsentry/src/main/ets/utils/CardActionHandler.etsentry/src/main/ets/viewmodel/music/MediaService.etsentry/src/main/ets/utils/FormUtils.ets
小白看音乐卡片时,不要只盯着 MusicCard.ets。它只是入口,真正业务在后面。
播放按钮代码
MusicCard.ets 里有:
SymbolGlyph(this.isPlay ? $r('sys.symbol.pause_fill') : $r('sys.symbol.play_fill'))
.fontSize(IconSize.MEDIUM)
.symbolEffect(new ReplaceSymbolEffect(EffectScope.WHOLE), true)
.onClick(() => {
if (this.isPlay) {
ActionUtils.playByAction(this, PlayActionType.PAUSE, this.formId);
} else {
ActionUtils.playByAction(this, PlayActionType.PLAY, this.formId);
ActionUtils.requestOverFlowWithAction(
this,
LiveCardScale.MUSIC_WIDTH,
LiveCardScale.MUSIC_HEIGHT,
LIVE_CARD_DURATION,
'PLAY',
this.songId
);
}
});

这段代码分两种情况。
如果正在播放:点击就是暂停,只调用 playByAction(PAUSE)。
如果没在播放:点击就是播放,同时调用 requestOverFlowWithAction() 展开音乐 LiveForm 动画。
为什么暂停不展开 LiveForm
这是一个体验设计。
播放时给用户一个动效反馈,很合理。暂停时如果也展开大动画,就会有点吵。项目这里选择“播放才展开”,更克制。
小白做自己的卡片时也要注意:不是所有点击都要展开 LiveForm。只有需要强化反馈的动作才展开。
playByAction 发给应用侧
ActionUtils.ets 里:
public playByAction(component: object, type: PlayActionType, formId: string): void {
postCardAction(component, {
action: FormCarAction.CALL,
abilityName: ENTRY_ABILITY,
params: {
method: 'cardAction',
actionType: CardActionType.PLAY_ACTION,
playActionType: type,
formId: formId,
},
});
}
这里用的是 CALL,目标是主应用 ENTRY_ABILITY。
因为播放音乐是应用能力,不是 EntryFormAbility 的事情。
CardActionHandler 接住播放动作
CardActionHandler.ets 里:

private handlePlayAction(params: Record<string, string>): void {
if (params.playActionType) {
let playActionType: PlayActionType = params.playActionType as PlayActionType;
MediaService.getInstance().initAndPlayByAction(playActionType);
}
}
这里没有直接写播放器逻辑,而是交给 MediaService。
这很适合小白学习:Handler 只负责分发,不要什么都干。
MediaService 负责真正播放
MediaService 负责根据 PlayActionType 做具体业务,比如播放、暂停、上一首、下一首。
虽然这里不展开播放器内部细节,但你要知道它做完业务后,还会通过 FormUtils 更新卡片。
大概链路是:
播放状态变化
-> 更新当前歌曲和播放状态
-> MusicFileStore 保存当前歌曲
-> FormUtils.updateMusicControlCards 更新所有 MusicCard
卡片怎么刷新播放状态

MusicCard.ets 接收:
@LocalStorageProp('isPlay') isPlay: boolean = false;
@LocalStorageProp('title') title: string = 'SongName';
@LocalStorageProp('singer') singer: string = 'Singer';
@LocalStorageProp('musicCover') musicCover: Resource = $r('app.media.ic_dream');
应用侧通过 FormUtils.updateMusicControlCards() 下发这些字段。
所以播放按钮图标能从播放变暂停,不是按钮自己切状态,而是应用侧业务完成后刷新卡片。
requestOverFlowWithAction 的作用
播放时还会调用:
ActionUtils.requestOverFlowWithAction(..., 'PLAY', this.songId)
它多传了一个 PLAY。后面 MusicLiveCardAbility 会读取这个动作,告诉 MusicLiveCard 播放“跳舞”动画。
如果是上一首/下一首,则传 PREVIOUS 或 NEXT,LiveForm 就会播放切歌动画。
小白排查播放按钮
按钮点了没反应时,按这个顺序查:
MusicCard.ets的.onClick()有没有执行。formId是否为空。ActionUtils.playByAction()是否发出PLAY_ACTION。EntryAbility是否注册cardAction。CardActionHandler.handlePlayAction()是否被调用。MediaService是否初始化。FormUtils.updateMusicControlCards()是否刷新卡片。
写在最后
音乐播放按钮不是一个孤立按钮,它是“卡片 UI -> 应用业务 -> 卡片刷新 -> LiveForm 动画”的完整闭环。
小白记住这句话:按钮只发意图,真正状态以应用侧刷新为准。
更多推荐



所有评论(0)