【共创季稿事节】 HarmonyOS7 互动卡片开发实践:用 CardActionHandler 管住桌面卡片动作
金牌创作者
·
文章目录
前言
普通卡片能点播放、收藏、开始运动,但这些动作不应该直接写在卡片 UI 里。卡片只是桌面组件,真正的业务应该回到应用侧处理。
当前项目把这件事统一交给 CardActionHandler。这篇就带你把这条链路看明白。
效果图
音乐卡片里的播放、切歌、收藏这些动作,最后都应该被应用侧统一接住再处理。

先看这几个文件
entry/src/main/ets/utils/ActionUtils.etsentry/src/main/ets/utils/CardActionHandler.etsentry/src/main/ets/entryability/EntryAbility.etsentry/src/main/ets/model/common/FormCardConstant.ets
这四个文件连起来就是:卡片发动作,应用接动作,业务分发。
动作类型先统一定义
FormCardConstant.ets 里定义了:
export enum CardActionType {
PLAY_ACTION = 'playAction',
COLLECT_ACTION = 'collectAction',
REQUEST_UPDATE = 'requestUpdate',
EXERCISE_ACTION = 'exerciseAction',
}
小白可以这样理解:
PLAY_ACTION:音乐播放、暂停、上一首、下一首。COLLECT_ACTION:音乐收藏或取消收藏。REQUEST_UPDATE:新卡片主动请求当前数据。EXERCISE_ACTION:运动卡片开始、结束、重置。
把动作写成枚举的好处是:不容易写错字符串,也方便全项目搜索。
卡片侧怎么发 CALL
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。
CALL 表示“我要调用应用侧能力”。目标 Ability 是:
abilityName: ENTRY_ABILITY
也就是项目里的 LiveCardAbility。
EntryAbility 负责注册回调
EntryAbility.ets 里有:
CardActionHandler.setContext(this.context);
this.callee.on('cardAction', CardActionHandler.getHandler());
这两行很关键。
第一行把 context 交给 CardActionHandler,后面它才能访问数据库、文件、事件总线等能力。
第二行注册 cardAction 回调。卡片侧发来的 method: 'cardAction',最终就会走到这里。
应用销毁时还要解绑:

this.callee.off('cardAction');
CardActionHandler 怎么分发
核心代码是:
private cardActionCall = (data: rpc.MessageSequence): null => {
let params: Record<string, string> = JSON.parse(data.readString());
switch (params.actionType) {
case CardActionType.PLAY_ACTION:
this.handlePlayAction(params);
break;
case CardActionType.COLLECT_ACTION:
this.handleCollectAction(params);
break;
case CardActionType.REQUEST_UPDATE:
this.handleRequestUpdate(params);
break;
case CardActionType.EXERCISE_ACTION:
this.handleExerciseAction(params);
break;
}
return null;
};

它的职责很清楚:解析参数,然后根据 actionType 分发。
不要把大量业务逻辑都塞进 cardActionCall()。项目里做得不错,每个动作都有独立方法。
以播放动作为例
private handlePlayAction(params: Record<string, string>): void {
if (params.playActionType) {
let playActionType: PlayActionType = params.playActionType as PlayActionType;
MediaService.getInstance().initAndPlayByAction(playActionType);
}
}
这段代码没有自己操作播放器,而是交给 MediaService。
这就是合理分层:
CardActionHandler:分发动作
MediaService:处理音乐业务
FormUtils:刷新桌面卡片
SongRdbHelper:保存歌曲数据
新手加动作怎么做
假设你要加一个天气刷新动作,可以按这个顺序:
- 在
CardActionType增加:
WEATHER_REFRESH = 'weatherRefresh'
- 在
ActionUtils增加发送方法:
public refreshWeather(component: object, formId: string): void {
postCardAction(component, {
action: FormCarAction.CALL,
abilityName: ENTRY_ABILITY,
params: {
method: 'cardAction',
actionType: CardActionType.WEATHER_REFRESH,
formId
}
});
}
- 在
CardActionHandler里加分支:
case CardActionType.WEATHER_REFRESH:
this.handleWeatherRefresh(params);
break;
排查清单
动作没反应时这样查:
- 卡片点击事件有没有执行。
postCardAction的method是否是cardAction。EntryAbility是否注册了this.callee.on('cardAction', ...)。CardActionHandler是否收到actionType。switch里有没有对应分支。- 真正业务方法有没有报错。
写在最后
CardActionHandler 的价值是“收口”。卡片越多,动作越多,如果没有统一入口,项目很快会乱。
小白记住:卡片发动作,Handler 分发动作,业务服务处理动作。
更多推荐



所有评论(0)