静态卡片(ArkTS + FormLink)
1. 概述
在 HarmonyOS 中,服务卡片分为 动态卡片 和 静态卡片:
- 动态卡片:支持在运行时通过
postCardAction()交互。 - 静态卡片:不能 调用
postCardAction(),而是通过FormLink组件 与应用交互。
ArkTS 卡片提供 FormLink 静态卡片交互组件,用于静态卡片内部与卡片提供方应用 之间的交互。
FormLink 支持三种事件类型:
router:页面跳转(拉起 UIAbility)message:向 FormExtensionAbility 发送消息call:后台拉起 UIAbility 执行指定方法
静态卡片的典型结构由三部分组成:
- EntryFormAbility.ets:卡片生命周期管理(创建、更新、事件回调)。
- WidgetCard.ets:卡片 UI 页面(ArkTS 编写)。
- form_config.json:卡片配置信息(尺寸、布局、是否静态卡片等)。
2. 创建 ArkTS 静态卡片工程
在 DevEco Studio 中:
- 右键工程 → New → ArkTS Card(或 Module → Service Widget)
- 选择使用 ArkTS 语法,填写卡片名称等基本信息。
- 创建完成后,工程会自动生成以下文件:
-
EntryFormAbility.ets:- 继承
FormExtensionAbility - 负责卡片的创建
onAddForm、删除onRemoveForm、更新onUpdateForm、处理事件onFormEvent等。
- 继承
-
WidgetCard.ets:- ArkTS UI 页面文件
- 使用
@Entry @Component定义卡片 UI。
-
form_config.json:- 描述卡片的尺寸、布局文件路径、是否为静态卡片等。
- 比如卡片大小、卡片 ID、默认卡片等配置都在这里。
创建向导里的默认 UI 只是示例,实际开发中我们通常会删掉默认 UI,自定义 UI 和交互逻辑。
3. 配置 ArkTS 卡片的配置文件(form_config.json 概念)
这里只给重要字段的含义思路(不用死记 JSON):
name:卡片名称,对外展示。description:卡片描述。type:一般为"JS"(ArkTS 卡片也属于此类)。uiSyntax:ArkTS 卡片一般为"arkts"。isStatic:是否静态卡片。静态卡片必须为true。src:指向WidgetCard.ets对应的 UI 路径。supportDimensions:支持的卡片规格(例如["2*2"])。defaultDimension:默认尺寸。
配置完成后记得点击 Finish / OK,触发构建,让卡片配置生效。
4. WidgetCard:自定义静态卡片 UI
新建 ArkTS 卡片后,WidgetCard.ets 会自带一段默认 UI。
实际开发中我们常常会:
- 删除默认 UI 代码;
- 自己用 Column/Row/Stack/Text/Image 等组件重写。
例如,我们清空默认内容后,可以这样写一个最简单的静态卡片:
ts
@Entry
@Component
struct WidgetCard {
build() {
Column() {
Text('Hello Static Widget')
.fontSize(20)
.fontWeight(FontWeight.Medium)
.fontColor('#333333')
.margin(12)
}
.width('100%')
.height('100%')
}
}
5. 卡片背景设置(推荐做法)
⚠ 在 卡片(WidgetCard) 中,不能直接使用 .backgroundImage() 来设置背景图。
原因:卡片是一个轻量独立模块,不支持部分常规页面装饰能力。
正确方式 :使用 Image + Stack 实现背景效果。
5.1 资源准备
- 将图片放到:
resources/base/media/目录下
例如:resources/base/media/background.png - 在
resources/base/profile/media.json中注册对应的资源 ID(DevEco 一般会自动生成)。 - 命名规范:
- ✅
background.png - ✅
card_bg.jpg - ❌
BackGround.png(大写 / 奇怪字符不推荐)
- ✅
5.2 背景 + 前景内容示例
tsx
@Entry
@Component
struct WidgetCard {
title: string = 'Keep Running'
build() {
Column() {
Stack() {
// 1. 背景图
Image($r('app.media.background')) // 注意要和 media.json 中的 name 一致
.width('100%')
.height('100%')
.objectFit(ImageFit.Cover) // 填满卡片区域
// 2. 前景内容
Column() {
Text(this.title)
.fontColor('#c6ea96d6')
.fontWeight(FontWeight.Bold)
.fontSize(20)
.margin({ left: 12, top: 12 })
}
}
}
.width('100%')
.height('100%')
}
}
Stack 的作用:
- 最底层放背景 Image
- 上面叠加 Column/Row/Text 等前景内容
6. 使用 FormLink 实现静态卡片交互
静态卡片不能用 onClick 直接调逻辑,而是通过 FormLink 包裹可点击区域。
6.1 router 事件:点击卡片打开应用页面
下面是一个「点击卡片跳转到应用详情页」的完整示例:
ts
@Entry
@Component
struct WidgetCard {
title: string = '今日学习打卡'
build() {
// 整个卡片区域都可点击
FormLink({
action: 'router', // 跳转类型
abilityName: 'EntryAbility', // 目标 UIAbility
// 可选:bundleName / moduleName,通常省略,默认当前应用
params: {
targetPage: 'studyDetail', // 约定的目标页面标识
from: 'staticWidget'
}
}) {
// 内部是卡片 UI 内容
Stack() {
Image($r('app.media.background'))
.width('100%')
.height('100%')
.objectFit(ImageFit.Cover)
Column() {
Text(this.title)
.fontColor('#FFFFFF')
.fontWeight(FontWeight.Bold)
.fontSize(20)
.margin({ left: 12, top: 12 })
Text('点击查看详细学习统计')
.fontColor('#FFFFFF')
.opacity(0.8)
.fontSize(14)
.margin({ left: 12, top: 6 })
}
}
}
}
}
UIAbility 中解析参数决定跳转页面(简单示意):
ts
// EntryAbility.ts 片段
import UIAbility from '@ohos.app.ability.UIAbility';
import type Want from '@ohos.app.ability.Want';
import window from '@ohos.window';
let targetPage: string = 'pages/Index';
export default class EntryAbility extends UIAbility {
onCreate(want: Want) {
if (want.parameters && want.parameters.params) {
try {
const params = JSON.parse(want.parameters.params as string);
targetPage = params.targetPage === 'studyDetail'
? 'pages/StudyDetail'
: 'pages/Index';
} catch (e) {
console.error('parse params error', e);
}
}
}
onWindowStageCreate(windowStage: window.WindowStage) {
windowStage.loadContent(targetPage);
}
}
7. FormLink 的 message / call 简要示例
7.1 message:通知 FormExtensionAbility 刷新卡片
ts
FormLink({
action: 'message',
params: {
op: 'refresh',
timestamp: Date.now()
}
}) {
Text('点击刷新卡片内容')
.fontSize(16)
.margin(12)
}
在 EntryFormAbility.ets 中:
ts
onFormEvent(formId: string, message: string) {
// message 是 JSON 字符串
const payload = JSON.parse(message);
if (payload.op === 'refresh') {
// TODO:读取最新数据 → 调用 updateForm 更新卡片
}
}
7.2 call:后台拉起 UIAbility 执行任务
ts
FormLink({
action: 'call',
abilityName: 'EntryAbility',
params: {
method: 'syncFromCloud', // UIAbility 内约定的方法名
extraInfo: 'some data'
}
}) {
Text('后台同步数据')
.fontSize(16)
.margin(12)
}
UIAbility 内在 onCreate/onNewWant 里解析 params.method,根据不同方法名执行不同逻辑(例如:从云端同步数据、更新数据库,然后再通过 formProvider.updateForm 刷新卡片)。
8. 小结(你可以当作复习提纲)
- 静态卡片交互入口是 FormLink ,支持
router / message / call三种事件。 - 静态卡片相关文件:
EntryFormAbility.ets:生命周期 & 事件处理WidgetCard.ets:卡片 UI & FormLinkform_config.json:卡片配置(尺寸 / 是否静态 / UI 路径)
- 卡片背景不要用
.backgroundImage(),用 Stack + Image 实现。 - router:点击卡片跳转应用页面(最常用)。
- message:告诉 FormExtensionAbility 做数据刷新,仅改卡片。
- call:在后台拉起 UIAbility 执行任务,不切到前台。