鸿蒙 ArkTS 卡片开发实战:音乐播放器组件实现
通过构建一个音乐播放器卡片,演示了鸿蒙应用卡片的三种核心事件交互:message(刷新状态)、router(跳转页面)和 call(后台任务)。文章详细讲解了项目结构、代码实现(包括卡片 UI、FormExtensionAbility、UIAbility 及后台服务)以及配置文件配置,帮助开发者掌握卡片生命周期管理与数据刷新机制。

通过构建一个音乐播放器卡片,演示了鸿蒙应用卡片的三种核心事件交互:message(刷新状态)、router(跳转页面)和 call(后台任务)。文章详细讲解了项目结构、代码实现(包括卡片 UI、FormExtensionAbility、UIAbility 及后台服务)以及配置文件配置,帮助开发者掌握卡片生命周期管理与数据刷新机制。

为了让您能快速上手,我们将创建一个包含以下关键文件的项目:
MusicWidget.ets: 卡片 UI 页面,包含所有交互按钮。EntryFormAbility.ets: 卡片的生命周期管理,处理 message 事件和卡片刷新。EntryAbility.ets: 应用的主 UIAbility,处理 router 事件的跳转。MusicBackgroundAbility.ets: 后台 UIAbility,处理 call 事件的播放 / 暂停逻辑。module.json5: 应用配置文件,注册所有 Ability 和权限。form_config.json: 卡片配置文件。DevEco Studio 会自动生成 EntryFormAbility.ets、MusicWidget.ets 和 form_config.json。我们接下来要做的就是修改这些文件,并添加新的文件。
这是用户直接看到的界面,包含了触发所有事件的按钮。
import { postCardAction } from '@kit.FormKit';
let storage = new LocalStorage();
@Entry(storage)
@Component
struct MusicWidget {
// 从 EntryFormAbility 接收的数据
@LocalStorageProp('formId') formId: string = '';
@LocalStorageProp('songName') songName: string = '未播放';
@LocalStorageProp('isLiked') isLiked: boolean = false;
@LocalStorageProp('playStatus') playStatus: string = '暂停';
build() {
Column({ space: 15 })
.width('100%')
.height('100%')
.padding(15)
.justifyContent(FlexAlign.Center) {
Text('音乐卡片')
.fontSize(20)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 10 });
Text(`🎵 ${this.songName}`)
.fontSize(16)
.fontWeight(FontWeight.Normal);
Text(`▶️ 状态:${this.playStatus}`)
.fontSize(14)
.fontColor(Color.Grey)
.margin({ bottom: 20 });
// --- 事件按钮区域 ---
// 1. message 事件:喜欢/取消喜欢
Button(this.isLiked ? '❤️ 已喜欢' : '🤍 喜欢')
.width('80%')
.height(40)
.backgroundColor(this.isLiked ? Color.Pink : Color.White)
.fontColor(this.isLiked ? Color.White : Color.Black)
.border({ width: 1, color: Color.Pink })
.onClick(() => {
this.triggerMessageEvent();
});
// 2. router 事件:打开应用播放列表
Button('📱 打开播放列表')
.width('80%')
.height(40)
.backgroundColor(Color.Blue)
.fontColor(Color.White)
.onClick(() => {
this.triggerRouterEvent();
});
// 3. call 事件:播放/暂停
Button(`${this.playStatus === '播放' ? '⏸️ 暂停' : '▶️ 播放'}`)
.width('80%')
.height(40)
.backgroundColor(Color.Green)
.fontColor(Color.White)
.onClick(() => {
this.triggerCallEvent(this.playStatus === '播放' ? 'pause' : 'play');
});
}
}
// 触发 message 事件
private triggerMessageEvent(): void {
postCardAction(this, {
action: 'message',
params: {
formId: this.formId,
command: 'toggleLike',
isLiked: !this.isLiked
}
});
}
// 触发 router 事件
private triggerRouterEvent(): void {
postCardAction(this, {
action: 'router',
abilityName: 'EntryAbility',
params: {
targetPage: 'PlayListPage',
currentSong: this.songName
}
});
}
// 触发 call 事件
private triggerCallEvent(method: string): void {
postCardAction(this, {
action: 'call',
abilityName: 'MusicBackgroundAbility',
params: {
formId: this.formId,
method: method,
songId: 'song_001'
}
});
}
}
这个文件是卡片的'大脑',负责处理 message 事件并执行刷新。
import {
formBindingData,
FormExtensionAbility,
formInfo,
formProvider,
} from '@kit.FormKit';
import { Want, BusinessError } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
const TAG: string = 'EntryFormAbility';
const DOMAIN_NUMBER: number = 0xFF00;
export default class EntryFormAbility extends FormExtensionAbility {
// 卡片创建时触发,提供初始数据
onAddForm(want: Want): formBindingData.FormBindingData {
hilog.info(DOMAIN_NUMBER, TAG, 'onAddForm');
const formId = want.parameters?.[formInfo.FormParam.IDENTITY_KEY] as string;
const initData = {
formId: formId,
songName: '七里香 - 周杰伦',
isLiked: false,
playStatus: '暂停'
};
return formBindingData.createFormBindingData(initData);
}
(: , : ): <> {
hilog.(, , );
params = .(message);
(params. === ) {
newData = { : params. };
{
formProvider.(formId, formBindingData.(newData));
hilog.(, , );
} (error) {
err = error ;
hilog.(, , );
}
}
}
(: ): {
hilog.(, , );
}
}
处理 router 事件,负责跳转到应用内的页面。
import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
import { window } from '@kit.ArkUI';
import { hilog } from '@kit.PerformanceAnalysisKit';
const TAG: string = 'EntryAbility';
const DOMAIN_NUMBER: number = 0xFF00;
export default class EntryAbility extends UIAbility {
private currentWindowStage: window.WindowStage | null = null;
// 首次启动或在后台被 router 事件唤醒时触发
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
hilog.info(DOMAIN_NUMBER, TAG, `onCreate: ${JSON.stringify(want.parameters)}`);
// 处理 router 事件传递的参数
if (want.parameters.params) {
const params = JSON.parse(want.. );
(params. === ) {
hilog.(, , );
}
}
}
(: ): {
hilog.(, , );
}
(: .): {
. = windowStage;
.();
}
(: ): {
.?.(pageName, {
(err.) {
hilog.(, , );
}
});
}
}
注意: 这个文件需要您手动创建。在 ets 目录下新建一个 ability 文件夹,然后创建此文件。
它在后台运行,处理 call 事件。
import { UIAbility, Want, AbilityConstant } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { formBindingData, formProvider } from '@kit.FormKit';
import { rpc } from '@kit.IPCKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
const TAG: string = 'MusicBackgroundAbility';
const DOMAIN_NUMBER: number = 0xFF00;
// 用于 RPC 通信的数据序列化
class CallResult implements rpc.Parcelable {
result: string;
constructor(result: string) {
this.result = result;
}
marshalling(messageSequence: rpc.MessageSequence): boolean {
messageSequence.writeString(this.result);
return true;
}
unmarshalling(messageSequence: rpc.MessageSequence): boolean {
. = messageSequence.();
;
}
}
{
(: , : .): {
hilog.(, , );
{
..(, ..());
..(, ..());
} (error) {
err = error ;
hilog.(, , );
}
}
(: rpc.): {
params = .(data.());
formId = params.;
hilog.(, , );
.(formId, );
();
}
(: rpc.): {
params = .(data.());
formId = params.;
hilog.(, , );
.(formId, );
();
}
(: , : ): <> {
{
formProvider.(formId, formBindingData.({ : status }));
hilog.(, , );
} (error) {
err = error ;
hilog.(, , );
}
}
(): {
hilog.(, , );
..();
..();
}
}
这是最重要的配置文件,必须正确注册所有组件和权限。
{
"module": {
"name": "entry",
"type": "entry",
"description": "$string:module_desc",
"mainElement": "EntryAbility",
"deviceTypes": ["phone", "tablet"],
"deliveryWithInstall": true,
"installationFree": false,
"pages": "$profile:main_pages",
"abilities": [
{
"name": "EntryAbility",
"srcEntry": "./ets/entryability/EntryAbility.ets",
"description": "$string:EntryAbility_desc",
"icon": "$media:icon",
"label": "$string:EntryAbility_label",
"startWindowIcon": "$media:icon",
"startWindowBackground": "$color:start_window_background"
},
{
"name": "MusicBackgroundAbility",
"srcEntry": "./ets/ability/MusicBackgroundAbility.ets",
"description": "处理卡片 call 事件的后台服务"
}
],
"extensionAbilities": [
{
"name": "EntryFormAbility",
"srcEntry": "./ets/entryformability/EntryFormAbility.ets",
"label": "$string:EntryFormAbility_label",
"description": "$string:EntryFormAbility_desc",
"type": "form",
"metadata": [
{
"name": "ohos.extension.form",
"resource": "$profile:form_config"
}
]
}
],
"requestPermissions": [
{
"name": "ohos.permission.INTERNET"
},
{
"name": "ohos.permission.KEEP_BACKGROUND_RUNNING",
"reason": "$string:keep_background_running_reason",
"usedScene": {
"abilities": ["MusicBackgroundAbility"],
"when": "always"
}
}
]
}
}
注意: 您需要在 src/main/resources/base/element/string.json 中添加 keep_background_running_reason 的定义。
{
"string": [
{
"name": "keep_background_running_reason",
"value": "用于在后台处理卡片的播放/暂停指令"
}
]
}
确保卡片是动态的。
{
"forms": [
{
"name": "MusicWidget",
"description": "$string:MusicWidget_desc",
"src": "./ets/widget/pages/MusicWidget.ets",
"uiSyntax": "arkts",
"window": {
"designWidth": 720,
"autoDesignWidth": true
},
"colorMode": "auto",
"isDefault": true,
"updateEnabled": true,
"scheduledUpdateTime": "10:30",
"updateDuration": 0,
"defaultDimension"
本文通过构建一个音乐播放器卡片,演示了鸿蒙应用卡片的三种核心事件交互:message(刷新状态)、router(跳转页面)和 call(后台任务)。文章详细讲解了项目结构、代码实现以及配置文件配置,帮助开发者掌握卡片生命周期管理与数据刷新机制。

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online
通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online
将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online