HarmonyOS 互动卡片(Live Form)入门指南:双态架构与场景动效全景解析
HarmonyOS 互动卡片(Live Form)入门指南:双态架构与场景动效全景解析
前言
服务卡片(Service Card)是 HarmonyOS 的标志性特性之一,能够将应用的核心信息以卡片形式展示在桌面,让用户无需打开应用即可获取关键信息。然而,传统的动态卡片本质上是一张"图"——UI 被钉在卡片的矩形框里,动画能力有限,也无法接入传感器。
HarmonyOS 推出的互动卡片(Live Form,场景动效类型互动卡片)彻底打破了这一限制。它支持帧动画、3D 变换、陀螺仪交互,甚至能将渲染区域扩展到卡片边界之外,营造"破框"效果。用户点击卡片或摇一摇手机,憨憨(官方吉祥物角色)就会在卡片上"活"起来——起床、跑步、取快递、跳舞。
本文作为互动卡片系列的第一篇,将从概念原理、双态架构、四种卡片类型、API 全景到前置条件,带你系统地理解这项能力。
一、互动卡片概念解析
1.1 什么是互动卡片
互动卡片(场景动效类型互动卡片)是一种支持动态动画和实时交互的卡片形态,区别于传统动态卡片,它具有以下核心能力:
| 能力 | 传统动态卡片 | 互动卡片 |
|---|---|---|
| 帧动画 | 不支持 | 支持流畅帧动画 |
| 3D 变换 | 不支持 | 支持缩放、旋转、透视 |
| 传感器交互 | 不支持 | 支持陀螺仪等传感器 |
| 破框渲染 | 不支持 | 渲染区域可超出卡片边界 |
| 摇一摇触发 | 不支持 | 支持(需要 HarmonyOS 7.0+) |
1.2 双态架构设计
互动卡片采用双态管理架构,同一张卡片在不同状态下由不同的 Ability 管理:

