概览:本文从静态、动态、互动三类卡片的特点与适用场景切入,系统介绍FormExtensionAbility生命周期、UI页面构建、form_config.json与module.json5配置要点,并深入演示卡片与应用间的三大交互方式:router事件(跳转页面)、message事件(传递消息并刷新卡片)和call事件(拉起后台任务)。此外,还涵盖API18+支持的应用内调用openFormManager直接添加卡片到桌面的能力。
服务卡片介绍
Form Kit(卡片开发服务)提供了一种在桌面、锁屏等系统应用上嵌入显示应用信息的开发框架和API,可以将应用内用户关注的重要信息或常用操作抽取到服务卡片(简称“卡片”)上,通过将卡片添加到桌面、锁屏等系统应用上,以达到信息展示、服务直达的便捷体验效果。
卡片常见使用步骤: 1、长按“桌面的应用图标”,弹出操作菜单。 2、点击“卡片”选项,进入卡片管理页面,可以预览卡片。 3、点击“添加到桌面”按钮,即可在桌面上看到新添加的卡片。
服务卡片类型
| 卡片类型 | 支持的能力 | 适用场景 | 优缺点 |
|---|---|---|---|
| 静态卡片 | 仅支持UI组件和布局能力。 | 主要用于展示静态信息(UI相对固定),仅可以通过FormLink组件跳转到指定的UIAbility。 | 功能简单但可以有效控制内存开销。 |
| 动态卡片 | 除了支持UI组件和布局能力,还支持通用事件能力和自定义动效能力。 | 用于有复杂业务逻辑和交互的场景。例如:卡片页面图片的刷新、卡片内容的刷新等。 | 功能丰富但内存开销较大。 |
| 互动卡片 | 在动态卡片基础上,额外支持溢出动效能力。 | 用于有复杂业务逻辑和交互,需要执行溢出动效呈现更好视觉体验的场景。例如:桌面卡片游戏等。 | 功能丰富但内存开销较大。 |
创建动态服务卡片
服务卡片相关文件及作用
1、FormExtensionAbility对象生命周期
import { formBindingData, FormExtensionAbility, formInfo } from '@kit.FormKit';
import { Want } from '@kit.AbilityKit';
export default class EntryFormAbility extends FormExtensionAbility {
// 卡片提供方接收创建卡片的通知接口。当用户把卡片添加到桌面的那一刻被调用,完成一些卡片的初始化工作,比如卡片绑定一些初始化数据。
onAddForm(want: Want) {
const formData = '';
return formBindingData.createFormBindingData(formData);
}
// 卡片提供方收到卡片使用方将临时卡片转常态卡片的通知接口。临时卡片、常态卡片是卡片使用方的概念。
// 临时卡片是短期存在的,在特定事件或用户行为后显示,完成后自动消失。
// 常态卡片是持久存在的,在用户未进行清除或更改的情况下,会一直存在,平时开发的功能卡片属于常态卡片。当前卡片使用方不会使用临时卡片。
onCastToNormalForm(formId: string) {
}
// 卡片提供方接收携带参数的更新卡片的通知接口。获取最新数据后调用formProvider的updateForm接口刷新卡片数据。
onUpdateForm(formId: string) {
}
// 卡片提供方接收处理卡片事件的通知接口。用户点击某个按钮,触发我们定义的一个事件的时候,这个方法就会接收,卡片所有的交互逻辑主要在这里面进行处理。
onFormEvent(formId: string, message: string) {
}
// 卡片提供方接收销毁卡片的通知接口。
onRemoveForm(formId: string) {
}
// 卡片提供方接收查询卡片状态通知接口,默认返回卡片初始状态(该方法可以选择性重写)
onAcquireFormState(want: Want) {
return formInfo.FormState.READY;
}
}
2、卡片UI页面文件
@Entry
@Component
struct WidgetCard {
build() {
Column() {
}
.width('100%')
.height('100%')
.backgroundColor('#EBD9CC')
}
}
3、form_config.json卡片信息配置
{
"forms": [
{
"name": "widget",
"displayName": "$string:widget_display_name",
"description": "$string:widget_desc",
"src": "./ets/widget/pages/WidgetCard.ets", // 指定卡片需要呈现的UI页面路径
"uiSyntax": "arkts",
"window": {
"designWidth": 720,
"autoDesignWidth": true
},
"colorMode": "auto",
"isDynamic": true,
"isDefault": true,
"updateEnabled": false,
"scheduledUpdateTime": "10:30", // 卡片数据刷新时间设定
"updateDuration": 1,
"defaultDimension": "2*4", // 不同尺寸卡片设定
"supportDimensions": [
"2*4"
]
}
]
}
4、module.json5配置
{
"module": {
"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"
}
]
}
]
}
}
服务卡片与页面之间的交互
ArkTS卡片提供页面交互能力,包括卡片与卡片提供方(例如:应用)的页面跳转、卡片拉起卡片提供方进程、卡片与卡片提供方的消息传递。
支持router、message和call三种类型的事件,具体使用场景如下:
router事件(进入应用):可以使用router事件跳转到指定UIAbility,以完成点击卡片跳转至应用内页面的功能。对于非系统应用仅支持跳转到自己应用内的UIAbility。
message事件(更新卡片状态):可以使用message拉起FormExtensionAbility,通过onFormEvent接口回调通知,以完成点击卡片控件后传递消息给应用的功能。
call事件(后台任务):可以使用call事件拉起指定UIAbility到后台,再通过UIAbility申请对应后台长时任务完成音乐播放等功能。
1、卡片跳转到应用页面(router事件)
卡片中调用postCardAction方法打开EntryAbility,传递参数,并设定action为router类型。
在EntryAbility的生命周期onCreate或onNewWant中接收参数并按参数条件载入不同页面。
WidgetCard.ets
@Entry
@Component
struct WidgetCard {
build() {
Column() {
Button('router事件:打开PageA')
.height(30)
.onClick(() => {
postCardAction(this, {
action: 'router',
abilityName: 'EntryAbility',
params: { targetPage: 'funA' }
})
})
Button('router事件:打开PageB')
.height(30)
.onClick(() => {
postCardAction(this, {
action: 'router',
abilityName: 'EntryAbility',
params: { targetPage: 'funB' }
})
})
}
.width('100%')
.height('100%')
.backgroundColor('#EBD9CC')
}
}
EntryAbility.ets
import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
import { window } from '@kit.ArkUI';
export default class EntryAbility extends UIAbility {
private selectPage: string = '';
private currentWindowStage: window.WindowStage | null = null;
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
// 1-1. 获取router事件中传递的targetPage参数
if (want?.parameters?.params) {
// want?.parameters.params对应postCardAction()中params内容
let params: Record<string, Object> = JSON.parse(want?.parameters.params as string);
this.selectPage = params.targetPage as string;
}
}
// 1-2. 如果UIAbility已经在后台运行,在收到router事件后会触发onNewWant生命周期回调
onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void {
if (want?.parameters?.params) {
// want?.parameters.params对应postCardAction()中params内容
let params: Record<string, Object> = JSON.parse(want?.parameters.params as string);
this.selectPage = params.targetPage as string;
}
if (this.currentWindowStage !== null) {
this.onWindowStageCreate(this.currentWindowStage)
}
}
onDestroy(): void {
}
onWindowStageCreate(windowStage: window.WindowStage): void {
let targetPage: string;
// 2. 根据传递的targetPage不同,选择拉起不同的页面
switch (this.selectPage) {
case 'funA':
targetPage = 'pages/PageA';
break;
case 'funB':
targetPage = 'pages/PageB';
break;
default:
targetPage = 'pages/Index';
}
if (this.currentWindowStage === null) {
this.currentWindowStage = windowStage;
}
windowStage.loadContent(targetPage, (err) => {
});
}
onWindowStageDestroy(): void {
}
onForeground(): void {
}
onBackground(): void {
}
}
2、卡片传递消息给应用(message事件)
卡片中调用postCardAction方法,并设定action为message类型。
在EntryFormAbility的生命周期onFormEvent中接收message,并触发卡片刷新。
WidgetCard.ets
@LocalStorageProp('randomNumber') num: number = 0
Text('num: ' + this.num)
Button('message事件:生成随机数')
.height(30)
.onClick(() => {
postCardAction(this, {
action: 'message',
abilityName: 'EntryAbility',
params: { msgText: 'messageEvent' }
})
})
EntryFormAbility.ets
// 卡片提供方接收处理卡片事件的通知接口。用户点击某个按钮,触发我们定义的一个事件的时候,这个方法就会接收,卡片所有的交互逻辑主要在这里面进行处理。
onFormEvent(formId: string, message: string) {
class FormDataClass {
randomNumber: number = Math.random() // 和卡片布局中randomNumber对应,@LocalStorageProp('randomNumber') num: number = 0
}
let formData = new FormDataClass()
// formInfo:专门更新卡片数据的数据包
let formInfo: formBindingData.FormBindingData = formBindingData.createFormBindingData(formData);
formProvider.updateForm(formId, formInfo).then(() => {
}).catch((error: BusinessError) => {
})
}
3、卡片拉起应用UIAbility到后台(call事件))
申请ohos.permission.KEEP_BACKGROUND_RUNNING后台运行权限。
卡片中调用postCardAction方法,并设定action为call类型。
在EntryAbility的生命周期onCreate中监听call事件,并拉起应用后台任务。
module.json5
"requestPermissions": [
{
// 保持应用在后台持续运行(用于长/短时任务退后台,允许Service Ability在后台持续运行)
"name": "ohos.permission.KEEP_BACKGROUND_RUNNING",
"reason": "$string:permission_keep_background_running_desc",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "inuse"
}
}
],
resources > base > element > string.json
{
"name": "permission_keep_background_running_desc",
"value": "It is used to backtrack long/short tasks, allowing Service Ability to run continuously in the background"
},
WidgetCard.ets
Button('call事件')
.height(30)
.onClick(() => {
postCardAction(this, {
action: 'call',
abilityName: 'EntryAbility',
params: {
num: 1,
method: 'funA'
}
})
})
EntryAbility.ets
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
try {
// 卡片事件监听
this.callee.on('funA', (data: rpc.MessageSequence) => {
console.log('funA触发了', JSON.stringify(data.readString()));
return new MyParcelable(1, '') // 要求返回一个特殊数据类型
})
} catch (err) {
}
}
// ipc通信返回类型的实现,用于数据序列化和反序列化
class MyParcelable implements rpc.Parcelable {
num: number;
str: string;
constructor(num: number, str: string) {
this.num = num;
this.str = str;
}
marshalling(messageSequence: rpc.MessageSequence): boolean {
messageSequence.writeInt(this.num);
messageSequence.writeString(this.str);
return true;
}
unmarshalling(messageSequence: rpc.MessageSequence): boolean {
this.num = messageSequence.readInt();
this.str = messageSequence.readString();
return true;
}
}
应用内请求卡片加桌
从API version 18开始,Form Kit提供在应用内将ArkTS卡片添加到桌面的能力。
通过openFormManager方法在应用内添加拉起卡片管理页面入口。
want参数中的bundleName需跟当前应用包名保持一致。
Button('拉起卡片管理面板')
.height(30)
.onClick(() => {
const want: Want = {
bundleName: "com.example.myapplication", // 与bundleName一致
abilityName: 'EntryFormAbility',
parameters: {
'ohos.extra.param.key.form_dimension': 1,
'ohos.extra.param.key.form_name': 'widget',
'ohos.extra.param.key.module_name': 'entry'
},
};
try {
// 点击按钮后调用openFormManager方法,拉起卡片管理页面
formProvider.openFormManager(want);
} catch (error) {
}
})