📌注意:
- 普通应用也可以添加卡片
- 元服务默认就有一张2*2的卡片,并且因为元服务没有icon,以卡片作为入口
- 后续课程中如非特别说明,卡片的开发方式在普通应用和元服务中一致
1 服务卡片介绍
服务卡片(以下简称“卡片”)是一种界面展示形式,可以将应用的重要信息或操作前置到卡片,以达到服务直达、减少体验层级的目的。卡片常用于嵌入到其他应用(当前卡片使用方只支持系统应用,如桌面)中作为其界面显示的一部分,并支持拉起页面、发送消息等基础的交互功能。
1.1 卡片的基本概念
- 卡片使用方: 显示卡片内容的地方,例如手机桌面
- 卡片提供方: 包含卡片的应用,提供卡片的显示内容、控件布局以及控件点击处理逻辑。
- FormExtensionAbility:卡片业务逻辑模块,提供卡片创建、销毁、刷新等生命周期回调。
- 卡片页面:卡片UI模块,包含页面控件、布局、事件等显示和交互信息。
1.2 卡片常用添加方法
**针对应用**
- 长按“桌面图标”,弹出操作菜单。
- 点击“服务卡片”选项,进入卡片预览界面。
- 点击“添加到桌面”按钮,即可在桌面上看到新添加的卡片。
1.2.2 针对元服务
- 手指捏合屏幕
- 点击“服务卡片”选项,拉到底,找到“其他服务卡片”,找到元服务,进入卡片预览界面
- 点击添加到“桌面按钮”,即可在桌面上看到新添加的卡片
1.2.3 划重点
- 应用和元服务都可以使用服务卡片
- 对应用而言:卡片是可选的,因为有icon作为入口
- 对元服务而言:卡片是必须的,因为没有icon作为入口,默认
- 添加卡片到桌面:
- 对应用而言:选中icon,点击 ”服务卡片“,选择并添加即可
- 对元服务而言:捏合桌面,选择”服务卡片“,在 “其他服务卡片”中找到元应用,选择卡片并添加即可
1.3 服务卡片UI页面开发方式
在Stage模型下,服务卡片的UI页面支持通过ArkTS和JS两种语言进行开发:
- 基于声明式范式ArkTS UI开发的卡片,简称ArkTS卡片
- 基于类Web范式JS UI开发的卡片,简称JS卡片。
ArkTS卡片与JS卡片具备不同的实现原理及特征,在场景能力上的差异如下表所示。
类别 | JS卡片 | ArkTS卡片 |
开发范式 | 类Web范式 | 声明式范式 |
组件能力 | 支持 | 支持 |
布局能力 | 支持 | 支持 |
事件能力 | 支持 | 支持 |
自定义动效 | 不支持 | 支持 |
自定义绘制 | 不支持 | 支持 |
逻辑代码执行(不包含import能力) | 不支持 | 支持 |
划重点: 开发中推荐使用 ArkTS 卡片进行开发,可以使用最多的ArkTs的特性,和应用开发体验基本一致
2 创建服务卡片
2.1 创建步骤
- 创建卡片:右键-新建-Service Widget
- 根据实际业务场景,选择一个卡片模板。
- 基础卡片:最简单的卡片模版
- 图文信息:图片在上,信息在下的卡片模版
- 沉浸式信息:图片作为背景,信息覆盖在上面
- 在选择卡片的开发语言类型(Language)时,选择ArkTS选项,然后单击“Finish”,即可完成ArkTS卡片创建。
2.2 卡片涉及文件
ArkTS卡片创建之后需要关注的几个核心文件:
- FormExtensionAbility:卡片业务逻辑模块,提供卡片创建、销毁、刷新等生命周期回调。
- 卡片页面WidgetCard.ets:卡片UI模块,包含页面控件、布局、事件等显示和交互信息。
- form_config.json:卡片的描述和配置信息,包括定义卡片大小,定时刷新等功能
2.3 卡片相关配置
相比于之前应用的界面,现在额外增加了卡片,在进入到卡片界面开发之后,咱们先搞明白几个较为常见,都和卡片配置相关的问题
- 卡片如何和应用进行关联?
- 多张卡片,如何设置默认卡片(服务卡片界面默认选中的那张)?
- 如何调整卡片保存的位置?
- 如何删除卡片(某张 / 所有)?
2.3.1 问题1 卡片如何和应用进行关联?
卡片在module.json5配置文件中的extensionAbilities标签下配置,添加卡片时会自动添加卡片与应用的关联。
配置示例如下:
2.3.2 问题2、3、4
-
多张卡片,如何设置默认卡片(服务卡片界面默认选中的那张)?
-
如何调整卡片保存的位置?
-
如何删除卡片(某张 / 所有)?
默认情况下会使用开发视图的resources/base/profile/目录下的form_config.json作为卡片profile配置文件。
内部字段详细结构说明可以参考官方文档,这里咱们只针对需求部分进行说明
多张卡片,如何设置默认卡片(服务卡片界面默认选中的那张)?
- 调整form_config.json中的卡片配置信息isDefault为true
- 每个UIAbility有且只有一个默认卡片,所以当有多个卡片信息时,只需要配置一个的isDefault为true
如何调整卡片保存的位置?
- src:表示卡片对应的UI代码的完整路径,使用ArkTs卡片时,需要包含后缀名
- 调整卡片位置及对应的src属性即可改变卡片保存位置
form_config.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": false,
"scheduledUpdateTime": "10:30",
"updateDuration": 1,
"defaultDimension": "2*2",
"supportDimensions": [
"2*2"
]
},
{
"name": "widget1",
"description": "This is a service widget.",
"src": "./ets/widget1/pages/Widget1Card.ets",
"uiSyntax": "arkts",
"window": {
"designWidth": 720,
"autoDesignWidth": true
},
"colorMode": "auto",
"isDefault": false,
"updateEnabled": false,
"scheduledUpdateTime": "10:30",
"updateDuration": 1,
"defaultDimension": "2*2",
"supportDimensions": [
"2*2"
]
}
]
}
- 如何删除卡片?
- 删除某张卡片:
- 删除卡片文件:ets/widget/pages/xxx.ets
- 删除配置信息:resources/base/profile/form_config.json,卡片数组中,和卡片相关的那个对象即可
- 删除所有卡片:
- 删除卡片文件:ets/widget/pages/xxx.ets
- 删除配置文件:resources/base/profile/form_config.json
- 删除module.json5中,和卡片相关的配置
module.json5
- 删除某张卡片:
{
"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"
}
]
}
]
}
}
3 服务卡片页面开发
📌使用ArkTS方式开发的卡片,支持绝大多数ArkTS声明式开发范式,但是因为卡片一般显示桌面应用,为确保桌面的使用体验以及功耗相关考虑,对于ArkTS的卡片有一些约束,这里列出影响较大的2个:
- 暂不支持导入模块
- 仅支持声明式范式的部分组件、事件、动效、数据管理、状态管理和API能力(常用的都可以使用)
更为具体的约束细节,可以参考如下官方文档
3.1 页面尺寸
服务卡片支持多种卡片尺寸:微、小、中、大。卡片展示的尺寸大小分别对应桌面不同的宫格数量,微卡片对应 12 宫格,小卡片对应 22 宫格,中卡片对应 24 宫格,大卡片对应 44 宫格。
卡片尺寸 | 对应宫格数 |
微卡片 | 1*2 宫格 |
小卡片 | 2*2 宫格 |
中卡片 | 2*4 宫格 |
大卡片 | 4*4 宫格 |
同一个应用可以支持多种不同类型的服务卡片,不同尺寸与类型可以通过卡片管理界面进行切换和选择。
上滑应用图标展示的默认卡片的尺寸由开发者指定。
服务卡片在上架时必定要包含小尺寸卡片,即2*2尺寸卡片,该尺寸在不同设备上具备最佳兼容性。
划重点:
- 实际开发时根据产品的要求,添加对应尺寸的卡片即可
- 服务卡片的设计原则参考-服务卡片设计原则
3.2 尺寸调整
调整卡片属性中的 supportDimensions 属性即可调整对应卡片的宫格数,需要注意的是,只能设置如下的值:
12,22,24,44
同时为了在布局实现时更为便捷,可以每张卡就设置一种宫格数
- defaultDimension :表示卡片的默认外观规格,取值必须在该卡片supportDimensions配置的列表中。
- supportDimensions:表示卡片支持的外观规格,取值范围:
- 1 * 2:表示1行2列的二宫格。
- 2 * 2:表示2行2列的四宫格。
- 2 * 4:表示2行4列的八宫格。
- 4 * 4:表示4行4列的十六宫格。
📌defaultDimension 的值只能是来源于supportDimensions数组中定义的值,否则卡片不会显示
{
"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*4" ,
"supportDimensions": [
"2*4"
]
// 错误写法
"defaultDimension": " 2*4 ",
"supportDimensions": [
" 2*2 "
]
}
]
}
3.3 页面开发
实际开发时虽然卡片能力有部分限制,但是绝大多数都是支持的,根据实际需求实现效果即可,这里不再展开,卡片开发的重点是之后的 卡片事件 及 数据交互
3.4 卡片生命周期
卡片生命周期管理文件(EntryFormAbility.ts)
EntryFormAbility.ts页面中默认有很多生命周期函数,其中使用较为频繁的生命周期函数为:
- onAddForm:添加卡片时触发
- onUpdateForm:卡片支持定时更新/定点更新/卡片使用方主动请求更新功能时触发
- onFormEvent:卡片通过message触发事件时触发
- onRemoveForm:移除卡片时触发
// FormInfo模块提供对卡片的相关卡片信息和状态进行设置和查询的能力
import formInfo from '@ohos.app.form.formInfo';
// 卡片数据绑定模块提供卡片数据绑定的能力。包括FormBindingData对象的创建、相关信息的描述。
import formBindingData from '@ohos.app.form.formBindingData';
// FormExtensionAbility为卡片扩展模块,提供卡片创建、销毁、刷新等生命周期回调。
import FormExtensionAbility from '@ohos.app.form.FormExtensionAbility';
export default class EntryFormAbility extends FormExtensionAbility {
// 使用方创建卡片时触发,提供方需要返回卡片数据绑定类
onAddForm(want) {
// Called to return a FormBindingData object.
let formData = {};
return formBindingData.createFormBindingData(formData);
}
// 使用方将临时卡片转换为常态卡片触发,提供方需要做相应的处理
onCastToNormalForm(formId) {
// Called when the form provider is notified that a temporary form is successfully
// converted to a normal form.
}
// 若卡片支持定时更新/定点更新/卡片使用方主动请求更新功能,则提供方需要重写该方法以支持数据更新
onUpdateForm(formId) {
// Called to notify the form provider to update a specified form.
}
onChangeFormVisibility(newStatus) {
// Called when the form provider receives form events from the system.
}
// 若卡片支持触发事件,则需要重写该方法并实现对事件的触发
onFormEvent(formId, message) {
// Called when a specified message event defined by the form provider is triggered.
}
// 当对应的卡片删除时触发的回调,入参是被删除的卡片ID
onRemoveForm(formId) {
// Called to notify the form provider that a specified form has been destroyed.
}
// 卡片提供方接收查询卡片状态通知接口,默认返回卡片初始状态。
onAcquireFormState(want) {
// Called to return a {@link FormState} object.
return formInfo.FormState.READY;
}
};
📌FormExtensionAbility进程不能常驻后台
即在卡片生命周期函数中无法处理长时间的任务,在生命周期调度完成后会继续存活5秒
如果5秒内没有新的生命周期回调触发则进程自动退出。
针对可能需要5秒以上才能完成的业务逻辑,建议拉起主应用进行处理,处理完成后使用updateForm方法通知卡片进行刷新。
4 给卡片设置数据
因为卡片不支持import,所以没法通过引入@ohos.net.http等包来进行数据请求,因此需要通过借助外部能力获取数据后,再传入卡片内部进行展示。
外部能力有:
- EntryFormAbility / EntryAbility
- 应用或者元服务的各个页面(包括组件)
常见的场景:
- 添加卡片到桌面时设置初始数据 → EntryFormAbility 的onAddForm生命周期函数中完成
- 在应用或者元服务页面获取完数据后,主动将数据更新到卡片中
4.1 添加卡片到桌面时初始化数据
当用户把卡片添加到桌面上时,会触发FormExtensionAbility中的 onAddForm生命周期方法,此时我们可以给卡片传递初始数据。
分为同步和异步两种方式
4.1.1 同步方式(不常用)
当将FormExtensionAbility中一个已有的数据传递给卡片时,可以直接使用同步方式
分3步走:
- 在FormExtensionAbility中的onAddForm中定义数据对象
- 通过onAddForm的返回值返回数据
- 卡片中通过localStorageProp接收
案例场景: 在onAddForm中给卡片传入 一个标题 和 一个描述信息,在卡片中正常显示
- 在FormExtensionAbility中的onAddForm中定义需要传递给卡片的数据对象
- 在onAddForm中返回通过 formBindingData.createFormBindingData 绑定的数据
export default class EntryFormAbility extends FormExtensionAbility {
onAddForm(want) {
// 1. 定义需要传递给卡片的数据对象
let formData = {
title:'博学谷鸿蒙课程',
desc:'鸿蒙千帆起,我要当舵手'
};
// 2. 返回绑定后的数据对象,卡片将自动获取到该数据进行显示
return formBindingData.createFormBindingData(formData);
}
}
- 卡片WidgetCard.ets中通过localStorageProp接收
const storage = new LocalStorage();
@Entry(storage)
@Component
struct WidgetCard {
@LocalStorageProp("title") title: string = '';
@LocalStorageProp("desc") desc: string = ''
build() {
Column() {
Text(this.title)
Text(this.desc)
}
.width("100%")
.height("100%")
.onClick(() => {
postCardAction(this, {
"action": "router",
"abilityName": "EntryAbility",
"params": {
"message": "add detail"
}
});
})
}
}
在模拟器中将卡片到桌面,如果上述代码没有问题的话,应该就可以看到传递的默认数据渲染到卡片上啦
划重点:
- 在EntryFormAbility中通过onAddForm的返回值设置初始数据
- 在卡片中通过LocalStorageProp接收并使用数据
4.1.2 异步方式(常用)
当FormExtensionAbility请求一个数据后,是异步回调的,此时需要用到异步方式更新卡片数据
分3步走:
- 在FormExtensionAbility中的onAddForm中异步获取一个数据
- 通过formProvider.updateForm()方法异步更新卡片数据
- 卡片中通过localStorageProp接收
案例场景: 在FormExtensionAbility的onAddForm中异步获取数据后,将数据更新到卡片上
数据:① title:博学谷鸿蒙课程 ② desc:鸿蒙千帆起,我要当舵手
- 在EntryFormAbility的onAddForm方法中用setTimeout模拟异步请求数据
- 在setTimeout回调函数中使用formProvider.updateForm方法传入异步数据
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) {
let formData = {
title:'鸿蒙课程',
desc:'鸿蒙千帆起,我要当舵手'
};
// 1. 定义需要传递给卡片的数据对象
setTimeout(()=>{
formData = {
title:'鸿蒙课程-刷新后',
desc:'鸿蒙千帆起,我要当舵手 - 刷新后'
};
// 1. 定义需要传递给卡片的数据对象
let bindData = formBindingData.createFormBindingData(formData);
// 2. 获取当前添加到桌面的卡片id
let formid = want.parameters[formInfo.FormParam.IDENTITY_KEY];
// 3. 更新数据到卡片上
formProvider.updateForm(formid,bindData) .then(res=>{
console.log('mylog','更新成功');
}).catch(err=>{
console.log('mylog','更新失败');
});
},2000); // 注意:此处不能超过5000,即5秒钟,因为EntryFormAbility的生命周期为5秒
// 当异步更新时,onAddForm方法最后可以返回 null
return formBindingData.createFormBindingData(formData);
}
};
📌注意 : FormExtensionAbility进程不能常驻后台,在生命周期调度完成后会继续存在5秒,如5秒内没有新的生命周期回调触发则进程自动退出
- 卡片WidgetCard.ets中通过localStorageProp接收
const storage = new LocalStorage();
@Entry(storage)
@Component
struct WidgetCard {
@LocalStorageProp("title") title: string = '';
@LocalStorageProp("desc") desc: string = ''
build() {
Column() {
Text(this.title)
Text(this.desc)
}
.width("100%")
.height("100%")
.onClick(() => {
postCardAction(this, {
"action": "router",
"abilityName": "EntryAbility",
"params": {
"message": "add detail"
}
});
})
}
}
4.2 在应用 /元服务中更新数据到卡片
卡片添加到桌面上以后,用户在访问应用/元服务中的页面时,可以从页面中主动向卡片推送数据,以此来更新卡片数据
分为3步走:
- 在卡片添加到桌面时,可以在onAddForm中将卡片id保存到首选项中
- 在主应用或者元服务页面中调用**formProvider.updateForm(**卡片id,数据) 给卡片发送数据
- 卡片中通过localStorageProp接收数据
前置准备:在项目common中创建一个PreferencesUtil.ts工具类函数(以后可以直接使用它来存储和获取卡片id数据)
4.2.1 存卡片id
- 在卡片添加到桌面时,可以在onAddForm中将卡片id保存到首选项中(使用PreferencesUtil工具函数实现)
import formInfo from '@ohos.app.form.formInfo';
import formBindingData from '@ohos.app.form.formBindingData';
import FormExtensionAbility from '@ohos.app.form.FormExtensionAbility';
import { PreferencesUtil } from '../common/PreferencesUtil';
export default class EntryFormAbility extends FormExtensionAbility {
onAddForm(want) {
let formData = {
title:'鸿蒙课程',
desc:'鸿蒙千帆起,我要当舵手'
};
// 1. 获取卡片id
let formId = want.parameters[formInfo.FormParam.IDENTITY_KEY];
//2. 将卡片id保存到首选项
PreferencesUtil.getInstance().addFormId(this.context,formId);
// 2. 将数据转为卡片识别的对象返回
return formBindingData.createFormBindingData(formData);
}
};
4.2.2 页面中发送数据
- 在主应用或者元服务页面中调用**formProvider.updateForm(**卡片id,数据) 给卡片发送数据
在src/main/ets/pages/Home/QATop.ets中向卡片发送数据
async aboutToAppear() {
// 1. 从首选项获取卡片id
let cardids = await PreferencesUtil.getInstance().getFormIds(getContext(this));
// 2. 遍历卡片id,更新卡片数据
let data = {
title: '鸿蒙课程 - 主应用推送刷新'+Math.random(),
desc: '鸿蒙千帆起,我要当舵手-主应用推送刷新'+Math.random()
};
cardids.forEach(cardid => {
let bindData = formBindingData.createFormBindingData(data);
formProvider.updateForm(cardid, bindData)
.then(res => {
console.log('mylog',JSON.stringify(res))
})
.catch(err => {
console.log('mylog','err',JSON.stringify(err))
})
})
}
Button('更新卡片数据')
.onClick(async () => {
await this.updataData();
})
4.2.3 卡片中接收数据
卡片WidgetCard.ets中通过localStorageProp接收
const storage = new LocalStorage();
@Entry(storage)
@Component
struct WidgetCard {
@LocalStorageProp("title") title: string = '';
@LocalStorageProp("desc") desc: string = ''
build() {
Column() {
Text(this.title)
Text(this.desc)
}
.width("100%")
.height("100%")
.onClick(() => {
postCardAction(this, {
"action": "router",
"abilityName": "EntryAbility",
"params": {
"message": "add detail"
}
});
})
}
}
4.2.4 测试卡片数据刷新结果
- 添加卡片到桌面上
- 进入主应用执行刷新数据
- 检查桌面上的卡片数据是否更新成功
📌**注意:**坑点: 添加卡片到桌面后,在不杀掉主应用的情况下,通过主应用向卡片更新数据不成功,需要先杀掉主应用后再打开主应用才能正常
4.2.5 主应用不能刷新卡片数据bug解决
📌坑点: 添加卡片到桌面后,在不杀掉主应用的情况下,通过主应用向卡片更新数据不成功,需要先杀掉主应用后再打开主应用才能正常
原因: 在主应用中获取不到onAddForm函数中保存到首选项的卡片id数据(预测卡片和主应用是两个进程,主应用不杀掉之前,2个进程间数据不能共享)
解决: 在onAddForm函数中通知主应用将卡片id也保存一份到自己的首选项中,自己存自己取就能解决进程间数据不能共享的问题
- 在onAddForm生命周期事件中增加发布方法
import { PublishEventType } from '../common/Constants';
onAddForm(want) {
// Called to return a FormBindingData object.
let formData = {
title:'博学谷鸿蒙课程',
desc:'鸿蒙千帆起,我要当舵手'
};
// 1. 定义需要传递给卡片的数据对象
setTimeout(()=>{
formData = {
title:'鸿蒙课程-刷新后',
desc:'鸿蒙千帆起,我要当舵手 - 刷新后'
};
// 1. 定义需要传递给卡片的数据对象
let bindData = formBindingData.createFormBindingData(formData);
// 2. 获取当前添加到桌面的卡片id
let formid = want.parameters[formInfo.FormParam.IDENTITY_KEY];
PreferencesUtil.getInstance().addFormId(this.context,formid)
SubscriberClass.publish(PublishEventType.APP_PUBLISH,formid)
// 3. 更新数据到卡片上
formProvider.updateForm(formid,bindData).then(res=>{
console.log('mylog','更新成功');
}).catch(err=>{
console.log('mylog','更新失败');
});
},2000); // 注意:此处不能超过5000,即5秒钟,因为EntryFormAbility的生命周期为5秒
return formBindingData.createFormBindingData(formData);
}
- 在index.ets中增加订阅方法
async aboutToAppear(){
// 判断如果用户登录过,则首选项中有数据,则获取到数据后再重新设置一下内存中的用户数据
// 否则就跳转到登录页面
let user = await AppStorageKit.GetLoginUser(getContext(this));
if(user && user.uid){
// 用户存在
AppStorageKit.SetLoginUser(user,getContext(this))
console.log('Index,判断用户是存在的')
}else{
router.pushUrl({url:Constants.PAGE_LOGIN})
}
// 接收通知保存卡片数据
SubscriberClass.subscribe(PublishEventType.APP_PUBLISH,null,(formid)=>{
PreferencesUtil.getInstance().addFormId(getContext(this),formid)
console.log('mylog->','publish',formid)
})
}
5 卡片事件
为什么要有卡片事件: 因为卡片本身不能导入任何包来进行操作,比如:不能直接做http请求,所以只能通过不同事件,调起各种能力来进行相关功能操作。
在卡片内部可以通过postCardAction()主动与主应用的Ability以及卡片自己的Ability进行交互,当前支持:
- router:拉起应用到前台,用户直接操作应用中的功能
- call:拉起应用到后台,用户无感知,不做任何界面操作 - 长时任务
- message:拉起FormExtensionAbility(卡片生命周期管理文件)- 短时任务 5秒以内
**postCardAction**参数
接口定义: postCardAction(component: Object, action: Object): void
接口参数说明:
参数名 | 参数类型 | 必填 | 参数描述 |
component | Object | 是 | 当前自定义组件的实例,通常传入this。 |
action | Object | 是 | action的具体描述,详情见下表。 |
action参数说明:
Key | Value | 样例描述 |
“action” | string | action的类型,支持三种预定义的类型: * “router”:跳转到提供方应用的指定UIAbility。 * ** “message”:自定义消息。触发后会调用提供方FormExtensionAbility的onFormEvent()生命周期回调。 * ** “call”:后台启动提供方应用。触发后会拉起提供方应用的指定UIAbility(仅支持launchType 为singleton的UIAbility,即启动模式为单实例的UIAbility),但不会调度到前台。提供方应用需要具备后台运行权限(ohos.permission.KEEP_BACKGROUND_RUNNING) |
“bundleName” | string | “router” / “call” 类型时跳转的包名,可选。 |
“moduleName” | string | “router” / “call” 类型时跳转的模块名,可选。 |
“abilityName” | string | “router” / “call” 类型时跳转的UIAbility名,必填。 |
“params” | Object | 当前action携带的额外参数,内容使用JSON格式的键值对形式。 "call"类型时需填入参数’method’,且类型需要为string类型,用于触发UIAbility中对应的方法,必填。 |
5.2 postCardAction()示例代码
Button('跳转')
.width('40%')
.height('20%')
.onClick(() => {
postCardAction(this, {
'action': 'router',
'bundleName': 'com.example.myapplication',
'abilityName': 'EntryAbility',
'params': {
'message': 'testForRouter' // 自定义要发送的message
}
});
})
5.3 router事件-拉起应用到前台
postCardAction接口的router类型,可以从卡片中拉起应用到前台,然后用户可以在界面上做一些操作。
场景:例如相机卡片,卡片上提供拍照、录像等按钮,就可以通过postCardAction的route类型来实现拉起
场景示例代码:通过postCardAction的router类型,拉起应用,并跳转到指定页面
- 在卡片中点击按钮,触发postCardAction,通过router拉起应用后,传入不同的参数
@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事件并获取参数,根据传递的params不同,选择拉起不同的页面
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;
}
// 手动触发onWindowStageCreate实现页面跳转功能
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;
}
// 通过loadContent 拉起对应页面
windowStage.loadContent(targetPage, (err, data) => {
if (err && err.code) {
console.info('Failed to load the content. Cause: %{public}s', JSON.stringify(err));
return;
}
});
}
};
划重点:
- 卡片中通过 postCardAction,并且 action设置为router实现拉起应用
- 通过params 传递参数,在UIAbilit的 onCreate(首次) 或 onNewWant(非首次)中接收参数
- 在onWindowStageCreate 中根据传递的参数 拉起指定的页面
5.4 call事件-拉起应用到后台
场景:如果希望在卡片中调起主应用的UIAbility在后台做一个任务时可以用postCardAction接口的call能力(注意:主应用是后台执行一个任务,用户看不到主应用的界面,是无感的)
注意:method参数为必选参数,且类型需要为string类型,用于触发UIAbility中对应的方法。
📌任务执行没有时间限制,可以做一个长时任务
- 前期准备工作:
在module.json5中增加后台运行权限(ohos.permission.KEEP_BACKGROUND_RUNNING)才可以正常调用
{
"name": "ohos.permission.KEEP_BACKGROUND_RUNNING",
"usedScene": {
"abilities": [
"EntryAbility"
],
"when": "inuse"
}
}
- 拉起后台,传入method参数
@Entry
@Component
struct WidgetCard {
build() {
Column() {
Button('功能A')
.margin('20%')
.onClick(() => {
console.info('call EntryAbility funA');
postCardAction(this, {
'action': 'call',
'abilityName': 'EntryAbility', // 只能跳转到当前应用下的UIAbility
'params': {
'method': 'funA', // 在EntryAbility中调用的方法名
'num': 1 // 需要传递的其他参数
}
});
})
}
.width('100%')
.height('100%')
}
}
- 在UIAbility中通过on监听method传入的参数事件,其余数据可以通过readString的方式获取。需要注意的是,UIAbility需要onCreate生命周期中监听所需的方法。
import UIAbility from '@ohos.app.ability.UIAbility';
function FunACall(context) {
return function (data) {
// 获取call事件中传递的所有参数
let params = JSON.parse(data.readString());
console.log('FunACall param:' + params);
let second = params.num;
console.log('mylog->',params,typeof params,second)
let formData = {
title: '博学谷鸿蒙课程-UIAbility',
desc: '鸿蒙千帆起,我要当舵手-UIAbility'
};
let bindData = formBindingData.createFormBindingData(formData)
setTimeout(() => {
PreferencesUtil.getInstance().getFormIds(context).then(cards => {
cards.forEach(cardid => {
formProvider.updateForm(cardid, bindData)
})
})
}, second)
return null;
}
}
export default class
EntryAbility extends UIAbility {
// 如果UIAbility第一次启动,在收到call事件后会触发onCreate生命周期回调
onCreate(want, launchParam) {
try {
// 监听call事件所需的方法
this.callee.on('funA', FunACall(this.context));
} catch (error) {
console.log('register failed with error. Cause: ' + JSON.stringify(error));
}
}
// 进程退出时,解除监听
onDestroy() {
try {
this.callee.off('funA');
} catch (error) {
console.log('register failed with error. Cause: ' + JSON.stringify(error));
}
}
};
划重点:
- 卡片通过 postCardAction,并且 action设置为 call实现后台拉起应用
- 通过params传递参数:
- method:方法名
- 其他参数根据需求自行选择
- onCreate和onDestroy中分别进行监听和移除监听
- 需要添加后台运行权限(ohos.permission.KEEP_BACKGROUND_RUNNING)
5.5 message事件-拉起FormExtensionAbility
场景:如果希望在卡片中调起卡片的FormExtensionAbility在后台做一个任务时可以用postCardAction接口的message能力,然后由在FormExtensionAbility对应的生命周期函数onFormEvent中编写逻辑,常用于刷新卡片数据,接下来提供一个简单的例子:
📌FormExtensionAbility的生命周期为5秒,所以如果超过5秒的长时任务不能在用message事件,请改用call事件调起主应用的Ability去执行
- 在卡片页面通过注册Button的onClick点击事件回调,并在回调中调用postCardAction接口触发message事件拉起FormExtensionAbility。
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生命周期中编写对应的逻辑即可
📌由于FormExtensionAbility的生命周期是5秒,演示代码中setTimeout如果超过5秒则不会执行
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: '博学谷鸿蒙课程-EntryFormAbility',
desc: '鸿蒙千帆起,我要当舵手-EntryFormAbility'
};
let bindData = formBindingData.createFormBindingData(formData)
setTimeout(()=>{
console.log('mylog->','输出文本setTimeout')
formProvider.updateForm(formId,bindData)
},5000) //由于FormExtensionAbility的生命周期是5秒,演示代码中setTimeout如果超过5秒则不会执行
}
...
}