鸿蒙操作系统开发 Form Kit 桌面卡片实战:从卡片创建到定时刷新
一、背景说明
鸿蒙生态正在加速扩张——微信鸿蒙版安装量近日突破 9000 万次,HarmonyOS 终端用户数预计本月突破 1 亿,桌面卡片作为鸿蒙生态最具辨识度的入口之一,是每个原生应用都值得掌握的能力。本文不谈生态数据,聚焦一个具体技术点:使用 Form Kit(@kit.FormKit)开发一个 ArkTS 桌面卡片,实现卡片页面布局、数据绑定、定时刷新和事件交互的完整闭环。
二、技术原理
鸿蒙桌面卡片本质是一个由应用提供方渲染、桌面(卡片使用方)展示的轻量 UI 单元,核心机制如下:
- FormExtensionAbility:卡片提供方的生命周期载体,在独立的 Extension 进程中运行。卡片被添加到桌面时回调
onAddForm,移除时回调onRemoveForm,定时/主动刷新时回调onUpdateForm。 - FormBindingData:卡片数据绑定对象,通过
createFormBindingData(obj)携带键值对数据,卡片页面用@LocalStorageProp('key')声明式接收。 - formProvider:提供方主动操作卡片的 API,
updateForm()更新卡片内容,setFormNextRefreshTime()设置下次定时刷新(最小间隔 5 分钟)。 - 卡片页面:ArkTS 卡片使用受限的声明式语法(
Chart、Image、Text等少量组件),与主应用 UI 隔离运行。
数据流:桌面添加卡片 → onAddForm 返回 FormBindingData → 卡片页面 @LocalStorageProp 绑定渲染 → 定时/事件触发 onUpdateForm → formProvider.updateForm 推送新数据。
三、实现步骤
工程为 Stage 模型(API 12+),DevEco Studio 中右键 src/main/ets → New → Service Widget 即可生成骨架,本文给出手工实现的完整文件。
1. 卡片配置文件 WidgetCard.ets
卡片页面文件位于 entry/src/main/ets/widget/pages/:
let storage = new LocalStorage();
@Entry(storage)
@Component
struct WidgetWeatherCard {
@LocalStorageProp('city') city: string = '北京';
@LocalStorageProp('temperature') temperature: string = '--';
@LocalStorageProp('updateTime') updateTime: string = '';
build() {
Column() {
Row() {
Text(this.city)
.fontSize(16)
.fontColor('#FFFFFF')
.fontWeight(FontWeight.Bold)
Blank()
Text(this.updateTime)
.fontSize(10)
.fontColor('#B3FFFFFF')
}
.width('100%')
Text(`${this.temperature}°C`)
.fontSize(40)
.fontColor('#FFFFFF')
.fontWeight(FontWeight.Bold)
.margin({ top: 8 })
Text('点击卡片查看详情')
.fontSize(11)
.fontColor('#B3FFFFFF')
.margin({ top: 12 })
}
.width('100%')
.height('100%')
.padding(16)
.linearGradient({
angle: 135,
colors: [['#3A7BD5', 0.0], ['#00D2FF', 1.0]]
})
.onClick(() => {
postCardAction(this, {
'action': 'router',
'abilityName': 'EntryAbility',
'params': { 'from': 'widget' }
})
})
}
}
2. 卡片提供方 EntryFormAbility.ets
import { formBindingData, FormExtensionAbility, formInfo, formProvider } from '@kit.FormKit';
import { Want } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
const TAG: string = 'EntryFormAbility';
const DOMAIN: number = 0xFF00;
export default class EntryFormAbility extends FormExtensionAbility {
// 添加卡片时回调,返回初始数据
onAddForm(want: Want): formBindingData.FormBindingData {
const formId: string = want.parameters?.[formInfo.FormParam.IDENTITY_KEY] as string;
hilog.info(DOMAIN, TAG, `onAddForm, formId=${formId}`);
const obj: Record<string, string> = {
'city': '北京',
'temperature': '23',
'updateTime': this.getCurrentTime()
};
// 定时刷新:下一次刷新时间为 30 分钟后
formProvider.setFormNextRefreshTime(formId, 30, (err: BusinessError) => {
if (err) {
hilog.error(DOMAIN, TAG, `setFormNextRefreshTime failed: ${err.code}`);
}
});
return formBindingData.createFormBindingData(obj);
}
// 定时刷新/使用方主动刷新时回调
onUpdateForm(formId: string): void {
hilog.info(DOMAIN, TAG, `onUpdateForm, formId=${formId}`);
// 真实场景这里请求天气接口,此处演示用模拟数据
const obj: Record<string, string> = {
'city': '北京',
'temperature': `${18 + Math.floor(Math.random() * 10)}`,
'updateTime': this.getCurrentTime()
};
const data: formBindingData.FormBindingData = formBindingData.createFormBindingData(obj);
formProvider.updateForm(formId, data).catch((err: BusinessError) => {
hilog.error(DOMAIN, TAG, `updateForm failed, code=${err.code}, msg=${err.message}`);
});
}
// 卡片 message 事件回调(卡片内 postCardAction action=message 时触发)
onFormEvent(formId: string, message: string): void {
hilog.info(DOMAIN, TAG, `onFormEvent, msg=${message}`);
}
onRemoveForm(formId: string): void {
// 卡片被移除,可在此清理该 formId 相关的持久化数据
hilog.info(DOMAIN, TAG, `onRemoveForm, formId=${formId}`);
}
private getCurrentTime(): string {
const d = new Date();
return `${String(d.getHours()).padStart(2, '0')}:${String(d.getMinutes()).padStart(2, '0')}`;
}
}
3. 卡片配置 form_config.json
卡片在 resources/base/profile/form_config.json 中注册,并在 module.json5 的 extensionAbilities 里声明:
{
"forms": [
{
"name": "WeatherWidget",
"displayName": "$string:widget_name",
"description": "$string:widget_desc",
"src": "./ets/widget/pages/WidgetCard.ets",
"window": {
"designWidth": 720,
"autoDesignWidth": true
},
"colorMode": "auto",
"isDefault": true,
"updateEnabled": true,
"scheduledUpdateTime": "07:30",
"updateDuration": 1,
"supportShapes": ["rect"]
}
]
}
{
"extensionAbilities": [
{
"name": "EntryFormAbility",
"srcEntry": "./ets/entryformability/EntryFormAbility.ets",
"icon": "$media:icon",
"label": "$string:widget_name",
"description": "$string:widget_desc",
"type": "form",
"metadata": [
{
"name": "ohos.extension.form",
"resource": "$profile:form_config"
}
]
}
]
}
4. 主应用处理卡片跳转
卡片 router 事件拉起主应用时,参数通过 want 传入,在 UIAbility 中接收:
// EntryAbility.ets 的 onCreate 中
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
if (want.parameters?.['from'] === 'widget') {
console.info('从天气卡片拉起,跳转天气详情页');
// 可用 windowStage.loadContent 加载对应页面
}
}
四、应用场景示例
运行应用后长按桌面空白处 → 服务卡片 → 选择本应用卡片添加到桌面,即可看到实时气温卡片;也可从主应用主动推送一次刷新:
// 主应用内(如数据更新后)主动刷新指定卡片
import { formBindingData, formProvider } from '@kit.FormKit';
import { BusinessError } from '@kit.BasicServicesKit';
async function pushWidgetUpdate(formId: string, temp: string): Promise<void> {
const obj: Record<string, string> = { 'temperature': temp };
try {
await formProvider.updateForm(formId, formBindingData.createFormBindingData(obj));
} catch (err) {
const e = err as BusinessError;
console.error(`update failed, code=${e.code}`);
}
}
五、技术要点
1. 刷新策略与限制
setFormNextRefreshTime最小间隔 5 分钟,updateDuration取值 1/2 表示每 30/60 分钟刷新;scheduledUpdateTime支持定点刷新(如每天 07:30);- 定时刷新不精确,系统会合并调度,需要秒级刷新的场景应改用主进程数据推送 +
updateForm。
2. Extension 进程 10 秒生命周期
FormExtensionAbility 进程非常驻:生命周期回调结束后进程保留约 10 秒即退出。超过 10 秒的工作(如网络请求重试)应拉起主应用处理,完成后由主进程调用 updateForm 推送结果,不要在回调里死等。
3. 错误处理
formProvider.updateForm(formId, data).catch((err: BusinessError) => {
if (err.code === 16501001) {
console.error('卡片已不存在,停止刷新并清理 formId');
} else if (err.code === 16501003) {
console.error('无权操作该卡片');
} else {
console.error(`update error code=${err.code}`);
}
});
4. 数据限制
- 单次
FormBindingData序列化后过大会导致更新失败,图片建议走formImages携带 fd,控制在 2MB/张、共 5 张以内(API 20 起放宽至 20 张/10MB); - 卡片页面仅支持有限的组件与样式,复杂交互应路由到主应用完成。
六、扩展建议
- requestForm 主动刷新:卡片页面调用
requestForm让提供方立即走一遍onUpdateForm; - 动态卡片(Live Form):API 20+ 支持 LiveFormExtensionAbility,可开发实况窗级常驻卡片;
- 卡片内直接请求数据:卡片页面可配置网络权限后用
@ohos.net.http自取数据,减少提供方推送频次:
// 卡片页面内轻量请求(卡片可用组件/能力有限,适合简单场景)
import { http } from '@kit.NetworkKit';
结语
本文用 Form Kit 完成了鸿蒙桌面卡片从页面布局、生命周期实现、form_config 注册到定时刷新与事件跳转的完整实现,核心代码约 200 行,均可直接编译运行。掌握卡片开发后,天气、日程、播放器等高频信息都可以从"打开应用"变为"一眼可见",显著提升应用在鸿蒙桌面的存在感。
更多推荐

所有评论(0)