提交 b0afadd5 编写于 作者: Z zhongjianfei

Add ArkTS Card Docs.

Signed-off-by: Nzhongjianfei <zhongjianfei@huawei.com>
Change-Id: I910f0f1f853c61744daf9b8b4122e0822cf65b32
上级 338fd314
...@@ -17,11 +17,11 @@ ...@@ -17,11 +17,11 @@
- ExtensionAbility组件 - ExtensionAbility组件
- [ExtensionAbility组件概述](extensionability-overview.md) - [ExtensionAbility组件概述](extensionability-overview.md)
- [ServiceExtensionAbility](serviceextensionability.md) - [ServiceExtensionAbility](serviceextensionability.md)
- [FormExtensionAbility(服务卡片)](widget-development-stage.md)
- [AccessibilityExtensionAbility](accessibilityextensionability.md) - [AccessibilityExtensionAbility](accessibilityextensionability.md)
- [EnterpriseAdminExtensionAbility](enterprise-extensionAbility.md) - [EnterpriseAdminExtensionAbility](enterprise-extensionAbility.md)
- [InputMethodExtensionAbility](inputmethodextentionability.md) - [InputMethodExtensionAbility](inputmethodextentionability.md)
- [WindowExtensionAbility](windowextensionability.md) - [WindowExtensionAbility](windowextensionability.md)
- [服务卡片开发指导](widget-development-stage.md)
- [AbilityStage组件容器](abilitystage.md) - [AbilityStage组件容器](abilitystage.md)
- [应用上下文Context](application-context-stage.md) - [应用上下文Context](application-context-stage.md)
- 信息传递载体Want - 信息传递载体Want
......
# 配置卡片的配置文件
卡片相关的配置文件主要包含FormExtensionAbility的配置和卡片的配置两部分:
1. 卡片需要在[module.json5配置文件](../quick-start/module-configuration-file.md)中的extensionAbilities标签下,配置FormExtensionAbility相关信息。FormExtensionAbility需要填写metadata元信息标签,其中键名称为固定字符串“ohos.extension.form”,资源为卡片的具体配置信息的索引。
配置示例如下:
```json
{
"module": {
...
"extensionAbilities": [
{
"name": "EntryFormAbility",
"srcEntry": "./ets/entryformability/EntryFormAbility.ts",
"label": "$string:EntryFormAbility_label",
"description": "$string:EntryFormAbility_desc",
"type": "form",
"metadata": [
{
"name": "ohos.extension.form",
"resource": "$profile:form_config"
}
]
}
]
}
}
```
2. 卡片的具体配置信息。在上述FormExtensionAbility的元信息(“metadata”配置项)中,可以指定卡片具体配置信息的资源索引。例如当resource指定为$profile:form_config时,会使用开发视图的resources/base/profile/目录下的form_config.json作为卡片profile配置文件。内部字段结构说明如下表所示。
**表1** 卡片form_config.json配置文件
| 属性名称 | 含义 | 数据类型 | 是否可缺省 |
| -------- | -------- | -------- | -------- |
| name | 表示卡片的类名,字符串最大长度为127字节。 | 字符串 | 否 |
| description | 表示卡片的描述。取值可以是描述性内容,也可以是对描述性内容的资源索引,以支持多语言。字符串最大长度为255字节。 | 字符串 | 可缺省,缺省为空。 |
| src | 表示卡片对应的UI代码的完整路径。当为ArkTS卡片时,完整路径需要包含卡片文件的后缀,如:"./ets/widget/pages/WidgetCard.ets"。当为JS卡片时,完整路径无需包含卡片文件的后缀,如:"./js/widget/pages/WidgetCard" | 字符串 | 否 |
| uiSyntax | 表示该卡片的类型,当前支持如下两种类型:<br/>-&nbsp;arkts:当前卡片为ArkTS卡片。<br/>-&nbsp;hml:当前卡片为JS卡片。 | 字符串 | 可缺省,缺省值为hml |
| window | 用于定义与显示窗口相关的配置。 | 对象 | 可缺省 |
| isDefault | 表示该卡片是否为默认卡片,每个UIAbility有且只有一个默认卡片。<br/>-&nbsp;true:默认卡片。<br/>-&nbsp;false:非默认卡片。 | 布尔值 | 否 |
| colorMode | 表示卡片的主题样式,取值范围如下:<br/>-&nbsp;auto:自适应。<br/>-&nbsp;dark:深色主题。<br/>-&nbsp;light:浅色主题。 | 字符串 | 可缺省,缺省值为“auto”。 |
| supportDimensions | 表示卡片支持的外观规格,取值范围:<br/>-&nbsp;1&nbsp;\*&nbsp;2:表示1行2列的二宫格。<br/>-&nbsp;2&nbsp;\*&nbsp;2:表示2行2列的四宫格。<br/>-&nbsp;2&nbsp;\*&nbsp;4:表示2行4列的八宫格。<br/>-&nbsp;4&nbsp;\*&nbsp;4:表示4行4列的十六宫格。 | 字符串数组 | 否 |
| defaultDimension | 表示卡片的默认外观规格,取值必须在该卡片supportDimensions配置的列表中。 | 字符串 | 否 |
| updateEnabled | 表示卡片是否支持周期性刷新(包含定时刷新和定点刷新),取值范围:<br/>-&nbsp;true:表示支持周期性刷新,可以在定时刷新(updateDuration)和定点刷新(scheduledUpdateTime)两种方式任选其一,当两者同时配置时,定时刷新优先生效。<br/>-&nbsp;false:表示不支持周期性刷新。 | 布尔类型 | 否 |
| scheduledUpdateTime | 表示卡片的定点刷新的时刻,采用24小时制,精确到分钟。<br/>&gt;&nbsp;**说明:**<br/>&gt;&nbsp;updateDuration参数优先级高于scheduledUpdateTime,两者同时配置时,以updateDuration配置的刷新时间为准。 | 字符串 | 可缺省,缺省时不进行定点刷新。 |
| updateDuration | 表示卡片定时刷新的更新周期,单位为30分钟,取值为自然数。<br/>当取值为0时,表示该参数不生效。<br/>当取值为正整数N时,表示刷新周期为30\*N分钟。<br/>&gt;&nbsp;**说明:**<br/>&gt;&nbsp;updateDuration参数优先级高于scheduledUpdateTime,两者同时配置时,以updateDuration配置的刷新时间为准。 | 数值 | 可缺省,缺省值为“0”。 |
| formConfigAbility | 表示卡片的配置跳转链接,采用URI格式。 | 字符串 | 可缺省,缺省值为空。 |
| formVisibleNotify | 标识是否允许卡片使用卡片可见性通知。 | 字符串 | 可缺省,缺省值为空。 |
| metadata | 表示卡片的自定义信息,包含customizeData数组标签。 | 对象 | 可缺省,缺省值为空。 |
配置示例如下:
```json
{
"forms": [
{
"name": "widget",
"description": "This is a service widget.",
"src": "./ets/widget/pages/WidgetCard.ets",
"uiSyntax": "arkts",
"window": {
"designWidth": 720,
"autoDesignWidth": true
},
"colorMode": "auto",
"isDefault": true,
"updateEnabled": true,
"scheduledUpdateTime": "10:30",
"updateDuration": 1,
"defaultDimension": "2*2",
"supportDimensions": [
"2*2"
]
}
]
}
```
# 使用方刷新卡片内容(仅对系统应用开放)
当使用方添加了一些周期性刷新的卡片后,由于周期性刷新的时间间隔限制,可以在使用方中提供按钮主动触发卡片的刷新。这种场景下使用方可以通过调用[requestForm](../reference/apis/js-apis-app-form-formHost.md#requestform)接口请求卡片刷新,系统会调用卡片提供方FormExtensionAbility中的[onUpdateForm](../reference/apis/js-apis-app-form-formExtensionAbility.md#onupdateform)生命周期回调,在回调中,可以使用[updateForm](../reference/apis/js-apis-app-form-formProvider.md#updateform)接口刷新卡片内容。onUpdateForm生命周期回调参考[通过FormExtensionAbility刷新卡片内容](arkts-ui-widget-event-formextensionability.md)
```ts
import formHost from '@ohos.app.form.formHost';
@Entry()
@Component
struct WidgetCard {
formId = ...; // 卡片ID
build() {
Button(`刷新卡片`)
.type(ButtonType.Capsule)
.width('50%')
.height(50)
.onClick(() => {
console.info('FormAbility update form click');
// formId需要为实际需要刷新的卡片ID
formHost.requestForm(this.formId.toString()).then(() => {
console.info('Succeeded in requestForming.');
});
})
...
}
}
```
# 创建一个ArkTS卡片
开发者可以使用IDE创建ArkTS卡片,具体步骤请参见[DevEco Studio服务卡片开发指南](https://gitee.com/link?target=https%3A%2F%2Fdeveloper.harmonyos.com%2Fcn%2Fdocs%2Fdocumentation%2Fdoc-guides%2Fohos-development-service-widget-0000001263280425)。使用IDE生成的卡片模板包括三个部分:卡片页面(WidgetCard.ets)、卡片生命周期管理(FormExtensionAbility)和卡片配置文件(form_config.json)。
在选择卡片的开发语言类型(Language)时,需要选择ArkTS选项,如下图所示。
![WidgetCreateProject](figures/WidgetCreateProject.png)
# 通过FormExtensionAbility刷新卡片内容
在卡片页面中可以通过**postCardAction**接口触发message事件至FormExtensionAbility,然后由FormExtensionAbility刷新卡片内容,下面是这种刷新方式的简单示例。
- 在卡片页面通过注册Button的onClick点击事件回调,并在回调中调用**postCardAction**接口触发事件至FormExtensionAbility
```ts
let storage = new LocalStorage();
@Entry(storage)
@Component
struct WidgetCard {
@LocalStorageProp('title') title: string = 'init';
@LocalStorageProp('detail') detail: string = 'init';
build() {
Column() {
Button('刷新')
.onClick(() => {
postCardAction(this, {
'action': 'message',
'params': {
'msgTest': 'messageEvent'
}
});
})
Text(`${this.title}`)
Text(`${this.detail}`)
}
.width('100%')
.height('100%')
}
}
```
- 在FormExtensionAbility的onFormEvent生命周期中调用[updateForm](../reference/apis/js-apis-app-form-formProvider.md#updateform)接口刷新卡片
```ts
import formBindingData from '@ohos.app.form.formBindingData';
import FormExtensionAbility from '@ohos.app.form.FormExtensionAbility';
import formProvider from '@ohos.app.form.formProvider';
export default class EntryFormAbility extends FormExtensionAbility {
onFormEvent(formId, message) {
// Called when a specified message event defined by the form provider is triggered.
console.info(`FormAbility onEvent, formId = ${formId}, message: ${JSON.stringify(message)}`);
let formData = {
'title': 'Title Update Success.', // 和卡片布局中对应
'detail': 'Detail Update Success.', // 和卡片布局中对应
};
let formInfo = formBindingData.createFormBindingData(formData)
formProvider.updateForm(formId, formInfo).then((data) => {
console.info('FormAbility updateForm success.' + JSON.stringify(data));
}).catch((error) => {
console.error('FormAbility updateForm failed: ' + JSON.stringify(error));
})
}
// ...
}
```
运行效果如下图所示。
![WidgetUpdatePage](figures/WidgetUpdatePage.png)
# 卡片事件能力说明
ArkTS卡片中提供了postCardAction()接口用于卡片内部和提供方应用间的交互,当前支持router、message和call三种类型的事件,仅在卡片中可以调用。
![WidgetPostCardAction](figures/WidgetPostCardAction.png)
接口定义:postCardAction(component: Object, action: Object): void
接口参数说明:
| **参数名** | **参数类型** | **必填** | **参数描述** |
| -------- | -------- | -------- | -------- |
| component | Object | 是 | 当前自定义组件的实例,通常传入this。 |
| action | Object | 是 | action的具体描述,详情见下表。 |
action参数说明:
| **Key** | **Value** | **样例描述** |
| -------- | -------- | -------- |
| "action" | string | action的类型,支持三种预定义的类型:<br/>-&nbsp;"router":应用跳转,触发后会跳转到对应UIAbility,仅允许跳转到当前应用的UIAbility。<br/>-&nbsp;"message":自定义消息,触发后会调用提供方FormExtensionAbility的[onFormEvent()](../reference/apis/js-apis-app-form-formExtensionAbility.md#onformevent)生命周期回调。<br/>-&nbsp;"call":应用非前台启动,触发后会启动对应的UIAbility,但不会调度到前台,call的目标应用需要具备后台运行权限([ohos.permission.KEEP_BACKGROUND_RUNNING](../security/permission-list.md#ohospermissionkeep_background_running))。 |
| "bundleName" | string | "router"&nbsp;/&nbsp;"call"&nbsp;类型时跳转的包名,可选。 |
| "moduleName" | string | "router"&nbsp;/&nbsp;"call"&nbsp;类型时跳转的模块名,可选。 |
| "abilityName" | string | "router"&nbsp;/&nbsp;"call"&nbsp;类型时跳转的UIAbility名,必填。 |
| "params" | Object | 当前action携带的额外参数,内容使用JSON格式的键值对形式。 |
postCardAction()接口示例代码:
```typescript
Button('跳转')
.width('40%')
.height('20%')
.onClick(() => {
postCardAction(this, {
'action': 'router',
'bundleName': 'com.example.myapplication',
'abilityName': 'EntryAbility',
'params': {
'message': 'testForRouter' // 自定义要发送的message
}
});
})
```
以下是卡片开发过程中可以通过卡片事件实现的典型开发场景:
- [通过FormExtensionAbility刷新卡片内容](arkts-ui-widget-event-formextensionability.md)
- [通过UIAbility刷新卡片内容](arkts-ui-widget-event-uiability.md)
- [使用router事件跳转到指定页面](arkts-ui-widget-event-router.md)
# 使用router事件跳转到指定页面
在卡片中使用**postCardAction**接口的router能力,能够快速拉起卡片提供方应用,因此页面较多的应用往往会通过卡片提供不同的跳转按钮,实现一键直达的效果。例如相机卡片,卡片上提供拍照、录像等按钮,点击不同按钮将拉起相机应用的不同页面,从而提高用户的体验。
![WidgerCameraCard](figures/WidgerCameraCard.png)
通常使用按钮控件来实现页面拉起,示例代码如下:
- 在卡片页面中布局两个按钮,点击其中一个按钮时调用postCardAction向指定UIAbility发送router事件,并在事件内定义需要传递的内容。
```ts
@Entry
@Component
struct WidgetCard {
build() {
Column() {
Button('功能A')
.margin('20%')
.onClick(() => {
console.info('Jump to EntryAbility funA');
postCardAction(this, {
'action': 'router',
'abilityName': 'EntryAbility', // 只能跳转到当前应用下的UIAbility
'params': {
'targetPage': 'funA' // 在EntryAbility中处理这个信息
}
});
})
Button('功能B')
.margin('20%')
.onClick(() => {
console.info('Jump to EntryAbility funB');
postCardAction(this, {
'action': 'router',
'abilityName': 'EntryAbility', // 只能跳转到当前应用下的UIAbility
'params': {
'targetPage': 'funB' // 在EntryAbility中处理这个信息
}
});
})
}
.width('100%')
.height('100%')
}
}
```
- 在UIAbility中接收router事件并获取参数,根据传递的message不同,选择拉起不同的页面。
```ts
import UIAbility from '@ohos.app.ability.UIAbility';
import window from '@ohos.window';
let selectPage = "";
let currentWindowStage = null;
export default class CameraAbility extends UIAbility {
// 如果UIAbility第一次启动,在收到Router事件后会触发onCreate生命周期回调
onCreate(want, launchParam) {
// 获取router事件中传递的targetPage参数
console.info("onCreate want:" + JSON.stringify(want));
if (want.parameters.params !== undefined) {
let params = JSON.parse(want.parameters.params);
console.info("onCreate router targetPage:" + params.targetPage);
selectPage = params.targetPage;
}
}
// 如果UIAbility已在后台运行,在收到Router事件后会触发onNewWant生命周期回调
onNewWant(want, launchParam) {
console.info("onNewWant want:" + JSON.stringify(want));
if (want.parameters.params !== undefined) {
let params = JSON.parse(want.parameters.params);
console.info("onNewWant router targetPage:" + params.targetPage);
selectPage = params.targetPage;
}
if (currentWindowStage != null) {
this.onWindowStageCreate(currentWindowStage);
}
}
onWindowStageCreate(windowStage: window.WindowStage) {
let targetPage;
// 根据传递的targetPage不同,选择拉起不同的页面
switch (selectPage) {
case 'funA':
targetPage = 'pages/FunA';
break;
case 'funB':
targetPage = 'pages/FunB';
break;
default:
targetPage = 'pages/Index';
}
if (currentWindowStage === null) {
currentWindowStage = windowStage;
}
windowStage.loadContent(targetPage, (err, data) => {
if (err && err.code) {
console.info('Failed to load the content. Cause: %{public}s', JSON.stringify(err));
return;
}
});
}
};
```
# 通过UIAbility刷新卡片内容
在卡片页面中可以通过**postCardAction**接口触发router事件或者call事件拉起UIAbility,然后由UIAbility刷新卡片内容,下面是这种刷新方式的简单示例。
- 在卡片页面通过注册Button的onClick点击事件回调,并在回调中调用**postCardAction**接口触发事件至FormExtensionAbility。
```ts
let storage = new LocalStorage();
@Entry(storage)
@Component
struct WidgetCard {
@LocalStorageProp('detail') detail: string = 'init';
build() {
Column() {
Button('跳转')
.margin('20%')
.onClick(() => {
console.info('postCardAction to EntryAbility');
postCardAction(this, {
'action': 'router',
'abilityName': 'EntryAbility', // 只能跳转到当前应用下的UIAbility
'params': {
'detail': 'RouterFromCard'
}
});
})
Text(`${this.detail}`).margin('20%')
}
.width('100%')
.height('100%')
}
}
```
- 在UIAbility的onCreate()或者onNewWant()生命周期中可以通过入参want获取卡片的formID和传递过来的参数信息,然后调用[updateForm](../reference/apis/js-apis-app-form-formProvider.md#updateform)接口刷新卡片。
```ts
import UIAbility from '@ohos.app.ability.UIAbility';
import formBindingData from '@ohos.app.form.formBindingData';
import formProvider from '@ohos.app.form.formProvider';
import formInfo from '@ohos.app.form.formInfo';
export default class EntryAbility extends UIAbility {
// 如果UIAbility第一次启动,在收到Router事件后会触发onCreate生命周期回调
onCreate(want, launchParam) {
console.info('Want:' + JSON.stringify(want));
if (want.parameters[formInfo.FormParam.IDENTITY_KEY] !== undefined) {
let curFormId = want.parameters[formInfo.FormParam.IDENTITY_KEY];
let message = JSON.parse(want.parameters.params).detail;
console.info(`UpdateForm formId: ${curFormId}, message: ${message}`);
let formData = {
"detail": message + ': onCreate UIAbility.', // 和卡片布局中对应
};
let formMsg = formBindingData.createFormBindingData(formData)
formProvider.updateForm(curFormId, formMsg).then((data) => {
console.info('updateForm success.' + JSON.stringify(data));
}).catch((error) => {
console.error('updateForm failed:' + JSON.stringify(error));
})
}
}
// 如果UIAbility已在后台运行,在收到Router事件后会触发onNewWant生命周期回调
onNewWant(want, launchParam) {
console.info('onNewWant Want:' + JSON.stringify(want));
if (want.parameters[formInfo.FormParam.IDENTITY_KEY] !== undefined) {
let curFormId = want.parameters[formInfo.FormParam.IDENTITY_KEY];
let message = JSON.parse(want.parameters.params).detail;
console.info(`UpdateForm formId: ${curFormId}, message: ${message}`);
let formData = {
"detail": message + ': onNewWant UIAbility.', // 和卡片布局中对应
};
let formMsg = formBindingData.createFormBindingData(formData)
formProvider.updateForm(curFormId, formMsg).then((data) => {
console.info('updateForm success.' + JSON.stringify(data));
}).catch((error) => {
console.error('updateForm failed:' + JSON.stringify(error));
})
}
}
...
}
```
# 刷新本地图片和网络图片
在卡片上通常需要展示本地图片或从网络上下载的图片,获取本地图片和网络图片需要通过FormExtensionAbility来实现,如下示例代码介绍了如何在卡片上显示本地图片和网络图片。
1. 下载网络图片需要使用到网络能力,需要申请ohos.permission.INTERNET权限,配置方式请参见[配置文件权限声明](../security/accesstoken-guidelines.md)
2. 在EntryFormAbility中的onAddForm生命周期回调中实现本地文件的刷新
```ts
import formBindingData from '@ohos.app.form.formBindingData';
import formProvider from '@ohos.app.form.formProvider';
import FormExtensionAbility from '@ohos.app.form.FormExtensionAbility';
import request from '@ohos.request';
import fs from '@ohos.file.fs';
export default class EntryFormAbility extends FormExtensionAbility {
...
// 在添加卡片时,打开一个本地图片并将图片内容传递给卡片页面显示
onAddForm(want) {
// 假设在当前卡片应用的tmp目录下有一个本地图片:head.PNG
let tempDir = this.context.getApplicationContext().tempDir;
// 打开本地图片并获取其打开后的fd
let file;
try {
file = fs.openSync(tempDir + '/' + 'head.PNG');
} catch (e) {
console.error(`openSync failed: ${JSON.stringify(e)}`);
}
let formData = {
'text': 'Image: Bear',
'imgName': 'imgBear',
'formImages': {
'imgBear': file.fd
},
'loaded': true
}
// 将fd封装在formData中并返回至卡片页面
return formBindingData.createFormBindingData(formData);
}
...
}
```
3. 在EntryFormAbility中的onFormEvent生命周期回调中实现网络文件的刷新
```ts
import formBindingData from '@ohos.app.form.formBindingData';
import formProvider from '@ohos.app.form.formProvider';
import FormExtensionAbility from '@ohos.app.form.FormExtensionAbility';
import request from '@ohos.request';
import fs from '@ohos.file.fs';
export default class EntryFormAbility extends FormExtensionAbility {
// 在卡片页面触发message事件时,下载一个网络图片,并将网络图片内容传递给卡片页面显示
onFormEvent(formId, message) {
let formInfo = formBindingData.createFormBindingData({
'text': '刷新中...'
})
formProvider.updateForm(formId, formInfo)
// 注意:FormExtensionAbility在触发生命周期回调时被拉起,仅能在后台存在5秒
// 建议下载能快速下载完成的小文件,如在5秒内未下载完成,则此次网络图片无法刷新至卡片页面上
let netFile = 'https://xxxx/xxxx.png'; // 需要在此处使用真实的网络图片下载链接
let tempDir = this.context.getApplicationContext().tempDir;
let tmpFile = tempDir + '/file' + Date.now();
request.downloadFile(this.context, {
url: netFile, filePath: tmpFile
}).then((task) => {
task.on('complete', function callback() {
console.info('ArkTSCard download complete:' + tmpFile);
let file;
try {
file = fs.openSync(tmpFile);
} catch (e) {
console.error(`openSync failed: ${JSON.stringify(e)}`);
}
let formData = {
'text': 'Image: Https',
'imgName': 'imgHttps',
'formImages': {
'imgHttps': file.fd
},
'loaded': true
}
let formInfo = formBindingData.createFormBindingData(formData)
formProvider.updateForm(formId, formInfo).then((data) => {
console.info('FormAbility updateForm success.' + JSON.stringify(data));
}).catch((error) => {
console.error('FormAbility updateForm failed: ' + JSON.stringify(error));
})
})
task.on('fail', function callBack(err) {
console.info('ArkTSCard download task failed. Cause:' + err);
let formInfo = formBindingData.createFormBindingData({
'text': '刷新失败'
})
formProvider.updateForm(formId, formInfo)
});
}).catch((err) => {
console.error('Failed to request the download. Cause: ' + JSON.stringify(err));
});
}
...
};
```
4. 在卡片页面通过Image组件展示EntryFormAbility传递过来的卡片内容。
```ts
let storage = new LocalStorage();
@Entry(storage)
@Component
struct WidgetCard {
@LocalStorageProp('text') text: string = '加载中...';
@LocalStorageProp('loaded') loaded: boolean = false;
@LocalStorageProp('imgName') imgName: string = 'name';
build() {
Column() {
Text(this.text)
.fontSize('12vp')
.textAlign(TextAlign.Center)
.width('100%')
.height('15%')
Row() {
if (this.loaded) {
Image('memory://' + this.imgName)
.width('50%')
.height('50%')
.margin('5%')
} else {
Image('common/start.PNG')
.width('50%')
.height('50%')
.margin('5%')
}
}.alignItems(VerticalAlign.Center)
.justifyContent(FlexAlign.Center)
Button('刷新')
.height('15%')
.onClick(() => {
postCardAction(this, {
'action': 'message',
'params': {
'info': 'refreshImage'
}
});
})
}
.width('100%').height('100%')
.alignItems(HorizontalAlign.Center)
.padding('5%')
}
}
```
> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
> - Image组件通过入参(**memory://fileName**)中的**memory://**标识来进行远端内存图片显示,其中**fileName**需要和EntryFormAbility传递对象(**'formImages': {key: fd})**中的**key**相对应。
>
> - Image组件通过传入的参数是否有变化来决定是否刷新图片,因此EntryFormAbility每次传递过来的**imgName**都需要不同,连续传递两个相同的**imgName**时,图片不会刷新。
# 卡片数据交互说明
ArkTS卡片框架提供了updateForm()接口和requestForm()接口主动触发卡片的页面刷新。**(介绍下LocalStorageProp在这个过程中起到的作用)**
![WidgetLocalStorageProp](figures/WidgetLocalStorageProp.png)
| 接口 | 是否系统能力 | 约束 |
| -------- | -------- | -------- |
| updateForm | 否 | 1.&nbsp;提供方调用。<br/>2.&nbsp;提供方仅允许刷新自己的卡片,其他提供方的卡片无法刷新。 |
| requestForm | 是 | 1.&nbsp;使用方调用。<br/>2.&nbsp;仅允许刷新添加到当前使用方的卡片,添加到其他使用方的卡片无法刷新。 |
下面介绍卡片页面刷新的典型场景。
- [定时刷新和定点刷新](arkts-ui-widget-update-by-time.md)
- [刷新本地图片和网络图片](arkts-ui-widget-image-update.md)
- [根据卡片状态刷新不同内容](arkts-ui-widget-update-by-status.md)
- [使用方刷新卡片内容(仅对系统应用开放)](arkts-ui-widget-content-update.md)
\ No newline at end of file
# 卡片生命周期管理
创建ArkTS卡片,需实现[FormExtensionAbility](../reference/apis/js-apis-app-form-formExtensionAbility.md)生命周期接口。
1. 在EntryFormAbility.ts中,导入相关模块。
```ts
import formInfo from '@ohos.app.form.formInfo';
import formBindingData from '@ohos.app.form.formBindingData';
import FormExtensionAbility from '@ohos.app.form.FormExtensionAbility';
import formProvider from '@ohos.app.form.formProvider';
```
2. 在EntryFormAbility.ts中,实现[FormExtensionAbility](../reference/apis/js-apis-app-form-formExtensionAbility.md)生命周期接口,其中在onAddForm的入参want中可以通过[FormParam](../reference/apis/js-apis-app-form-formInfo.md#formparam)取出卡片的相关信息。
```typescript
import formInfo from '@ohos.app.form.formInfo';
import formBindingData from '@ohos.app.form.formBindingData';
import FormExtensionAbility from '@ohos.app.form.FormExtensionAbility';
import formProvider from '@ohos.app.form.formProvider';
export default class EntryFormAbility extends FormExtensionAbility {
onAddForm(want) {
console.info('[EntryFormAbility] onAddForm');
// 在入参want中可以取出卡片的唯一标识:formId
let formId: string = want.parameters[formInfo.FormParam.IDENTITY_KEY];
// 使用方创建卡片时触发,提供方需要返回卡片数据绑定类
let obj = {
'title': 'titleOnAddForm',
'detail': 'detailOnAddForm'
};
let formData = formBindingData.createFormBindingData(obj);
return formData;
}
onCastToNormalForm(formId) {
// Called when the form provider is notified that a temporary form is successfully
// converted to a normal form.
// 使用方将临时卡片转换为常态卡片触发,提供方需要做相应的处理
console.info(`[EntryFormAbility] onCastToNormalForm, formId: ${formId}`);
}
onUpdateForm(formId) {
// 若卡片支持定时更新/定点更新/卡片使用方主动请求更新功能,则提供方需要重写该方法以支持数据更新
console.info('[EntryFormAbility] onUpdateForm');
let obj = {
'title': 'titleOnUpdateForm',
'detail': 'detailOnUpdateForm'
};
let formData = formBindingData.createFormBindingData(obj);
formProvider.updateForm(formId, formData).catch((err) => {
if (err) {
// 异常分支打印
console.error(`[EntryFormAbility] Failed to updateForm. Code: ${err.code}, message: ${err.message}`);
return;
}
});
}
onChangeFormVisibility(newStatus) {
// Called when the form provider receives form events from the system.
// 需要配置formVisibleNotify为true,且为系统应用才会回调
console.info('[EntryFormAbility] onChangeFormVisibility');
}
onFormEvent(formId, message) {
// Called when a specified message event defined by the form provider is triggered.
// 若卡片支持触发事件,则需要重写该方法并实现对事件的触发
console.info('[EntryFormAbility] onFormEvent');
}
onRemoveForm(formId) {
// Called to notify the form provider that a specified form has been destroyed.
// 当对应的卡片删除时触发的回调,入参是被删除的卡片ID
console.info('[EntryFormAbility] onRemoveForm');
}
onConfigurationUpdate(config) {
// 当系统配置信息置更新时触发的回调
console.info('[EntryFormAbility] configurationUpdate:' + JSON.stringify(config));
}
onAcquireFormState(want) {
// Called to return a {@link FormState} object.
// 卡片提供方接收查询卡片状态通知接口,默认返回卡片初始状态。
return formInfo.FormState.READY;
}
}
```
> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
> FormExtensionAbility进程不能常驻后台,即在卡片生命周期回调函数中无法处理长时间的任务,在生命周期调度完成后会继续存在5秒,如5秒内没有新的生命周期回调触发则进程自动退出。针对可能需要5秒以上才能完成的业务逻辑,建议[拉起主应用](arkts-ui-widget-event-uiability.md)进行处理,处理完成后使用[updateForm](../reference/apis/js-apis-app-form-formProvider.md#updateform)通知卡片进行刷新。
# ArkTS卡片相关模块
**图1** ArkTS卡片相关模块  
![WidgetModules](figures/WidgetModules.png)
- [FormExtensionAbility](../reference/apis/js-apis-app-form-formExtensionAbility.md):卡片扩展模块,提供卡片创建、销毁、刷新等生命周期回调。
- [FormExtensionContext](../reference/apis/js-apis-inner-application-formExtensionContext.md):FormExtensionAbility的上下文环境,提供FormExtensionAbility具有的接口和能力。
- [formProvider](../reference/apis/js-apis-app-form-formProvider.md):提供卡片提供方相关的接口能力,可通过该模块提供接口实现更新卡片、设置卡片更新时间、获取卡片信息、请求发布卡片等。
- [formInfo](../reference/apis/js-apis-app-form-formInfo.md):提供了卡片信息和状态等相关类型和枚举。
- [formBindingData](../reference/apis/js-apis-app-form-formBindingData.md):提供卡片数据绑定的能力,包括FormBindingData对象的创建、相关信息的描述。
- [页面布局(Card.ets)](arkts-ui-widget-page-overview.md):提供声明式范式的UI接口能力。
- [ArkTS卡片特有能力](arkts-ui-widget-event-overview.md):postCardAction用于卡片内部和提供方应用间的交互,仅在卡片中可以调用。
- [ArkTS卡片能力列表](arkts-ui-widget-page-overview.md#arkts卡片支持的页面能力):列举了能在ArkTS卡片中使用的API、组件、事件、属性和生命周期调度。
- [卡片配置](arkts-ui-widget-configuration.md):包含FormExtensionAbility的配置和卡片的配置
-[module.json5配置文件](../quick-start/module-configuration-file.md)中的extensionAbilities标签下,配置FormExtensionAbility相关信息。
- 在resources/base/profile/目录下的[form_config.json配置文件](arkts-ui-widget-configuration.md)中,配置卡片(WidgetCard.ets)相关信息。
# 卡片使用动效能力
ArkTS卡片开放了使用动画效果的能力,支持[显式动画](../reference/arkui-ts/ts-explicit-animation.md)[属性动画](../reference/arkui-ts/ts-animatorproperty.md)[组件内转场](../reference/arkui-ts/ts-transition-animation-component.md)能力。需要注意的是,ArkTS卡片使用动画效果时具有以下限制:
**表1** 动效参数限制
| 名称 | 参数说明 | 限制描述 |
| -------- | -------- | -------- |
| duration | 动画播放时长 | 限制最长的动效播放时长为1秒,当设置大于1秒的时间时,动效时长仍为1秒。 |
| tempo | 动画播放速度 | 卡片中禁止设置此参数,使用默认值1。 |
| delay | 动画延迟执行的时长 | 卡片中禁止设置此参数,使用默认值0。 |
| iterations | 动画播放次数 | 卡片中禁止设置此参数,使用默认值1。 |
以下示例代码实现了按钮旋转的动画效果:
![WidgetAnimation](figures/WidgetAnimation.gif)
```ts
@Entry
@Component
struct AttrAnimationExample {
@State rotateAngle: number = 0;
build() {
Column() {
Button('change rotate angle')
.onClick(() => {
this.rotateAngle = 90;
})
.margin(50)
.rotate({ angle: this.rotateAngle })
.animation({
curve: Curve.EaseOut,
iterations: 1,
playMode: PlayMode.AlternateReverse
})
}.width('100%').margin({ top: 20 })
}
}
```
# 卡片使用自定义绘制能力
ArkTS卡片开放了自定义绘制的能力,在卡片上可以通过[Canvas](../reference/arkui-ts/ts-components-canvas-canvas.md)组件创建一块画布,然后通过[CanvasRenderingContext2D](../reference/arkui-ts/ts-canvasrenderingcontext2d.md)对象在画布上进行自定义图形的绘制,如下示例代码实现了在画布的中心绘制了一个笑脸。
```typescript
@Entry
@Component
struct Card {
private canvasWidth: number = 0;
private canvasHeight: number = 0;
// 初始化CanvasRenderingContext2D和RenderingContextSettings
private settings: RenderingContextSettings = new RenderingContextSettings(true);
private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings);
build() {
Column() {
Row() {
Canvas(this.context)
.margin('5%')
.width('90%')
.height('90%')
.onReady(() => {
console.info('[ArkTSCard] onReady for canvas draw content');
// 在onReady回调中获取画布的实际宽和高
this.canvasWidth = this.context.width;
this.canvasHeight = this.context.height;
// 绘制画布的背景
this.context.fillStyle = 'rgba(203, 154, 126, 1.00)';
this.context.fillRect(0, 0, this.canvasWidth, this.canvasHeight);
// 在画布的中心绘制一个红色的圆
this.context.beginPath();
let radius = this.context.width / 3
let circleX = this.context.width / 2
let circleY = this.context.height / 2
this.context.moveTo(circleX - radius, circleY);
this.context.arc(circleX, circleY, radius, 2 * Math.PI, 0, true);
this.context.closePath();
this.context.fillStyle = 'red';
this.context.fill();
// 绘制笑脸的左眼
let leftR = radius / 4
let leftX = circleX - (radius / 2)
let leftY = circleY - (radius / 3.5)
this.context.beginPath();
this.context.arc(leftX, leftY, leftR, 0, Math.PI, true);
this.context.strokeStyle = '#ffff00'
this.context.lineWidth = 10
this.context.stroke()
// 绘制笑脸的右眼
let rightR = radius / 4
let rightX = circleX + (radius / 2)
let rightY = circleY - (radius / 3.5)
this.context.beginPath();
this.context.arc(rightX, rightY, rightR, 0, Math.PI, true);
this.context.strokeStyle = '#ffff00'
this.context.lineWidth = 10
this.context.stroke()
// 绘制笑脸的嘴巴
let mouthR = radius / 2.5
let mouthX = circleX
let mouthY = circleY + (radius / 3)
this.context.beginPath();
this.context.arc(mouthX, mouthY, mouthR, Math.PI, 0, true);
this.context.strokeStyle = '#ffff00'
this.context.lineWidth = 10
this.context.stroke()
})
}
}.height('100%').width('100%')
}
}
```
运行效果如下图所示。
![WidgetCanvasDemo](figures/WidgetCanvasDemo.jpeg)
# 卡片页面能力说明
开发者可以使用声明式范式开发ArkTS卡片页面。如下卡片页面由DevEco Studio模板自动生成,开发者可以根据自身的业务场景进行调整。
![WidgetPreviewPage](figures/WidgetPreviewPage.png)
ArkTS卡片具备JS卡片的全量能力,并且新增了动效能力和自定义绘制的能力,支持[声明式范式](../reference/arkui-ts/Readme-CN.md)的部分组件、事件、动效、数据管理、状态管理能力,详见“[ArkTS卡片支持的页面能力](#arkts卡片支持的页面能力)”。
## ArkTS卡片支持的页面能力
ArkTS卡片支持的页面能力如下,详细介绍请参见[ArkTS声明式开发范式API参考](../reference/arkui-ts/Readme-CN.md)
在这些能力中,只有标识“支持在ArkTS卡片中使用”的具体能力可用于ArkTS卡片,同时请留意卡片场景下的能力差异说明。
- 属性动画
- 显式动画
- 组件内转场
- 像素单位
- Blank组件
- Button组件
- Checkbox组件
- CheckboxGroup组件
- DataPanel组件
- Divider组件
- Gauge组件
- Image组件
- LoadingProgress组件
- Marquee组件
- Progress组件
- Qrcode组件
- Radio组件
- Rating组件
- Slider组件
- Span组件
- Text组件
- Toggle组件
- Canvas绘制上下文对象
- Canvas组件
- 渐变对象
- ImageBitmap对象
- ImageData对象
- Path2D对象
- ForEach组件
- Badge容器组件
- Column容器组件
- Counter容器组件
- Flex容器组件
- GridCol容器组件
- GridRow容器组件
- List容器组件
- ListItem容器组件
- RelativeContainer容器组件
- Row容器组件
- Stack容器组件
- Circle绘制组件
- Ellipse绘制组件
- Line绘制组件
- Path绘制组件
- Polygon绘制组件
- Polyline绘制组件
- Rect绘制组件
- Shape绘制组件
- Background通用属性
- BackgroundBlurStyle通用属性
- BorderImage通用属性
- Border通用属性
- ComponentId通用属性
- Enable通用属性
- FlexLayout通用属性
- GradientColor通用属性
- ImageEffect通用属性
- LayoutConstraints通用属性
- Location通用属性
- Opacity通用属性
- Overlay通用属性
- PolymorphicStyle通用属性
- SharpClipping通用属性
- Size通用属性
- Touch-target通用属性
- Transformation通用属性
- Visibility通用属性
- ZOrder通用属性
- 点击事件
- 挂载卸载事件
- 组件生命周期
- 状态管理
# 根据卡片状态刷新不同内容
相同的卡片可以添加到桌面上实现不同的功能,比如添加两张桌面的卡片,一张显示杭州的天气,一张显示北京的天气,设置每天早上7点触发定时刷新,卡片需要感知当前的配置是杭州还是北京,然后将对应城市的天气信息刷新到卡片上,以下示例介绍了如何根据卡片的状态动态选择需要刷新的内容。
- 卡片配置文件:配置每天早上7点触发定时刷新
```json
{
"forms": [
{
"name": "widget",
"description": "This is a service widget.",
"src": "./ets/widget/pages/WidgetCard.ets",
"uiSyntax": "arkts",
"window": {
"designWidth": 720,
"autoDesignWidth": true
},
"colorMode": "auto",
"isDefault": true,
"updateEnabled": true,"scheduledUpdateTime": "07:00",
"updateDuration": 0,
"defaultDimension": "2*2",
"supportDimensions": ["2*2"]
}
]
}
```
- 卡片页面:卡片具备不同的状态选择,在不同的状态下需要刷新不同的内容,因此在状态发生变化时通过postCardAction通知EntryFormAbility。
```ts
let storage = new LocalStorage();
@Entry(storage)
@Component
struct WidgetCard {
@LocalStorageProp('textA') textA: string = '待刷新...';
@LocalStorageProp('textB') textB: string = '待刷新...';
@State selectA: boolean = false;
@State selectB: boolean = false;
build() {
Column() {
Row() {
Checkbox({ name: 'checkbox1', group: 'checkboxGroup' })
.select(false)
.onChange((value: boolean) => {
this.selectA = value;
postCardAction(this, {
'action': 'message',
'params': {
'selectA': JSON.stringify(value)
}
});
})
Text('状态A')
}
Row() {
Checkbox({ name: 'checkbox2', group: 'checkboxGroup' })
.select(false)
.onChange((value: boolean) => {
this.selectB = value;
postCardAction(this, {
'action': 'message',
'params': {
'selectB': JSON.stringify(value)
}
});
})
Text('状态B')
}
Row() { // 选中状态A才会进行刷新的内容
Text('状态A: ')
Text(this.textA)
}
Row() { // 选中状态B才会进行刷新的内容
Text('状态B: ')
Text(this.textB)
}
}.padding('10%')
}
}
```
- EntryFormAbility:将卡片的状态存储在本地数据库中,在刷新事件回调触发时,通过formId获取当前卡片的状态,然后根据卡片的状态选择不同的刷新内容。
```ts
import formProvider from '@ohos.app.form.formProvider';
import formBindingData from '@ohos.app.form.formBindingData';
import FormExtensionAbility from '@ohos.app.form.FormExtensionAbility';
import dataStorage from '@ohos.data.storage'
export default class EntryFormAbility extends FormExtensionAbility {
onAddForm(want) {
let formData = {};
return formBindingData.createFormBindingData(formData);
}
onRemoveForm(formId) {
console.info('onRemoveForm, formId:' + formId);
let storeDB = dataStorage.getStorageSync(this.context.filesDir + 'myStore')
storeDB.deleteSync('A' + formId);
storeDB.deleteSync('B' + formId);
}
onCastToNormalForm(formId) {
console.info('onCastToNormalForm, formId:' + formId);
let storeDB = dataStorage.getStorageSync(this.context.filesDir + 'myStore')
storeDB.putSync('A' + formId, 'false');
storeDB.putSync('B' + formId, 'false');
storeDB.flushSync();
}
onUpdateForm(formId) {
let storeDB = dataStorage.getStorageSync(this.context.filesDir + 'myStore')
let stateA = storeDB.getSync('A' + formId, 'false').toString()
let stateB = storeDB.getSync('B' + formId, 'false').toString()
// A状态选中则更新textA
if (stateA === 'true') {
let formInfo = formBindingData.createFormBindingData({
'textA': 'AAA'
})
formProvider.updateForm(formId, formInfo)
}
// B状态选中则更新textB
if (stateB === 'true') {
let formInfo = formBindingData.createFormBindingData({
'textB': 'BBB'
})
formProvider.updateForm(formId, formInfo)
}
}
onFormEvent(formId, message) {
// 存放卡片状态
console.info('onFormEvent formId:' + formId + 'msg:' + message);
let storeDB = dataStorage.getStorageSync(this.context.filesDir + 'myStore')
let msg = JSON.parse(message)
if (msg.selectA != undefined) {
console.info('onFormEvent selectA info:' + msg.selectA);
storeDB.putSync('A' + formId, msg.selectA);
}
if (msg.selectB != undefined) {
console.info('onFormEvent selectB info:' + msg.selectB);
storeDB.putSync('B' + formId, msg.selectB);
}
storeDB.flushSync();
}
};
```
> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
> 通过本地数据库进行卡片信息的持久化时,建议在卡片转为常态卡片(**[onCastToNormalForm](../reference/apis/js-apis-app-form-formExtensionAbility.md#oncasttonormalform)**)时进行持久化,并在卡片销毁(**[onRemoveForm](../reference/apis/js-apis-app-form-formExtensionAbility.md#onremoveform)**)时删除当前卡片存储的持久化信息,避免反复添加删除卡片导致数据库文件持续变大。
# 定时刷新和定点刷新
当前卡片框架提供了如下几种按时间刷新卡片的方式:
- 定时刷新:表示每隔一段时间刷新卡片内容,在form_config.json文件中配置,详见[updateDuration](arkts-ui-widget-configuration.md)字段。例如,每小时刷新一次卡片内容。注意:updateDuration(定时刷新)优先级比scheduledUpdateTime(定点刷新)高,配置定时刷新后,定点刷新将失效。
```json
{
"forms": [
{
"name": "widget",
"description": "This is a service widget.",
"src": "./ets/widget/pages/WidgetCard.ets",
"uiSyntax": "arkts",
"window": {
"designWidth": 720,
"autoDesignWidth": true
},
"colorMode": "auto",
"isDefault": true,
"updateEnabled": true, // 使能刷新功能
"scheduledUpdateTime": "10:30",
"updateDuration": 2, // 设置卡片定时刷新的更新周期(单位为30分钟,取值为自然数)
"defaultDimension": "2*2",
"supportDimensions": ["2*2"]
}
]
}
```
- 定点刷新:表示每天在某个时间点刷新,在form_config.json文件中配置,详见[scheduledUpdateTime](arkts-ui-widget-configuration.md)字段。例如,每天在10:30更新卡片内容。
> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
> 当同时配置了定时刷新(updateDuration)和定点刷新(scheduledUpdateTime)时,定时刷新的优先级更高。如果想要配置定点刷新,则需要将updateDuration配置为0。
```json
{
"forms": [
{
"name": "widget",
"description": "This is a service widget.",
"src": "./ets/widget/pages/WidgetCard.ets",
"uiSyntax": "arkts",
"window": {
"designWidth": 720,
"autoDesignWidth": true
},
"colorMode": "auto",
"isDefault": true,
"updateEnabled": true, // 使能刷新功能
"scheduledUpdateTime": "10:30", // 设置卡片的定点刷新的时刻
"updateDuration": 0,
"defaultDimension": "2*2",
"supportDimensions": ["2*2"]
}
]
}
```
- 下次刷新:通过[setFormNextRefreshTime](../reference/apis/js-apis-app-form-formProvider.md#setformnextrefreshtime)接口指定卡片的下一次刷新时间(最短时间5分钟),例如,在接口调用的5分钟后刷新卡片内容。
```ts
import formProvider from '@ohos.app.form.formProvider';
let formId = '123456789'; // 实际业务场景需要使用正确的formId
try {
// 设置过5分钟后更新卡片内容
formProvider.setFormNextRefreshTime(formId, 5, (err, data) => {
if (err) {
console.error(`Failed to setFormNextRefreshTime. Code: ${err.code}, message: ${err.message}`);
return;
} else {
console.info('Succeeded in setFormNextRefreshTimeing.');
}
});
} catch (err) {
console.error(`Failed to setFormNextRefreshTime. Code: ${err.code}, message: ${err.message}`);
}
```
在触发定时、定点或主动刷新后,系统会调用FormExtensionAbility的[onUpdateForm](../reference/apis/js-apis-app-form-formExtensionAbility.md#onupdateform)生命周期回调,在回调中,可以使用[updateForm](../reference/apis/js-apis-app-form-formProvider.md#updateform)进行提供方刷新卡片。onUpdateForm生命周期回调参考[通过FormExtensionAbility刷新卡片内容](arkts-ui-widget-event-formextensionability.md)
> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
> 1. 定时刷新有配额限制,每张卡片每天最多通过定时方式触发刷新50次,定时刷新包含[卡片配置项updateDuration](arkts-ui-widget-configuration.md)和调用[setFormNextRefreshTime](../reference/apis/js-apis-app-form-formProvider.md#setformnextrefreshtime)两种,当达到50次配额后,无法通过定时方式再次触发刷新,刷新次数会在每天的0点重置。
>
> 2. 当前定时刷新使用同一个计时器进行计时,因此卡片定时刷新的第一次刷新会有最多30分钟的偏差。比如第一张卡片A(每隔半小时刷新一次)在3点20分添加成功,定时器启动并每隔半小时触发一次事件,第二张卡片B(每隔半小时刷新一次)在3点40分添加成功,在3点50分定时器事件触发时,卡片A触发定时刷新,卡片B会在下次事件(4点20分)中才会触发。
>
> 3. 定时刷新和定点刷新仅在屏幕亮屏情况下才会触发,在灭屏场景下仅会将记录刷新动作,待亮屏时统一进行刷新。
# ArkTS卡片运行机制
## 实现原理
**图1** ArkTS卡片实现原理  
![WidgetPrinciple](figures/WidgetPrinciple.png)
- 卡片使用方:显示卡片内容的宿主应用,控制卡片在宿主中展示的位置,当前仅系统应用可以作为卡片使用方。
- 卡片提供方:提供卡片显示内容的应用,控制卡片的显示内容、控件布局以及控件点击事件。
- 卡片管理服务:用于管理系统中所添加卡片的常驻代理服务,提供[formProvider](../reference/apis/js-apis-app-form-formProvider.md)[formHost](../reference/apis/js-apis-app-form-formHost.md)的接口能力,同时提供卡片对象的管理与使用以及卡片周期性刷新等能力。
- 卡片渲染服务:用于管理卡片渲染实例,渲染实例与卡片使用方上的[卡片组件](../reference/arkui-ts/ts-basic-components-formcomponent.md)一一绑定。卡片渲染服务运行卡片页面代码widgets.abc进行渲染,并将渲染后的数据发送至卡片使用方对应的[卡片组件](../reference/arkui-ts/ts-basic-components-formcomponent.md)
**图2** ArkTS卡片渲染服务运行原理  
![WidgetRender](figures/WidgetRender.png)
与JS卡片相比,ArkTS卡片支持在卡片中运行逻辑代码,为确保ArkTS卡片发生问题后不影响卡片使用方应用的使用,ArkTS卡片新增了卡片渲染服务用于运行卡片页面代码widgets.abc,卡片渲染服务由卡片管理服务管理。卡片使用方的每个卡片组件都对应了卡片渲染服务里的一个渲染实例,同一应用提供方的渲染实例运行在同一个虚拟机运行环境中,不同应用提供方的渲染实例运行在不同的虚拟机运行环境中,通过虚拟机运行环境隔离不同应用提供方卡片之间的资源与状态。开发过程中需要注意的是[globalThis](uiability-data-sync-with-ui.md#使用globalthis进行数据同步)对象的使用,相同应用提供方的卡片globalThis对象是同一个,不同应用提供方的卡片globalThis对象是不同的。
## ArkTS卡片的优势
卡片作为应用的一个快捷入口,ArkTS卡片相较于JS卡片具备如下几点优势:
- 统一开发范式,提升开发体验和开发效率。
OpenHarmony在2022年发布了声明式范式的UI开发框架,而卡片还延续了css/hml/json三段式类Web范式的开发方式,提高了开发者的学习成本,提供ArkTS卡片能力后,统一了卡片和页面的开发范式,页面的布局可以直接复用到卡片布局中,提升开发体验和开发效率。
**图3** 卡片工程结构对比  
![WidgetProject](figures/WidgetProject.png)
- 增强了卡片的能力,使卡片更加万能。
- 新增了动效的能力:ArkTS卡片开放了[属性动画](../reference/arkui-ts/ts-animatorproperty.md)[显式动画](../reference/arkui-ts/ts-explicit-animation.md)的能力,使卡片的交互更加友好。
- 新增了自定义绘制的能力:ArkTS卡片开放了[Canvas](../reference/arkui-ts/ts-components-canvas-canvas.md)画布组件,卡片可以使用自定义绘制的能力构建更多样的显示和交互效果。
- 允许卡片中运行逻辑代码:开放逻辑代码运行后很多业务逻辑可以在卡片内部自闭环,拓宽了卡片的业务适用场景。
## ArkTS卡片的约束
ArkTS卡片相较于JS卡片具备了更加丰富的能力,但也增加了使用卡片进行恶意行为的风险。由于ArkTS卡片显示在使用方应用中,使用方应用一般为桌面应用,为确保桌面的使用体验以及功耗相关考虑,对ArkTS卡片的能力做了以下约束:
- 不支持加载so。
- 不支持使用native语言开发。
- 仅支持声明式范式的[部分](arkts-ui-widget-page-overview.md)组件、事件、动效、数据管理、状态管理和API能力。
- 卡片的事件处理和使用方的事件处理是独立的,建议在使用方支持左右滑动的场景下卡片内容不要使用左右滑动功能的组件,以防手势冲突影响交互体验。
当前ArkTS卡片的能力会持续加强,如下能力会在后续版本支持:
- 不支持断点调试能力,会在后续版本支持。
- 不支持import,会在后续版本支持。
- 不支持极速预览,会在后续版本支持。
# 开发基于JS UI的卡片
以下内容介绍基于类Web范式的JS UI卡片开发指南。
## 运作机制
卡片框架的运作机制如图1所示。
**图1** 卡片框架运作机制(Stage模型)
![JSCardPrinciple](figures/JSCardPrinciple.png)
卡片使用方包含以下模块:
- 卡片使用:包含卡片的创建、删除、请求更新等操作。
- 通信适配层:由OpenHarmony SDK提供,负责与卡片管理服务通信,用于将卡片的相关操作到卡片管理服务。
卡片管理服务包含以下模块:
- 周期性刷新:在卡片添加后,根据卡片的刷新策略启动定时任务周期性触发卡片的刷新。
- 卡片缓存管理:在卡片添加到卡片管理服务后,对卡片的视图信息进行缓存,以便下次获取卡片时可以直接返回缓存数据,降低时延。
- 卡片生命周期管理:对于卡片切换到后台或者被遮挡时,暂停卡片的刷新;以及卡片的升级/卸载场景下对卡片数据的更新和清理。
- 卡片使用方对象管理:对卡片使用方的RPC对象进行管理,用于使用方请求进行校验以及对卡片更新后的回调处理。
- 通信适配层:负责与卡片使用方和提供方进行RPC通信。
卡片提供方包含以下模块:
- 卡片服务:由卡片提供方开发者实现,开发者实现生命周期处理创建卡片、更新卡片以及删除卡片等请求,提供相应的卡片服务。
- 卡片提供方实例管理模块:由卡片提供方开发者实现,负责对卡片管理服务分配的卡片实例进行持久化管理。
- 通信适配层:由OpenHarmony SDK提供,负责与卡片管理服务通信,用于将卡片的更新数据主动推送到卡片管理服务。
> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
> 实际开发时只需要作为卡片提供方进行卡片内容的开发,卡片使用方和卡片管理服务由系统自动处理。
## 接口说明
FormExtensionAbility类拥有如下API接口,具体的API介绍详见[接口文档](../reference/apis/js-apis-app-form-formExtensionAbility.md)
| 接口名 | 描述 |
| -------- | -------- |
| onAddForm(want:&nbsp;Want):&nbsp;formBindingData.FormBindingData | 卡片提供方接收创建卡片的通知接口。 |
| onCastToNormalForm(formId:&nbsp;string):&nbsp;void | 卡片提供方接收临时卡片转常态卡片的通知接口。 |
| onUpdateForm(formId:&nbsp;string):&nbsp;void | 卡片提供方接收更新卡片的通知接口。 |
| onChangeFormVisibility(newStatus:&nbsp;{&nbsp;[key:&nbsp;string]:&nbsp;number&nbsp;}):&nbsp;void | 卡片提供方接收修改可见性的通知接口。 |
| onFormEvent(formId:&nbsp;string,&nbsp;message:&nbsp;string):&nbsp;void | 卡片提供方接收处理卡片事件的通知接口。 |
| onRemoveForm(formId:&nbsp;string):&nbsp;void | 卡片提供方接收销毁卡片的通知接口。 |
| onConfigurationUpdate(config:&nbsp;Configuration):&nbsp;void | 当系统配置更新时调用。 |
| onShareForm?(formId:&nbsp;string):&nbsp;{&nbsp;[key:&nbsp;string]:&nbsp;any&nbsp;} | 卡片提供方接收卡片分享的通知接口。 |
FormExtensionAbility类还拥有成员context,为FormExtensionContext类,具体的API介绍详见[接口文档](../reference/apis/js-apis-inner-application-formExtensionContext.md)
| 接口名 | 描述 |
| -------- | -------- |
| startAbility(want:&nbsp;Want,&nbsp;callback:&nbsp;AsyncCallback&lt;void&gt;):&nbsp;void | 回调形式拉起一个卡片所属应用的UIAbility(系统接口,三方应用不支持调用,需申请后台拉起权限)。 |
| startAbility(want:&nbsp;Want):&nbsp;Promise&lt;void&gt; | Promise形式拉起一个卡片所属应用的UIAbility(系统接口,三方应用不支持调用,需申请后台拉起权限)。 |
formProvider类有如下API接口,具体的API介绍详见[接口文档](../reference/apis/js-apis-app-form-formProvider.md)
| 接口名 | 描述 |
| -------- | -------- |
| setFormNextRefreshTime(formId:&nbsp;string,&nbsp;minute:&nbsp;number,&nbsp;callback:&nbsp;AsyncCallback&lt;void&gt;):&nbsp;void; | 设置指定卡片的下一次更新时间。 |
| setFormNextRefreshTime(formId:&nbsp;string,&nbsp;minute:&nbsp;number):&nbsp;Promise&lt;void&gt;; | 设置指定卡片的下一次更新时间,以promise方式返回。 |
| updateForm(formId:&nbsp;string,&nbsp;formBindingData:&nbsp;FormBindingData,&nbsp;callback:&nbsp;AsyncCallback&lt;void&gt;):&nbsp;void; | 更新指定的卡片。 |
| updateForm(formId:&nbsp;string,&nbsp;formBindingData:&nbsp;FormBindingData):&nbsp;Promise&lt;void&gt;; | 更新指定的卡片,以promise方式返回。 |
formBindingData类有如下API接口,具体的API介绍详见[接口文档](../reference/apis/js-apis-app-form-formBindingData.md)
| 接口名 | 描述 |
| -------- | -------- |
| createFormBindingData(obj?:&nbsp;Object&nbsp;\|&nbsp;string):&nbsp;FormBindingData | 创建一个FormBindingData对象。 |
## 开发步骤
Stage卡片开发,即基于[Stage模型](stage-model-development-overview.md)的卡片提供方开发,主要涉及如下关键步骤:
- [创建卡片FormExtensionAbility](#创建卡片formextensionability):卡片生命周期回调函数FormExtensionAbility开发。
- [配置卡片配置文件](#配置卡片配置文件):配置应用配置文件module.json5和profile配置文件。
- [卡片数据交互](#卡片数据交互):对卡片信息进行持久化管理。
- [卡片数据交互](#卡片数据交互):通过updateForm更新卡片显示的信息。
- [开发卡片页面](#开发卡片页面):使用HML+CSS+JSON开发JS卡片页面。
- [开发卡片事件](#开发卡片事件):为卡片添加router事件和message事件。
### 创建卡片FormExtensionAbility
创建Stage模型的卡片,需实现FormExtensionAbility生命周期接口。先参考[DevEco Studio服务卡片开发指南](https://developer.harmonyos.com/cn/docs/documentation/doc-guides/ohos-development-service-widget-0000001263280425)生成服务卡片模板。
1. 在EntryFormAbility.ts中,导入相关模块。
```ts
import FormExtensionAbility from '@ohos.app.form.FormExtensionAbility';
import formBindingData from '@ohos.app.form.formBindingData';
import formInfo from '@ohos.app.form.formInfo';
import formProvider from '@ohos.app.form.formProvider';
import dataStorage from '@ohos.data.storage';
```
2. 在EntryFormAbility.ts中,实现FormExtension生命周期接口。
```ts
export default class EntryFormAbility extends FormExtensionAbility {
onAddForm(want) {
console.info('[EntryFormAbility] onAddForm');
// 使用方创建卡片时触发,提供方需要返回卡片数据绑定类
let obj = {
"title": "titleOnCreate",
"detail": "detailOnCreate"
};
let formData = formBindingData.createFormBindingData(obj);
return formData;
}
onCastToNormalForm(formId) {
// 使用方将临时卡片转换为常态卡片触发,提供方需要做相应的处理
console.info('[EntryFormAbility] onCastToNormalForm');
}
onUpdateForm(formId) {
// 若卡片支持定时更新/定点更新/卡片使用方主动请求更新功能,则提供方需要重写该方法以支持数据更新
console.info('[EntryFormAbility] onUpdateForm');
let obj = {
"title": "titleOnUpdate",
"detail": "detailOnUpdate"
};
let formData = formBindingData.createFormBindingData(obj);
formProvider.updateForm(formId, formData).catch((error) => {
console.info('[EntryFormAbility] updateForm, error:' + JSON.stringify(error));
});
}
onChangeFormVisibility(newStatus) {
// 使用方发起可见或者不可见通知触发,提供方需要做相应的处理,仅系统应用生效
console.info('[EntryFormAbility] onChangeFormVisibility');
}
onFormEvent(formId, message) {
// 若卡片支持触发事件,则需要重写该方法并实现对事件的触发
console.info('[EntryFormAbility] onFormEvent');
}
onRemoveForm(formId) {
// 删除卡片实例数据
console.info('[EntryFormAbility] onRemoveForm');
}
onConfigurationUpdate(config) {
console.info('[EntryFormAbility] nConfigurationUpdate, config:' + JSON.stringify(config));
}
onAcquireFormState(want) {
return formInfo.FormState.READY;
}
}
```
> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
> FormExtensionAbility不能常驻后台,即在卡片生命周期回调函数中无法处理长时间的任务。
### 配置卡片配置文件
1. 卡片需要在[module.json5配置文件](../quick-start/module-configuration-file.md)中的extensionAbilities标签下,配置ExtensionAbility相关信息。FormExtensionAbility需要填写metadata元信息标签,其中键名称为固定字符串"ohos.extension.form",资源为卡片的具体配置信息的索引。
配置示例如下:
```json
{
"module": {
...
"extensionAbilities": [
{
"name": "EntryFormAbility",
"srcEntrance": "./ets/entryformability/EntryFormAbility.ts",
"label": "$string:EntryFormAbility_label",
"description": "$string:EntryFormAbility_desc",
"type": "form",
"metadata": [
{
"name": "ohos.extension.form",
"resource": "$profile:form_config"
}
]
}
]
}
}
```
2. 卡片的具体配置信息。在上述FormExtensionAbility的元信息("metadata"配置项)中,可以指定卡片具体配置信息的资源索引。例如当resource指定为$profile:form_config时,会使用开发视图的resources/base/profile/目录下的form_config.json作为卡片profile配置文件。内部字段结构说明如下表所示。
**表1** 卡片profile配置文件
| 属性名称 | 含义 | 数据类型 | 是否可缺省 |
| -------- | -------- | -------- | -------- |
| name | 表示卡片的类名,字符串最大长度为127字节。 | 字符串 | 否 |
| description | 表示卡片的描述。取值可以是描述性内容,也可以是对描述性内容的资源索引,以支持多语言。字符串最大长度为255字节。 | 字符串 | 可缺省,缺省为空。 |
| src | 表示卡片对应的UI代码的完整路径。 | 字符串 | 否 |
| window | 用于定义与显示窗口相关的配置。 | 对象 | 可缺省 |
| isDefault | 表示该卡片是否为默认卡片,每个UIAbility有且只有一个默认卡片。<br/>-&nbsp;true:默认卡片。<br/>-&nbsp;false:非默认卡片。 | 布尔值 | 否 |
| colorMode | 表示卡片的主题样式,取值范围如下:<br/>-&nbsp;auto:自适应。<br/>-&nbsp;dark:深色主题。<br/>-&nbsp;light:浅色主题。 | 字符串 | 可缺省,缺省值为“auto”。 |
| supportDimensions | 表示卡片支持的外观规格,取值范围:<br/>-&nbsp;1&nbsp;\*&nbsp;2:表示1行2列的二宫格。<br/>-&nbsp;2&nbsp;\*&nbsp;2:表示2行2列的四宫格。<br/>-&nbsp;2&nbsp;\*&nbsp;4:表示2行4列的八宫格。<br/>-&nbsp;4&nbsp;\*&nbsp;4:表示4行4列的十六宫格。 | 字符串数组 | 否 |
| defaultDimension | 表示卡片的默认外观规格,取值必须在该卡片supportDimensions配置的列表中。 | 字符串 | 否 |
| updateEnabled | 表示卡片是否支持周期性刷新,取值范围:<br/>-&nbsp;true:表示支持周期性刷新,可以在定时刷新(updateDuration)和定点刷新(scheduledUpdateTime)两种方式任选其一,优先选择定时刷新。<br/>-&nbsp;false:表示不支持周期性刷新。 | 布尔类型 | 否 |
| scheduledUpdateTime | 表示卡片的定点刷新的时刻,采用24小时制,精确到分钟。<br/>updateDuration参数优先级高于scheduledUpdateTime,两者同时配置时,以updateDuration配置的刷新时间为准。 | 字符串 | 可缺省,缺省值为“0:0”。 |
| updateDuration | 表示卡片定时刷新的更新周期,单位为30分钟,取值为自然数。<br/>当取值为0时,表示该参数不生效。<br/>当取值为正整数N时,表示刷新周期为30\*N分钟。<br/>updateDuration参数优先级高于scheduledUpdateTime,两者同时配置时,以updateDuration配置的刷新时间为准。 | 数值 | 可缺省,缺省值为“0”。 |
| formConfigAbility | 表示卡片的配置跳转链接,采用URI格式。 | 字符串 | 可缺省,缺省值为空。 |
| formVisibleNotify | 标识是否允许卡片使用卡片可见性通知。 | 字符串 | 可缺省,缺省值为空。 |
| metaData | 表示卡片的自定义信息,包含customizeData数组标签。 | 对象 | 可缺省,缺省值为空。 |
配置示例如下:
```json
{
"forms": [
{
"name": "widget",
"description": "This is a service widget.",
"src": "./js/widget/pages/index/index",
"window": {
"designWidth": 720,
"autoDesignWidth": true
},
"colorMode": "auto",
"isDefault": true,
"updateEnabled": true,
"scheduledUpdateTime": "10:30",
"updateDuration": 1,
"defaultDimension": "2*2",
"supportDimensions": [
"2*2"
]
}
]
}
```
### 卡片信息的持久化
因大部分卡片提供方都不是常驻服务,只有在需要使用时才会被拉起获取卡片信息,且卡片管理服务支持对卡片进行多实例管理,卡片ID对应实例ID,因此若卡片提供方支持对卡片数据进行配置,则需要对卡片的业务数据按照卡片ID进行持久化管理,以便在后续获取、更新以及拉起时能获取到正确的卡片业务数据。
```ts
const DATA_STORAGE_PATH = "/data/storage/el2/base/haps/form_store";
async function storeFormInfo(formId: string, formName: string, tempFlag: boolean) {
// 此处仅对卡片ID:formId,卡片名:formName和是否为临时卡片:tempFlag进行了持久化
let formInfo = {
"formName": formName,
"tempFlag": tempFlag,
"updateCount": 0
};
try {
const storage = await dataStorage.getStorage(DATA_STORAGE_PATH);
// put form info
await storage.put(formId, JSON.stringify(formInfo));
console.info(`[EntryFormAbility] storeFormInfo, put form info successfully, formId: ${formId}`);
await storage.flush();
} catch (err) {
console.error(`[EntryFormAbility] failed to storeFormInfo, err: ${JSON.stringify(err)}`);
}
}
export default class EntryFormAbility extends FormExtension {
...
onAddForm(want) {
console.info('[EntryFormAbility] onAddForm');
let formId = want.parameters["ohos.extra.param.key.form_identity"];
let formName = want.parameters["ohos.extra.param.key.form_name"];
let tempFlag = want.parameters["ohos.extra.param.key.form_temporary"];
// 将创建的卡片信息持久化,以便在下次获取/更新该卡片实例时进行使用
// 此接口请根据实际情况实现,具体请参考:FormExtAbility Stage模型卡片实例
storeFormInfo(formId, formName, tempFlag);
let obj = {
"title": "titleOnCreate",
"detail": "detailOnCreate"
};
let formData = formBindingData.createFormBindingData(obj);
return formData;
}
}
```
且需要适配onRemoveForm卡片删除通知接口,在其中实现卡片实例数据的删除。
```ts
const DATA_STORAGE_PATH = "/data/storage/el2/base/haps/form_store";
async function deleteFormInfo(formId: string) {
try {
const storage = await dataStorage.getStorage(DATA_STORAGE_PATH);
// del form info
await storage.delete(formId);
console.info(`[EntryFormAbility] deleteFormInfo, del form info successfully, formId: ${formId}`);
await storage.flush();
} catch (err) {
console.error(`[EntryFormAbility] failed to deleteFormInfo, err: ${JSON.stringify(err)}`);
}
}
...
export default class EntryFormAbility extends FormExtension {
...
onRemoveForm(formId) {
console.info('[EntryFormAbility] onRemoveForm');
// 删除之前持久化的卡片实例数据
// 此接口请根据实际情况实现,具体请参考:FormExtAbility Stage模型卡片实例
deleteFormInfo(formId);
}
}
```
具体的持久化方法可以参考[轻量级数据存储开发指导](../database/database-preference-guidelines.md)
需要注意的是,卡片使用方在请求卡片时传递给提供方应用的Want数据中存在临时标记字段,表示此次请求的卡片是否为临时卡片:
- 常态卡片:卡片使用方会持久化的卡片;
- 临时卡片:卡片使用方不会持久化的卡片;
由于临时卡片的数据具有非持久化的特殊性,某些场景例如卡片服务框架死亡重启,此时临时卡片数据在卡片管理服务中已经删除,且对应的卡片ID不会通知到提供方,所以卡片提供方需要自己负责清理长时间未删除的临时卡片数据。同时对应的卡片使用方可能会将之前请求的临时卡片转换为常态卡片。如果转换成功,卡片提供方也需要对对应的临时卡片ID进行处理,把卡片提供方记录的临时卡片数据转换为常态卡片数据,防止提供方在清理长时间未删除的临时卡片时,把已经转换为常态卡片的临时卡片信息删除,导致卡片信息丢失。
### 卡片数据交互
当卡片应用需要更新数据时(如触发了定时更新或定点更新),卡片应用获取最新数据,并调用updateForm()接口主动触发卡片的更新。
```ts
onUpdateForm(formId) {
// 若卡片支持定时更新/定点更新/卡片使用方主动请求更新功能,则提供方需要重写该方法以支持数据更新
console.info('[EntryFormAbility] onUpdateForm');
let obj = {
"title": "titleOnUpdate",
"detail": "detailOnUpdate"
};
let formData = formBindingData.createFormBindingData(obj);
// 调用updateForm接口去更新对应的卡片,仅更新入参中携带的数据信息,其他信息保持不变
formProvider.updateForm(formId, formData).catch((error) => {
console.info('[EntryFormAbility] updateForm, error:' + JSON.stringify(error));
});
}
```
### 开发卡片页面
开发者可以使用类Web范式(HML+CSS+JSON)开发JS卡片页面。生成如下卡片页面,可以这样配置卡片页面文件:
![WidgetCardPage](figures/WidgetCardPage.png)
- HML:使用类Web范式的组件描述卡片的页面信息。
```html
<div class="container">
<stack>
<div class="container-img">
<image src="/common/widget.png" class="bg-img"></image>
</div>
<div class="container-inner">
<text class="title">{{title}}</text>
<text class="detail_text" onclick="routerEvent">{{detail}}</text>
</div>
</stack>
</div>
```
- CSS:HML中类Web范式组件的样式信息。
```css
.container {
flex-direction: column;
justify-content: center;
align-items: center;
}
.bg-img {
flex-shrink: 0;
height: 100%;
}
.container-inner {
flex-direction: column;
justify-content: flex-end;
align-items: flex-start;
height: 100%;
width: 100%;
padding: 12px;
}
.title {
font-size: 19px;
font-weight: bold;
color: white;
text-overflow: ellipsis;
max-lines: 1;
}
.detail_text {
font-size: 16px;
color: white;
opacity: 0.66;
text-overflow: ellipsis;
max-lines: 1;
margin-top: 6px;
}
```
- JSON:卡片页面中的数据和事件交互。
```json
{
"data": {
"title": "TitleDefault",
"detail": "TextDefault"
},
"actions": {
"routerEvent": {
"action": "router",
"abilityName": "EntryAbility",
"params": {
"message": "add detail"
}
}
}
}
```
### 开发卡片事件
卡片支持为组件设置交互事件(action),包括**router**事件和**message**事件,其中router事件用于UIAbility跳转,message事件用于卡片开发人员自定义点击事件。
关键步骤说明如下:
1. 在HML中为组件设置onclick属性,其值对应到JSON文件的actions字段中。
2. 设置router事件:
- action属性值为"router"。
- abilityName为跳转目标的UIAbility名(支持跳转FA模型的PageAbility组件和Stage模型的UIAbility组件),如目前DevEco Studio创建的Stage模型的UIAbility默认名为EntryAbility。
- params为传递给跳转目标UIAbility的自定义参数,可以按需填写。其值可以在目标UIAbility启动时的want中的parameters里获取。如Stage模型MainAbility的onCreate生命周期里的入参want的parameters字段下获取到配置的参数。
3. 设置message事件:
- action属性值为"message"。
- params为message事件的用户自定义参数,可以按需填写。其值可以在卡片生命周期函数onFormEvent()中的message里获取。
示例如下。
- HML文件
```html
<div class="container">
<stack>
<div class="container-img">
<image src="/common/widget.png" class="bg-img"></image>
</div>
<div class="container-inner">
<text class="title" onclick="routerEvent">{{title}}</text>
<text class="detail_text" onclick="messageEvent">{{detail}}</text>
</div>
</stack>
</div>
```
- CSS文件
```css
.container {
flex-direction: column;
justify-content: center;
align-items: center;
}
.bg-img {
flex-shrink: 0;
height: 100%;
}
.container-inner {
flex-direction: column;
justify-content: flex-end;
align-items: flex-start;
height: 100%;
width: 100%;
padding: 12px;
}
.title {
font-size: 19px;
font-weight: bold;
color: white;
text-overflow: ellipsis;
max-lines: 1;
}
.detail_text {
font-size: 16px;
color: white;
opacity: 0.66;
text-overflow: ellipsis;
max-lines: 1;
margin-top: 6px;
}
```
- JSON文件
```json
{
"data": {
"title": "TitleDefault",
"detail": "TextDefault"
},
"actions": {
"routerEvent": {
"action": "router",
"abilityName": "EntryAbility",
"params": {
"info": "router info",
"message": "router message"
}
},
"messageEvent": {
"action": "message",
"params": {
"detail": "message detail"
}
}
}
}
```
- 在UIAbility中接收router事件并获取参数
```ts
import UIAbility from '@ohos.app.ability.UIAbility'
export default class EntryAbility extends UIAbility {
onCreate(want, launchParam) {
let params = JSON.parse(want.parameters.params);
// 获取router事件中传递的info参数
if (params.info === "router info") {
// do something
// console.info("router info:" + params.info)
}
// 获取router事件中传递的message参数
if (params.message === "router message") {
// do something
// console.info("router message:" + params.message)
}
}
...
};
```
- 在FormExtensionAbility中接收message事件并获取参数
```ts
import FormExtension from '@ohos.app.form.FormExtensionAbility';
export default class FormAbility extends FormExtension {
...
onFormEvent(formId, message) {
// 获取message事件中传递的detail参数
let msg = JSON.parse(message)
if (msg.detail === "message detail") {
// do something
// console.info("message info:" + msg.detail)
}
}
...
};
```
# Stage模型服务卡片相关实例
针对Stage模型卡片提供方的开发,有以下相关实例可供参考:
- [基于Stage模型的JS卡片(API9)](https://gitee.com/openharmony/applications_app_samples/tree/master/code/SuperFeature/Widget/FormExtAbility)
- [基于Stage模型的JS卡片(成语接龙小游戏)(API9)](https://gitee.com/openharmony/applications_app_samples/tree/master/ability/FormGame)
- [基于Stage模型的ArkTS卡片(Canvas绘制实现的五子棋游戏卡片)(API9)](https://gitee.com/openharmony/applications_app_samples/tree/master/ability/ArkTSCard/CanvasGame)
- [基于Stage模型的ArkTS卡片(逻辑代码执行实现的计算器卡片)(API9)](https://gitee.com/openharmony/applications_app_samples/tree/master/ability/ArkTSFormCalc)
# 服务卡片概述
服务卡片(以下简称“卡片”)是一种界面展示形式,可以将应用的重要信息或操作前置到卡片,以达到服务直达、减少体验层级的目的。卡片常用于嵌入到其他应用(当前卡片使用方只支持系统应用,如桌面)中作为其界面显示的一部分,并支持拉起页面、发送消息等基础的交互功能。
## 服务卡片架构
**图1** 服务卡片架构  
![WidgetArchitecture](figures/WidgetArchitecture.png)
卡片的基本概念:
- 卡片使用方:如上图中的桌面,显示卡片内容的宿主应用,控制卡片在宿主中展示的位置。
- 应用图标:应用入口图标,点击后可拉起应用进程,图标内容不支持交互。
- 卡片:具备不同规格大小的界面展示,卡片的内容可以进行交互,如实现按钮进行[界面的刷新](arkts-ui-widget-event-formextensionability.md)[应用的跳转](arkts-ui-widget-event-router.md)等。
- 卡片提供方:包含卡片的应用,提供卡片的显示内容、控件布局以及控件点击处理逻辑。
- FormExtensionAbility:卡片业务逻辑模块,提供卡片创建、销毁、刷新等生命周期回调。
- 卡片页面:卡片UI模块,包含页面控件、布局、事件等显示和交互信息。
卡片的常见使用步骤如下。
**图2** 卡片常见使用步骤  
![WidgetUse](figures/WidgetUse.png)
1. 长按“桌面图标”,弹出操作菜单。
2. 点击“服务卡片”选项,进入卡片预览界面。
3. 点击“添加到桌面”按钮,即可在桌面上看到新添加的卡片。
## 服务卡片UI页面开发方式
在Stage模型下,服务卡片的UI页面支持通过[ArkTS](arkts-ui-widget-working-principles.md)[JS](js-ui-widget-development.md)两种语言进行开发:
- 基于声明式范式ArkTS UI开发的卡片,简称ArkTS卡片。
- 基于类Web范式JS UI开发的卡片,简称JS卡片。
ArkTS卡片与JS卡片具备不同的实现原理及特征,在场景能力上的差异如下表所示。
| 类别 | JS卡片 | ArkTS卡片 |
| -------- | -------- | -------- |
| 开发范式 | 类Web范式 | 声明式范式 |
| 组件能力 | 支持 | 支持 |
| 布局能力 | 支持 | 支持 |
| 事件能力 | 支持 | 支持 |
| 自定义动效 | 不支持 | 支持 |
| 自定义绘制 | 不支持 | 支持 |
| 逻辑代码执行(不包含import能力) | 不支持 | 支持 |
相比于JS卡片,ArkTS卡片在能力和场景方面更加丰富,因此无论开发何种用途的卡片,都推荐使用ArkTS卡片,因为它可以提高开发效率并实现动态化。但如果只需要做静态页面展示的卡片,可以考虑使用JS卡片。
## 限制
为了降低FormExtensionAbility能力被三方应用滥用的风险,在FormExtensionAbility中限制以下接口的调用
- @ohos.ability.particleAbility.d.ts
- @ohos.backgroundTaskManager.d.ts
- @ohos.resourceschedule.backgroundTaskManager.d.ts
- @ohos.multimedia.camera.d.ts
- @ohos.multimedia.audio.d.ts
- @ohos.multimedia.media.d.ts
\ No newline at end of file
...@@ -361,7 +361,7 @@ onUpdate(formId) { ...@@ -361,7 +361,7 @@ onUpdate(formId) {
![widget-development-fa](figures/widget-development-fa.png) ![widget-development-fa](figures/widget-development-fa.png)
> **说明:** > **说明:**
> 当前仅支持JS扩展的类Web开发范式来实现卡片的UI界面。 > FA模型当前仅支持JS扩展的类Web开发范式来实现卡片的UI界面。
- HML:使用类Web范式的组件描述卡片的页面信息。 - HML:使用类Web范式的组件描述卡片的页面信息。
......
# FormExtensionAbility(服务卡片) # 服务卡片开发指导(Stage模型)
## 卡片概述
[服务卡片概述](service-widget-overview.md)
FormExtensionAbility就是服务卡片扩展组件(以下简称“卡片”),是一种界面展示形式,可以将应用的重要信息或操作前置到卡片,以达到服务直达,减少体验层级的目的。
开发基于ArkTS UI的卡片
卡片常用于嵌入到其他应用(当前只支持系统应用)中作为其界面的一部分显示,并支持拉起页面、发送消息等基础的交互功能。
- [ArkTS卡片运行机制](arkts-ui-widget-working-principles.md)
卡片的基本概念: - [ArkTS卡片相关模块](arkts-ui-widget-modules.md)
- ArkTS卡片开发指导
- 卡片使用方:显示卡片内容的宿主应用,控制卡片在宿主中展示的位置。 - [创建一个ArkTS卡片](arkts-ui-widget-creation.md)
- [配置卡片的配置文件](arkts-ui-widget-configuration.md)
- 卡片管理服务:用于管理系统中所添加卡片的常驻代理服务,包括卡片对象的管理与使用,以及卡片周期性刷新等。 - [卡片生命周期管理](arkts-ui-widget-lifecycle.md)
- 开发卡片页面
- 卡片提供方:提供卡片显示内容原子化服务,控制卡片的显示内容、控件布局以及控件点击事件。 - [卡片页面能力说明](arkts-ui-widget-page-overview.md)
- [卡片使用动效能力](arkts-ui-widget-page-animation.md)
- [卡片使用自定义绘制能力](arkts-ui-widget-page-custom-drawing.md)
## 运作机制 - 开发卡片事件
- [卡片事件能力说明](arkts-ui-widget-event-overview.md)
卡片框架的运作机制如图1所示。 - [通过FormExtensionAbility刷新卡片内容](arkts-ui-widget-event-formextensionability.md)
- [通过UIAbility刷新卡片内容](arkts-ui-widget-event-uiability.md)
**图1** 卡片框架运作机制(Stage模型)   - [使用router事件跳转到指定页面](arkts-ui-widget-event-router.md)
![form-extension](figures/form-extension.png) - 卡片数据交互
- [卡片数据交互说明](arkts-ui-widget-interaction-overview.md)
卡片使用方包含以下模块: - [定时刷新和定点刷新](arkts-ui-widget-update-by-time.md)
- [刷新本地图片和网络图片](arkts-ui-widget-image-update.md)
- 卡片使用:包含卡片的创建、删除、请求更新等操作。 - [根据卡片状态刷新不同内容](arkts-ui-widget-update-by-status.md)
- [使用方刷新卡片内容(仅对系统应用开放)](arkts-ui-widget-content-update.md)
- 通信适配层:由OpenHarmony SDK提供,负责与卡片管理服务通信,用于将卡片的相关操作到卡片管理服务。
[开发基于JS UI的卡片](js-ui-widget-development.md)
卡片管理服务包含以下模块:
[Stage模型服务卡片相关实例](service-widget-development-samples.md)
- 周期性刷新:在卡片添加后,根据卡片的刷新策略启动定时任务周期性触发卡片的刷新。 \ No newline at end of file
- 卡片缓存管理:在卡片添加到卡片管理服务后,对卡片的视图信息进行缓存,以便下次获取卡片时可以直接返回缓存数据,降低时延。
- 卡片生命周期管理:对于卡片切换到后台或者被遮挡时,暂停卡片的刷新;以及卡片的升级/卸载场景下对卡片数据的更新和清理。
- 卡片使用方对象管理:对卡片使用方的RPC对象进行管理,用于使用方请求进行校验以及对卡片更新后的回调处理。
- 通信适配层:负责与卡片使用方和提供方进行RPC通信。
卡片提供方包含以下模块:
- 卡片服务:由卡片提供方开发者实现,开发者实现生命周期处理创建卡片、更新卡片以及删除卡片等请求,提供相应的卡片服务。
- 卡片提供方实例管理模块:由卡片提供方开发者实现,负责对卡片管理服务分配的卡片实例进行持久化管理。
- 通信适配层:由OpenHarmony SDK提供,负责与卡片管理服务通信,用于将卡片的更新数据主动推送到卡片管理服务。
> **说明:**
> 实际开发时只需要作为卡片提供方进行卡片内容的开发,卡片使用方和卡片管理服务由系统自动处理。
## 接口说明
FormExtensionAbility类拥有如下API接口,具体的API介绍详见[接口文档](../reference/apis/js-apis-app-form-formExtensionAbility.md)
| 接口名 | 描述 |
| -------- | -------- |
| onAddForm(want:&nbsp;Want):&nbsp;formBindingData.FormBindingData | 卡片提供方接收创建卡片的通知接口。 |
| onCastToNormalForm(formId:&nbsp;string):&nbsp;void | 卡片提供方接收临时卡片转常态卡片的通知接口。 |
| onUpdateForm(formId:&nbsp;string):&nbsp;void | 卡片提供方接收更新卡片的通知接口。 |
| onChangeFormVisibility(newStatus:&nbsp;{&nbsp;[key:&nbsp;string]:&nbsp;number&nbsp;}):&nbsp;void | 卡片提供方接收修改可见性的通知接口。 |
| onFormEvent(formId:&nbsp;string,&nbsp;message:&nbsp;string):&nbsp;void | 卡片提供方接收处理卡片事件的通知接口。 |
| onRemoveForm(formId:&nbsp;string):&nbsp;void | 卡片提供方接收销毁卡片的通知接口。 |
| onConfigurationUpdate(config:&nbsp;Configuration):&nbsp;void | 当系统配置更新时调用。 |
| onShareForm?(formId:&nbsp;string):&nbsp;{&nbsp;[key:&nbsp;string]:&nbsp;any&nbsp;} | 卡片提供方接收卡片分享的通知接口。 |
FormExtensionAbility类还拥有成员context,为FormExtensionContext类,具体的API介绍详见[接口文档](../reference/apis/js-apis-inner-application-formExtensionContext.md)
| 接口名 | 描述 |
| -------- | -------- |
| startAbility(want:&nbsp;Want,&nbsp;callback:&nbsp;AsyncCallback&lt;void&gt;):&nbsp;void | 回调形式拉起一个卡片所属应用的UIAbility(系统接口,三方应用不支持调用,需申请后台拉起权限)。 |
| startAbility(want:&nbsp;Want):&nbsp;Promise&lt;void&gt; | Promise形式拉起一个卡片所属应用的UIAbility(系统接口,三方应用不支持调用,需申请后台拉起权限)。 |
formProvider类有如下API接口,具体的API介绍详见[接口文档](../reference/apis/js-apis-app-form-formProvider.md)
| 接口名 | 描述 |
| -------- | -------- |
| setFormNextRefreshTime(formId:&nbsp;string,&nbsp;minute:&nbsp;number,&nbsp;callback:&nbsp;AsyncCallback&lt;void&gt;):&nbsp;void; | 设置指定卡片的下一次更新时间。 |
| setFormNextRefreshTime(formId:&nbsp;string,&nbsp;minute:&nbsp;number):&nbsp;Promise&lt;void&gt;; | 设置指定卡片的下一次更新时间,以promise方式返回。 |
| updateForm(formId:&nbsp;string,&nbsp;formBindingData:&nbsp;FormBindingData,&nbsp;callback:&nbsp;AsyncCallback&lt;void&gt;):&nbsp;void; | 更新指定的卡片。 |
| updateForm(formId:&nbsp;string,&nbsp;formBindingData:&nbsp;FormBindingData):&nbsp;Promise&lt;void&gt;; | 更新指定的卡片,以promise方式返回。 |
formBindingData类有如下API接口,具体的API介绍详见[接口文档](../reference/apis/js-apis-app-form-formBindingData.md)
| 接口名 | 描述 |
| -------- | -------- |
| createFormBindingData(obj?:&nbsp;Object&nbsp;\|&nbsp;string):&nbsp;FormBindingData | 创建一个FormBindingData对象。 |
## 开发步骤
Stage卡片开发,即基于[Stage模型](stage-model-development-overview.md)的卡片提供方开发,主要涉及如下关键步骤:
- [创建卡片FormExtensionAbility](#创建卡片formextensionability):卡片生命周期回调函数FormExtensionAbility开发。
- [配置卡片配置文件](#配置卡片配置文件):配置应用配置文件module.json5和profile配置文件。
- [卡片数据交互](#卡片数据交互):对卡片信息进行持久化管理。
- [卡片数据交互](#卡片数据交互):通过updateForm更新卡片显示的信息。
- [开发卡片页面](#开发卡片页面):使用HML+CSS+JSON开发JS卡片页面。
- [开发卡片事件](#开发卡片事件):为卡片添加router事件和message事件。
### 创建卡片FormExtensionAbility
创建Stage模型的卡片,需实现FormExtensionAbility生命周期接口。先参考[DevEco Studio服务卡片开发指南](https://developer.harmonyos.com/cn/docs/documentation/doc-guides/ohos-development-service-widget-0000001263280425)生成服务卡片模板。
1. 在EntryFormAbility.ts中,导入相关模块。
```ts
import FormExtension from '@ohos.app.form.FormExtensionAbility';
import formBindingData from '@ohos.app.form.formBindingData';
import formInfo from '@ohos.app.form.formInfo';
import formProvider from '@ohos.app.form.formProvider';
import dataStorage from '@ohos.data.storage';
```
2. 在EntryFormAbility.ts中,实现FormExtension生命周期接口。
```ts
export default class EntryFormAbility extends FormExtension {
onAddForm(want) {
console.info('[EntryFormAbility] onAddForm');
// 使用方创建卡片时触发,提供方需要返回卡片数据绑定类
let obj = {
"title": "titleOnCreate",
"detail": "detailOnCreate"
};
let formData = formBindingData.createFormBindingData(obj);
return formData;
}
onCastToNormalForm(formId) {
// 使用方将临时卡片转换为常态卡片触发,提供方需要做相应的处理
console.info('[EntryFormAbility] onCastToNormalForm');
}
onUpdateForm(formId) {
// 若卡片支持定时更新/定点更新/卡片使用方主动请求更新功能,则提供方需要重写该方法以支持数据更新
console.info('[EntryFormAbility] onUpdateForm');
let obj = {
"title": "titleOnUpdate",
"detail": "detailOnUpdate"
};
let formData = formBindingData.createFormBindingData(obj);
formProvider.updateForm(formId, formData).catch((error) => {
console.info('[EntryFormAbility] updateForm, error:' + JSON.stringify(error));
});
}
onChangeFormVisibility(newStatus) {
// 使用方发起可见或者不可见通知触发,提供方需要做相应的处理,仅系统应用生效
console.info('[EntryFormAbility] onChangeFormVisibility');
}
onFormEvent(formId, message) {
// 若卡片支持触发事件,则需要重写该方法并实现对事件的触发
console.info('[EntryFormAbility] onFormEvent');
}
onRemoveForm(formId) {
// 删除卡片实例数据
console.info('[EntryFormAbility] onRemoveForm');
}
onConfigurationUpdate(config) {
console.info('[EntryFormAbility] nConfigurationUpdate, config:' + JSON.stringify(config));
}
onAcquireFormState(want) {
return formInfo.FormState.READY;
}
}
```
> **说明:**
> FormExtensionAbility不能常驻后台,即在卡片生命周期回调函数中无法处理长时间的任务。
### 配置卡片配置文件
1. 卡片需要在[module.json5配置文件](../quick-start/module-configuration-file.md)中的extensionAbilities标签下,配置ExtensionAbility相关信息。FormExtensionAbility需要填写metadata元信息标签,其中键名称为固定字符串"ohos.extension.form",资源为卡片的具体配置信息的索引。
配置示例如下:
```json
{
"module": {
// ...
"extensionAbilities": [
{
"name": "EntryFormAbility",
"srcEntrance": "./ets/entryformability/EntryFormAbility.ts",
"label": "$string:EntryFormAbility_label",
"description": "$string:EntryFormAbility_desc",
"type": "form",
"metadata": [
{
"name": "ohos.extension.form",
"resource": "$profile:form_config"
}
]
}
]
}
}
```
2. 卡片的具体配置信息。在上述FormExtensionAbility的元信息("metadata"配置项)中,可以指定卡片具体配置信息的资源索引。例如当resource指定为$profile:form_config时,会使用开发视图的resources/base/profile/目录下的form_config.json作为卡片profile配置文件。内部字段结构说明如下表所示。
**表1** 卡片profile配置文件
| 属性名称 | 含义 | 数据类型 | 是否可缺省 |
| -------- | -------- | -------- | -------- |
| name | 表示卡片的类名,字符串最大长度为127字节。 | 字符串 | 否 |
| description | 表示卡片的描述。取值可以是描述性内容,也可以是对描述性内容的资源索引,以支持多语言。字符串最大长度为255字节。 | 字符串 | 可缺省,缺省为空。 |
| src | 表示卡片对应的UI代码的完整路径。 | 字符串 | 否 |
| window | 用于定义与显示窗口相关的配置。 | 对象 | 可缺省 |
| isDefault | 表示该卡片是否为默认卡片,每个Ability有且只有一个默认卡片。<br/>true:默认卡片。<br/>false:非默认卡片。 | 布尔值 | 否 |
| colorMode | 表示卡片的主题样式,取值范围如下:<br/>auto:自适应。<br/>dark:深色主题。<br/>light:浅色主题。 | 字符串 | 可缺省,缺省值为“auto”。 |
| supportDimensions | 表示卡片支持的外观规格,取值范围:<br/>1&nbsp;\*&nbsp;2:表示1行2列的二宫格。<br/>2&nbsp;\*&nbsp;2:表示2行2列的四宫格。<br/>2&nbsp;\*&nbsp;4:表示2行4列的八宫格。<br/>4&nbsp;\*&nbsp;4:表示4行4列的十六宫格。 | 字符串数组 | 否 |
| defaultDimension | 表示卡片的默认外观规格,取值必须在该卡片supportDimensions配置的列表中。 | 字符串 | 否 |
| updateEnabled | 表示卡片是否支持周期性刷新,取值范围:<br/>true:表示支持周期性刷新,可以在定时刷新(updateDuration)和定点刷新(scheduledUpdateTime)两种方式任选其一,优先选择定时刷新。<br/>false:表示不支持周期性刷新。 | 布尔类型 | 否 |
| scheduledUpdateTime | 表示卡片的定点刷新的时刻,采用24小时制,精确到分钟。<br/>updateDuration参数优先级高于scheduledUpdateTime,两者同时配置时,以updateDuration配置的刷新时间为准。 | 字符串 | 可缺省,缺省值为“0:0”。 |
| updateDuration | 表示卡片定时刷新的更新周期,单位为30分钟,取值为自然数。<br/>当取值为0时,表示该参数不生效。<br/>当取值为正整数N时,表示刷新周期为30\*N分钟。<br/>updateDuration参数优先级高于scheduledUpdateTime,两者同时配置时,以updateDuration配置的刷新时间为准。 | 数值 | 可缺省,缺省值为“0”。 |
| formConfigAbility | 表示卡片的配置跳转链接,采用URI格式。 | 字符串 | 可缺省,缺省值为空。 |
| formVisibleNotify | 标识是否允许卡片使用卡片可见性通知。 | 字符串 | 可缺省,缺省值为空。 |
| metaData | 表示卡片的自定义信息,包含customizeData数组标签。 | 对象 | 可缺省,缺省值为空。 |
配置示例如下:
```json
{
"forms": [
{
"name": "widget",
"description": "This is a service widget.",
"src": "./js/widget/pages/index/index",
"window": {
"designWidth": 720,
"autoDesignWidth": true
},
"colorMode": "auto",
"isDefault": true,
"updateEnabled": true,
"scheduledUpdateTime": "10:30",
"updateDuration": 1,
"defaultDimension": "2*2",
"supportDimensions": [
"2*2"
]
}
]
}
```
### 卡片信息的持久化
因大部分卡片提供方都不是常驻服务,只有在需要使用时才会被拉起获取卡片信息,且卡片管理服务支持对卡片进行多实例管理,卡片ID对应实例ID,因此若卡片提供方支持对卡片数据进行配置,则需要对卡片的业务数据按照卡片ID进行持久化管理,以便在后续获取、更新以及拉起时能获取到正确的卡片业务数据。
```ts
const DATA_STORAGE_PATH = "/data/storage/el2/base/haps/form_store";
async function storeFormInfo(formId: string, formName: string, tempFlag: boolean) {
// 此处仅对卡片ID:formId,卡片名:formName和是否为临时卡片:tempFlag进行了持久化
let formInfo = {
"formName": formName,
"tempFlag": tempFlag,
"updateCount": 0
};
try {
const storage = await dataStorage.getStorage(DATA_STORAGE_PATH);
// put form info
await storage.put(formId, JSON.stringify(formInfo));
console.info(`[EntryFormAbility] storeFormInfo, put form info successfully, formId: ${formId}`);
await storage.flush();
} catch (err) {
console.error(`[EntryFormAbility] failed to storeFormInfo, err: ${JSON.stringify(err)}`);
}
}
export default class EntryFormAbility extends FormExtension {
// ...
onAddForm(want) {
console.info('[EntryFormAbility] onAddForm');
let formId = want.parameters["ohos.extra.param.key.form_identity"];
let formName = want.parameters["ohos.extra.param.key.form_name"];
let tempFlag = want.parameters["ohos.extra.param.key.form_temporary"];
// 将创建的卡片信息持久化,以便在下次获取/更新该卡片实例时进行使用
// 此接口请根据实际情况实现,具体请参考:FormExtAbility Stage模型卡片实例
storeFormInfo(formId, formName, tempFlag);
let obj = {
"title": "titleOnCreate",
"detail": "detailOnCreate"
};
let formData = formBindingData.createFormBindingData(obj);
return formData;
}
}
```
且需要适配onRemoveForm卡片删除通知接口,在其中实现卡片实例数据的删除。
```ts
const DATA_STORAGE_PATH = "/data/storage/el2/base/haps/form_store";
async function deleteFormInfo(formId: string) {
try {
const storage = await dataStorage.getStorage(DATA_STORAGE_PATH);
// del form info
await storage.delete(formId);
console.info(`[EntryFormAbility] deleteFormInfo, del form info successfully, formId: ${formId}`);
await storage.flush();
} catch (err) {
console.error(`[EntryFormAbility] failed to deleteFormInfo, err: ${JSON.stringify(err)}`);
}
}
// ...
export default class EntryFormAbility extends FormExtension {
// ...
onRemoveForm(formId) {
console.info('[EntryFormAbility] onRemoveForm');
// 删除之前持久化的卡片实例数据
// 此接口请根据实际情况实现,具体请参考:FormExtAbility Stage模型卡片实例
deleteFormInfo(formId);
}
}
```
具体的持久化方法可以参考[轻量级数据存储开发指导](../database/database-preference-guidelines.md)
需要注意的是,卡片使用方在请求卡片时传递给提供方应用的Want数据中存在临时标记字段,表示此次请求的卡片是否为临时卡片:
- 常态卡片:卡片使用方会持久化的卡片;
- 临时卡片:卡片使用方不会持久化的卡片;
由于临时卡片的数据具有非持久化的特殊性,某些场景例如卡片服务框架死亡重启,此时临时卡片数据在卡片管理服务中已经删除,且对应的卡片ID不会通知到提供方,所以卡片提供方需要自己负责清理长时间未删除的临时卡片数据。同时对应的卡片使用方可能会将之前请求的临时卡片转换为常态卡片。如果转换成功,卡片提供方也需要对对应的临时卡片ID进行处理,把卡片提供方记录的临时卡片数据转换为常态卡片数据,防止提供方在清理长时间未删除的临时卡片时,把已经转换为常态卡片的临时卡片信息删除,导致卡片信息丢失。
### 卡片数据交互
当卡片应用需要更新数据时(如触发了定时更新或定点更新),卡片应用获取最新数据,并调用updateForm()接口主动触发卡片的更新。
```ts
onUpdateForm(formId) {
// 若卡片支持定时更新/定点更新/卡片使用方主动请求更新功能,则提供方需要重写该方法以支持数据更新
console.info('[EntryFormAbility] onUpdateForm');
let obj = {
"title": "titleOnUpdate",
"detail": "detailOnUpdate"
};
let formData = formBindingData.createFormBindingData(obj);
// 调用updateForm接口去更新对应的卡片,仅更新入参中携带的数据信息,其他信息保持不变
formProvider.updateForm(formId, formData).catch((error) => {
console.info('[EntryFormAbility] updateForm, error:' + JSON.stringify(error));
});
}
```
### 开发卡片页面
开发者可以使用类Web范式(HML+CSS+JSON)开发JS卡片页面。生成如下卡片页面,可以这样配置卡片页面文件:
![widget-development-stage](figures/widget-development-stage.png)
> **说明:**
> 当前仅支持JS扩展的类Web开发范式来实现卡片的UI界面。
- HML:使用类Web范式的组件描述卡片的页面信息。
```html
<div class="container">
<stack>
<div class="container-img">
<image src="/common/widget.png" class="bg-img"></image>
</div>
<div class="container-inner">
<text class="title">{{title}}</text>
<text class="detail_text" onclick="routerEvent">{{detail}}</text>
</div>
</stack>
</div>
```
- CSS:HML中类Web范式组件的样式信息。
```css
.container {
flex-direction: column;
justify-content: center;
align-items: center;
}
.bg-img {
flex-shrink: 0;
height: 100%;
}
.container-inner {
flex-direction: column;
justify-content: flex-end;
align-items: flex-start;
height: 100%;
width: 100%;
padding: 12px;
}
.title {
font-size: 19px;
font-weight: bold;
color: white;
text-overflow: ellipsis;
max-lines: 1;
}
.detail_text {
font-size: 16px;
color: white;
opacity: 0.66;
text-overflow: ellipsis;
max-lines: 1;
margin-top: 6px;
}
```
- JSON:卡片页面中的数据和事件交互。
```json
{
"data": {
"title": "TitleDefault",
"detail": "TextDefault"
},
"actions": {
"routerEvent": {
"action": "router",
"abilityName": "EntryAbility",
"params": {
"message": "add detail"
}
}
}
}
```
### 开发卡片事件
卡片支持为组件设置交互事件(action),包括**router**事件和**message**事件,其中router事件用于Ability跳转,message事件用于卡片开发人员自定义点击事件。
关键步骤说明如下:
1. 在HML中为组件设置onclick属性,其值对应到JSON文件的actions字段中。
2. 设置router事件:
- action属性值为"router"。
- abilityName为跳转目标的Ability名(支持跳转FA模型的PageAbility组件和Stage模型的UIAbility组件),如目前DevEco Studio创建的Stage模型的UIAbility默认名为EntryAbility。
- params为传递给跳转目标Ability的自定义参数,可以按需填写。其值可以在目标Ability启动时的want中的parameters里获取。如Stage模型MainAbility的onCreate生命周期里的入参want的parameters字段下获取到配置的参数。
3. 设置message事件:
- action属性值为"message"。
- params为message事件的用户自定义参数,可以按需填写。其值可以在卡片生命周期函数onFormEvent()中的message里获取。
示例如下。
- HML文件
```html
<div class="container">
<stack>
<div class="container-img">
<image src="/common/widget.png" class="bg-img"></image>
</div>
<div class="container-inner">
<text class="title" onclick="routerEvent">{{title}}</text>
<text class="detail_text" onclick="messageEvent">{{detail}}</text>
</div>
</stack>
</div>
```
- CSS文件
```css
.container {
flex-direction: column;
justify-content: center;
align-items: center;
}
.bg-img {
flex-shrink: 0;
height: 100%;
}
.container-inner {
flex-direction: column;
justify-content: flex-end;
align-items: flex-start;
height: 100%;
width: 100%;
padding: 12px;
}
.title {
font-size: 19px;
font-weight: bold;
color: white;
text-overflow: ellipsis;
max-lines: 1;
}
.detail_text {
font-size: 16px;
color: white;
opacity: 0.66;
text-overflow: ellipsis;
max-lines: 1;
margin-top: 6px;
}
```
- JSON文件
```json
{
"data": {
"title": "TitleDefault",
"detail": "TextDefault"
},
"actions": {
"routerEvent": {
"action": "router",
"abilityName": "EntryAbility",
"params": {
"info": "router info",
"message": "router message"
}
},
"messageEvent": {
"action": "message",
"params": {
"detail": "message detail"
}
}
}
}
```
- 在UIAbility中接收router事件并获取参数
```ts
import UIAbility from '@ohos.app.ability.UIAbility'
export default class EntryAbility extends UIAbility {
onCreate(want, launchParam) {
// 获取router事件中传递的info参数
if (want.parameters.info === "router info") {
// do something
// console.log("router info:" + want.parameters.info)
}
// 获取router事件中传递的message参数
if (want.parameters.message === "router message") {
// do something
// console.log("router message:" + want.parameters.message)
}
}
// ...
};
```
- 在FormExtensionAbility中接收message事件并获取参数
```ts
import FormExtension from '@ohos.app.form.FormExtensionAbility';
export default class FormAbility extends FormExtension {
// ...
onFormEvent(formId, message) {
// 获取message事件中传递的detail参数
let msg = JSON.parse(message)
if (msg.params.detail === "message detail") {
// do something
// console.log("message info:" + msg.params.detail)
}
}
// ...
};
```
## 限制
为了降低FormExtensionAbility能力被三方应用滥用的风险,在FormExtensionAbility中限制以下接口的调用
- @ohos.ability.particleAbility.d.ts
- @ohos.backgroundTaskManager.d.ts
- @ohos.resourceschedule.backgroundTaskManager.d.ts
- @ohos.multimedia.camera.d.ts
- @ohos.multimedia.audio.d.ts
- @ohos.multimedia.media.d.ts
## 相关实例
针对Stage模型卡片提供方的开发,有以下相关实例可供参考:
- [FormExtAbility:Stage模型卡片(ArkTS)(API9)](https://gitee.com/openharmony/applications_app_samples/tree/master/code/SuperFeature/Widget/FormExtAbility)
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册