图:互动卡片双态架构——非激活态(左)与激活态(右)由不同的 Ability 管理
| 状态 | 管理 Ability | 渲染方式 | 交互能力 |
|---|---|---|---|
| 非激活态 | FormExtensionAbility |
ArkTS 声明式 UI | 有限点击、数据刷新 |
| 激活态 | LiveFormExtensionAbility |
动态 UI 页面 | 帧动画、陀螺仪、破框渲染 |
核心设计:同一张卡片,平时是
FormExtensionAbility管的普通卡片,触发后换成LiveFormExtensionAbility管的动态 UI。两者靠form_config.json里的sceneAnimationParams串起来。
1.3 互动卡片类型
互动卡片包含两种类型,本文仅介绍场景动效类型:
| 类型 | 说明 | 触发方式 |
|---|---|---|
| 趣味交互类型 | 通过 funInteractionParams 配置 |
点击 |
| 场景动效类型 | 通过 sceneAnimationParams 配置 |
点击、摇一摇 |
二、四种官方示例卡片全景
2.1 卡片总览
官方提供了四种互动卡片示例,分别对应不同的交互场景和体验效果:
| 卡片名称 | 用途 | 触发方式 | 核心技术 |
|---|---|---|---|
| 睡眠卡片 | 状态提醒、起床动效 | 点击 | 帧动画、状态切换、破框 |
| 快递卡片 | 快递物流状态 | 点击 + 摇一摇 | 帧动画、陀螺仪交互 |
| 运动卡片 | 运动记录、卡路里计数 | 点击 | 帧动画、数字累加 |
| 音乐卡片 | 音乐播放控制 | 点击 | Canvas 自绘制、音频控制 |
2.2 睡眠卡片
触发方式:点击。
体验及交互:点击卡片触发憨憨起床动画,三叶草旋转,气球飘出卡片边界,憨憨从睡姿变为醒姿,动画结束后卡片状态更新为"按时起床"。
2.3 快递卡片
触发方式:点击、摇一摇。
体验及交互:点击卡片或摇动设备激活卡片后,倾斜手机驱动憨憨沿路线跑动——向右倾斜憨憨向右移动并缩小(近→远透视),向左倾斜憨憨向左移动并放大(远→近透视)。
2.4 运动卡片
触发方式:点击。
体验及交互:点击"开始运动"触发拉伸运动动画;点击"结束运动"触发庆祝动画并显示卡路里计数,从 0 逐步增长到 300kcal。
2.5 音乐卡片
触发方式:点击。
体验及交互:播放音乐时憨憨跳舞、专辑封面旋转;切歌时憨憨出框取专辑进行替换。
三、核心 API 全景
3.1 关键接口一览
| 接口名 | 所属模块 | 描述 |
|---|---|---|
onLiveFormCreate(liveFormInfo, session) |
LiveFormExtensionAbility |
互动卡片界面对象创建的回调 |
onLiveFormDestroy(liveFormInfo) |
LiveFormExtensionAbility |
互动卡片界面对象销毁的回调 |
formProvider.requestOverflow(formId, overflowInfo) |
@kit.FormKit |
卡片提供方发起互动卡片动效请求 |
formProvider.cancelOverflow(formId) |
@kit.FormKit |
卡片提供方取消互动卡片动效 |
formProvider.getFormRect(formId) |
@kit.FormKit |
查询卡片位置、尺寸 |
formProvider.updateForm(formId, formBindingData) |
@kit.FormKit |
更新卡片数据 |
postCardAction(ROUTER) |
@kit.FormKit |
卡片跳转到应用页面 |
postCardAction(MESSAGE) |
@kit.FormKit |
卡片发送消息到 FormExtensionAbility |
postCardAction(CALL) |
@kit.FormKit |
卡片调用应用方法 |
startAbilityByLiveForm(want) |
LiveFormExtensionContext |
互动卡片拉起应用页面 |
3.2 LiveFormInfo 关键字段
| 字段 | 类型 | 说明 |
|---|---|---|
formId |
string |
卡片唯一标识 |
borderRadius |
number |
卡片圆角信息 |
rect |
formInfo.Rect |
非激活态卡片相对激活态 UI 的位置和尺寸 |
3.3 OverflowInfo 关键字段
| 字段 | 类型 | 说明 |
|---|---|---|
area |
{ left, top, width, height } |
破框区域 |
duration |
number |
动画时长(毫秒) |
四、前置条件与环境准备
4.1 版本要求
| 组件 | 最低版本 | 说明 |
|---|---|---|
| 开发工具 | DevEco Studio 6.1.0 Release | 互动卡片开发环境 |
| HarmonyOS SDK | 6.1.0+ | 基础能力 |
| 摇一摇触发 | HarmonyOS 7.0+ | 摇一摇依赖系统级 shake 事件 |
| 语言 | ArkTS | 统一使用 V2 装饰器 |
4.2 前置知识
在阅读本文之前,建议先了解以下基础知识:
4.3 设备要求
- 支持实况窗的真机或模拟器
- 部分模拟器可能不支持摇一摇(shake),建议真机验证陀螺仪效果
- 摇一摇功能需 HarmonyOS 7.0 及以上版本
五、整体架构与数据流
5.1 整体架构
互动卡片以动态卡片作为入口,通过 form_config.json 中的 sceneAnimationParams 配置关联 LiveFormExtensionAbility。用户触发激活后,系统根据 sceneAnimationParams.abilityName 激活对应的 LiveFormExtensionAbility 实例:
### 6.7 摇一摇触发处理(onUpdateForm)
当用户在 `triggerTypes` 中配置了 `"shake"` 时,摇一摇事件会触发 `FormExtensionAbility.onUpdateForm`:
```typescript
// 在 FormExtensionAbility 中处理摇一摇事件
async onUpdateForm(formId: string): Promise<void> {
console.info(`摇一摇触发,激活卡片: ${formId}`);
// 摇一摇触发时,主动调用 requestOverflow 激活互动卡片
await this.requestOverflow(formId, 1.0, 1.0, 5000);
}
6.8 卡片尺寸适配(获取 formRect)
import { formProvider, formInfo } from '@kit.FormKit';
/**
* 获取卡片当前尺寸信息
* 用于计算破框区域和适配不同尺寸的卡片
*/
async function getCardDimensions(formId: string): Promise<formInfo.Rect | null> {
try {
const formRect = await formProvider.getFormRect(formId);
console.info(`卡片尺寸: ${formRect.width}x${formRect.height}`);
return formRect;
} catch (err) {
console.error(`获取卡片尺寸失败: ${JSON.stringify(err)}`);
return null;
}
}
用户触发(点击/摇一摇)
↓
系统识别触发事件
↓
查找 sceneAnimationParams.triggerTypes 配置的卡片
↓
读取 sceneAnimationParams.abilityName
↓
调用 FormExtensionAbility.onFormEvent(MESSAGE 路径)
↓
formProvider.requestOverflow 激活互动卡片
↓
创建 LiveFormExtensionAbility 实例
↓
onLiveFormCreate → session.loadContent 加载动画 UI
### 5.2 两种触发路径
| 触发方式 | 发起方 | 流程 | 最低版本 |
|---------|--------|------|---------|
| 点击触发 | 卡片自身 | 卡片 → `postCardAction(MESSAGE)` → `FormExtensionAbility.onFormEvent` → `requestOverflow` | API 12+ |
| 摇一摇触发 | 系统 | 系统检测 shake → 查找 triggerTypes 含 "shake" 的卡片 → `FormExtensionAbility.onUpdateForm` → `requestOverflow` | HarmonyOS 7.0+ |
## 六、基础代码入门
### 6.1 动态卡片创建(入口)
动态卡片是互动卡片的入口,需按标准流程创建:
```typescript
// entry/src/main/ets/widget/pages/DeliveryCard.ets
import { ActionUtils } from '../../utils/ActionUtils';
import { LiveCardScale } from '../../constants/LiveCardConstants';
const LIVE_CARD_DURATION: number = 5000;
@Entry
@Component
struct DeliveryCard {
build() {
RelativeContainer() {
// 背景图片
Image($rawfile('delivery/background.png'))
.objectFit(ImageFit.Contain)
.width('100%')
.height('100%')
.aspectRatio(1);
// 憨憨角色图片
Image($rawfile('delivery/fuzzball.png'))
.objectFit(ImageFit.Contain)
.width('145%')
.height('145%')
.offset({ x: `-22.5%`, y: `-22.5%` });
// 文字说明
Image($rawfile('delivery/delivery_text.png'))
.objectFit(ImageFit.Contain)
.width('100%')
.height('100%')
.aspectRatio(1);
// 底部跳转区域
Stack()
.width('100%')
.height('30%')
.onClick(() => {
ActionUtils.jumpAppPage(this, 'DeliveryPage');
})
.alignRules({
left: { anchor: '__container__', align: HorizontalAlign.Start },
bottom: { anchor: '__container__', align: VerticalAlign.Bottom }
});
}
.width('100%')
.height('100%')
.onClick(() => {
// 点击触发互动卡片激活
ActionUtils.requestOverFlow(
this, LiveCardScale.DELIVERY_WIDTH,
LiveCardScale.DELIVERY_HEIGHT, LIVE_CARD_DURATION
);
});
}
}
6.2 form_config.json 配置
{
"forms": [
{
"name": "DeliveryCard",
"displayName": "$string:DeliveryCard",
"description": "$string:DeliveryCardDes",
"src": "./ets/widget/pages/DeliveryCard.ets",
"uiSyntax": "arkts",
"isDynamic": true,
"defaultDimension": "2*2",
"supportDimensions": ["2*2"],
"sceneAnimationParams": {
"abilityName": "DeliveryLiveCardAbility",
"triggerTypes": ["shake"]
}
}
]
}
6.3 module.json5 声明
{
"module": {
"extensionAbilities": [
{
"name": "EntryFormAbility",
"srcEntry": "./ets/entryformability/EntryFormAbility.ets",
"type": "form",
"metadata": [
{
"name": "ohos.extension.form",
"resource": "$profile:form_config"
}
]
},
{
"name": "DeliveryLiveCardAbility",
"srcEntry": "./ets/livecardability/DeliveryLiveCardAbility.ets",
"type": "liveForm"
}
]
}
}
6.4 LiveFormExtensionAbility 实现
// entry/src/main/ets/livecardability/DeliveryLiveCardAbility.ets
import { LiveFormExtensionAbility, LiveFormInfo, formInfo } from '@kit.FormKit';
import { UIExtensionContentSession } from '@kit.AbilityKit';
export default class DeliveryLiveCardAbility extends LiveFormExtensionAbility {
onLiveFormCreate(liveFormInfo: LiveFormInfo, session: UIExtensionContentSession): void {
const storage: LocalStorage = new LocalStorage();
// 传递上下文
storage.setOrCreate('context', this.context);
storage.setOrCreate('session', session);
// 传递卡片信息
const formId: string = liveFormInfo.formId;
storage.setOrCreate('formId', formId);
// 传递圆角信息
const borderRadius: number = liveFormInfo.borderRadius;
storage.setOrCreate('borderRadius', borderRadius);
// 传递卡片位置和尺寸
const formRect: formInfo.Rect = liveFormInfo.rect;
storage.setOrCreate('formRect', formRect);
try {
// 加载互动卡片 UI 页面
session.loadContent('livecardability/pages/DeliveryLiveCard', storage);
} catch (error) {
console.error(`session.loadContent error: ${JSON.stringify(error)}`);
}
}
onLiveFormDestroy(liveFormInfo: LiveFormInfo): void {
console.info(`LiveForm destroyed: ${liveFormInfo.formId}`);
}
}
6.5 激活动效请求(requestOverflow)
// 在 FormExtensionAbility 中处理激活动效请求
async onFormEvent(formId: string, message: string): Promise<void> {
const params: Record<string, Object> = JSON.parse(message);
const shortMessage: string = params.message as string;
if (shortMessage === 'requestOverflow') {
const widthRatio: number = params.widthRatio as number;
const heightRatio: number = params.heightRatio as number;
const duration: number = params.duration as number;
await this.requestOverflow(formId, widthRatio, heightRatio, duration);
}
}
private async requestOverflow(
formId: string, widthRatio: number, heightRatio: number, duration: number
): Promise<void> {
try {
const formRect: formInfo.Rect = await formProvider.getFormRect(formId);
if (formRect.width <= 0 || formRect.height <= 0) return;
const cardWidth = formRect.width * widthRatio;
const cardHeight = formRect.height * heightRatio;
const leftOffset = (formRect.width - cardWidth) / 2;
const topOffset = (formRect.height - cardHeight) / 2;
await formProvider.requestOverflow(formId, {
area: {
left: leftOffset,
top: topOffset,
width: cardWidth,
height: cardHeight
},
duration: duration
});
} catch (err) {
console.error(`requestOverflow error: ${JSON.stringify(err)}`);
}
}
6.6 取消动效(cancelOverflow)
// 在互动卡片 UI 中点击取消动效
.onClick(() => {
formProvider.cancelOverflow(this.formId).catch((err: BusinessError) => {
console.error(`cancelOverflow error: ${err.code}, ${err.message}`);
});
})
七、约束与限制
| 限制项 | 说明 |
|---|---|
| 开发工具 | DevEco Studio 6.1.0 Release 及以上 |
| 摇一摇触发 | HarmonyOS 7.0 及以上版本 |
| 设备类型 | 支持实况窗的真机或模拟器 |
| 状态管理 | 建议统一使用 V2 装饰器(@ComponentV2、@Local、@LocalStorageProp) |
| 五元组 | 卡片五元组(bundleName、moduleName、abilityName、formName、formDimension)变更会导致卡片被删除 |
八、总结
本文作为互动卡片系列入门篇,系统介绍了以下核心内容:
- 概念原理:互动卡片是支持帧动画、3D 变换、陀螺仪交互和破框渲染的高级卡片形态
- 双态架构:非激活态由
FormExtensionAbility管理,激活态由LiveFormExtensionAbility管理 - 四种卡片:睡眠卡片(起床动画)、快递卡片(陀螺仪交互)、运动卡片(卡路里累加)、音乐卡片(Canvas 自绘制)
- API 全景:10 个核心接口,覆盖卡片创建、激活、取消、数据推送
- 基础代码:动态卡片 UI、配置文件、LiveFormExtensionAbility、requestOverflow
下一篇将深入讲解互动卡片的配置与触发机制,包括 form_config.json 详解、module.json5 配置、点击触发与摇一摇触发的完整流程。
如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!
相关资源:
更多推荐


所有评论(0)