1、卡片的创建
(1)因为后面我们要改变卡片上的值,所以需要对卡片进行刷新,这里选择Dynamic Widget动态卡片
(2)选择一个模板:
(3)配置信息:
(4)创建完成的工程目录:
①卡片工程目录:
- FormExtensionAbility:卡片扩展模块,提供卡片创建、销毁、刷新等生命周期回调。
- FormExtensionContext:FormExtensionAbility的上下文环境,提供FormExtensionAbility具有的接口和能力。
- formProvider:提供卡片提供方相关的接口能力,可通过该模块提供接口实现更新卡片、设置卡片更新时间、获取卡片信息、请求发布卡片等。
- formInfo:提供了卡片信息和状态等相关类型和枚举。
- formBindingData:提供卡片数据绑定的能力,包括FormBindingData对象的创建、相关信息的描述。
- 页面布局(WidgetCard.ets):基于ArkUI提供卡片UI开发能力。
-
- ArkTS卡片通用能力:提供了能在ArkTS卡片中使用的组件、属性和API。
- ArkTS卡片特有能力:postCardAction用于卡片内部和提供方应用间的交互,仅在卡片中可以调用。
- 卡片配置:包含FormExtensionAbility的配置和卡片的配置
-
- 在module.json5配置文件中的extensionAbilities标签下,配置FormExtensionAbility相关信息。
- 在resources/base/profile/目录下的form_config.json配置文件中,配置卡片(WidgetCard.ets)相关信息。
②卡片配置文件:
官网有详细讲解:文档中心
其他几个文档详细的去看官方文档吧,写的很详细。
2、卡片交互
针对动态卡片,ArkTS卡片中提供了postCardAction接口用于卡片内部和提供方应用间的交互,当前支持router、message和call三种类型的事件,仅在卡片中可以调用。
针对静态卡片,ArkTS卡片提供了FormLink用于卡片内部和提供方应用间的交互。(自己研究,这里不讲静态卡片)
动态卡片事件能力说明
动态卡片事件的主要使用场景如下:
- router事件:可以使用router事件跳转到指定UIAbility,并通过router事件刷新卡片内容。
- call事件:可以使用call事件拉起指定UIAbility到后台,并通过call事件刷新卡片内容。
- message事件:可以使用message拉起FormExtensionAbility,并通过FormExtensionAbility刷新卡片内容。
(1)router事件
router说白了,就是可以进行卡片跳转,比如我点击卡片不同对应位置,跳转不同页面,页面显示是由EntryAbility操作的,所以我们通过router事件跳转后,应该在EntryAbility的生命周期中进行处理。
比如我现在在pages中创建两个页面,两个页面显示不同内容:
在卡片中创建两个按钮:
现在我要实现:
1、点击‘跳转Index’按钮时,跳转到Index页面,
2、点击‘跳转Second’按钮时,跳转到Second页面。
在卡片页面调用postCardAction方法,定义router事件,进行参数传递:
在EntryAbility接收对应的参数,并在onCreate和OnNewWant回调函数中进行参数的获取,并交给定义的全局变量selectPage。
onCreate回调:UIAbility第一次创建时,回调函数;
onNewWant回调:UIAbility已存在被再次拉起时,调用方法。
在onNewWant回调函数中,定义windowStage的值不为空时,调用onWindowStageCreate方法,重新加载当前窗口。
在onWindowStageCreate方法中,利用switch语句判断获取参数的值,并根据不同值跳转不同页面,并判断,给的全局变量currentWindowStage是否为空(第一次创建UIAbility时变量为空),为空则把当前onWindowStageCreate方法中的参数windowStage进行传递赋值。
EntryAbility.ets完整代码:
import { AbilityConstant, ConfigurationConstant, UIAbility, Want } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { window } from '@kit.ArkUI';
import { formBindingData, formProvider } from '@kit.FormKit';
const DOMAIN = 0x0000;
export default class EntryAbility extends UIAbility {
// 定义全局变量
private selectPage: string = ''
private currentWindowStage: window.WindowStage | null = null
// 第一次创建UIAbility是会调用onCreate回调函数,创建windowStage舞台
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
this.context.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET);
hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onCreate');
if (want.parameters?.params) {
// 获取拉起卡片router事件中传递的params参数
let params: Record<string, Object> = JSON.parse(want.parameters?.params as string)
this.selectPage = params.targetPage as string
}
}
// 当前UIAbility已存在,再次拉起当前EntryAbility时,会调用onNewWant回调
onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void {
if (want.parameters?.params) {
let params: Record<string, Object> = JSON.parse(want.parameters?.params as string)
this.selectPage = params.targetPage as string
}
// 收到router事件后秒如果当前WindowStage存在,重新调用onWindowStageCreate创建windowStage
if (this.currentWindowStage !== null) {
this.onWindowStageCreate(this.currentWindowStage)
}
}
onDestroy(): void {
hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onDestroy');
}
onWindowStageCreate(windowStage: window.WindowStage): void {
// Main window is created, set main page for this ability
hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onWindowStageCreate');
let targetPage: string
switch (this.selectPage) {
case 'index':
targetPage = 'pages/Index'
break
case 'second':
targetPage = 'pages/Second'
break
default:
targetPage = 'pages/Index'
break
}
// 第一次创建windowStage时,把回调函数中windowStage参数传递给定义的全局变量currentWindowStage
if (this.currentWindowStage===null){
this.currentWindowStage=windowStage
}
windowStage.loadContent(targetPage, (err) => {
if (err.code) {
hilog.error(DOMAIN, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err));
return;
}
hilog.info(DOMAIN, 'testTag', 'Succeeded in loading the content.');
});
}
onWindowStageDestroy(): void {
// Main window is destroyed, release UI related resources
hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onWindowStageDestroy');
}
onForeground(): void {
// Ability has brought to foreground
hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onForeground');
}
onBackground(): void {
// Ability has back to background
hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onBackground');
}
}
(2)message事件
message事件基本就是卡片页面和卡片提供方EntryFormAbility进行交互的过程。例如:现在我要给卡片页面中的Button('+1')设置一个点击事件,点击让count里面的文本数量+1,使用message事件拉起EntryFromAbility,在EntryFormAbility中实现这个功能。
在卡片页面,设置文本,并设置按钮点击事件,这里需要改变的数据需要由@LocalStorageProp装饰,才能由EntryAbility中传递到当前页面,发生数据改变。
在EntryFormAbility中的onFormEvent回调(专门用来处理message事件的回调函数)中,接收传递过来的数据,并对数据实现+1效果,然后把改变后的值由formProvider传递给卡片。
卡片页面代码WidgetTestCard.ets:
let storage = new LocalStorage()
@Entry(storage)
@Component
export struct WidgetTestCard {
@LocalStorageProp('cou') count: number = 0
build() {
Column() {
Column() {
Button('跳转Index').fontSize(14)
.onClick(() => {
postCardAction(this, {
action: 'router',
abilityName: 'EntryAbility',
params: {
targetPage: 'index'
}
})
})
Button('跳转Second').fontSize(14)
.onClick(() => {
postCardAction(this, {
action: 'router',
abilityName: 'EntryAbility',
params: {
targetPage: 'second'
}
})
})
}
Row() {
Text(`总数:${this.count}`).fontSize(20)
Button('+1').fontSize(12)
.onClick(() => {
postCardAction(this, {
action: 'message',
params: {
count: this.count,
}
})
})
}
}
.height('100%')
.width('100%')
}
}
EntryFormAbility.ets代码:
import { formBindingData, FormExtensionAbility, formInfo, formProvider } from '@kit.FormKit';
import { Want } from '@kit.AbilityKit';
export default class EntryFormAbility extends FormExtensionAbility {
onAddForm(want: Want) {
// Called to return a FormBindingData object.
const formData = '';
return formBindingData.createFormBindingData(formData);
}
onCastToNormalForm(formId: string) {
// Called when the form provider is notified that a temporary form is successfully
// converted to a normal form.
}
onUpdateForm(formId: string) {
// Called to notify the form provider to update a specified form.
}
onFormEvent(formId: string, message: string) {
// Called when a specified message event defined by the form provider is triggered.
interface paramsInterface{
count:number
}
let params=JSON.parse(message) as paramsInterface
let formData:Record<string,Object>={
'cou':params.count+1
}
const formInfo=formBindingData.createFormBindingData(formData)
formProvider.updateForm(formId,formInfo)
}
onRemoveForm(formId: string) {
// Called to notify the form provider that a specified form has been destroyed.
}
onAcquireFormState(want: Want) {
// Called to return a {@link FormState} object.
return formInfo.FormState.READY;
}
}
定时和定点刷新不再说明,有需要请看我上一篇文章卡片......
(3)call事件
call事件也是拉起UIAbility,它和router事件、message事件不一样的是:
message:message卡片更新自己卡片页面数据,不会拉起应用,一定时间内(10s)如果没有更新的事件,会被销毁,适合做不太耗时的任务。
call:在服务卡片拉起应用的功能,例如音乐卡片界面点击暂停
/播放
,上一首
/下一首
场景下一般使用call事件。只在onCreate回调中。
router:只是拉起UIAbility,可以在首次和非首次进行数据重新渲染
1)当拉起事件,"action
"为"call
" 类型时,
- 提供方应用需要具备后台运行权限(
ohos.permission.KEEP_BACKGROUND_RUNNING
)。 params
需填入参数'method
',且类型需为string
类型,用于触发UIAbility中对应的方法。
所以需要在module.json配置文件中获取权限
"requestPermissions": [
{
"name": "ohos.permission.KEEP_BACKGROUND_RUNNING"
}
],
2)在卡片页面设置call事件
卡片WidgetTestCard.ets代码:
let storage = new LocalStorage()
@Entry(storage)
@Component
export struct WidgetTestCard {
@LocalStorageProp('cou') count: number = 0
@LocalStorageProp('num') num: number = 666
@LocalStorageProp('formId') formId: string='12345678'
build() {
Column() {
Column() {
Button('跳转Index').fontSize(14)
.onClick(() => {
postCardAction(this, {
action: 'router',
abilityName: 'EntryAbility',
params: {
targetPage: 'index'
}
})
})
Button('跳转Second').fontSize(14)
.onClick(() => {
postCardAction(this, {
action: 'router',
abilityName: 'EntryAbility',
params: {
targetPage: 'second'
}
})
})
}
Row() {
Text(`总数:${this.count}`).fontSize(20)
Button('+1').fontSize(12)
.onClick(() => {
postCardAction(this, {
action: 'message',
params: {
count: this.count,
}
})
})
}
Row() {
Text(`总数:${this.num}`).fontSize(20)
Button('+10').fontSize(12)
.onClick(() => {
postCardAction(this, {
action: 'call',
abilityName:'EntryAbility',
params: {
method:'funcA',
formId:this.formId,
num: this.num,
}
})
console.log('bbb2',`formId:${JSON.stringify(this.formId)}`)
})
}
}
.height('100%')
.width('100%')
}
}
3)在卡片EntryFormAbility中,在onAddForm(卡片创建时调用)回调函数中,通过want获取当前创建卡片的唯一key值,并把这个key值作为formId,传递给卡片页面。
EntryFormAbility.ets页面代码:
import { formBindingData, FormExtensionAbility, formInfo, formProvider } from '@kit.FormKit';
import { Want } from '@kit.AbilityKit';
export default class EntryFormAbility extends FormExtensionAbility {
onAddForm(want: Want) {
// 获取当前添加到桌面的卡片id
let formId: string = want.parameters![formInfo.FormParam.IDENTITY_KEY].toString()
console.log('bbb1',`formId:${JSON.stringify(formId)}`)
// 准备一个临时数据结构 存放formId 和数字
interface FromData {
formId: string,
}
// 准备初始化数据
let formData: FromData = {
'formId': formId,
}
// 返回给卡片
return formBindingData.createFormBindingData(formData);
}
onCastToNormalForm(formId: string) {
// Called when the form provider is notified that a temporary form is successfully
// converted to a normal form.
}
onUpdateForm(formId: string) {
// Called to notify the form provider to update a specified form.
}
onFormEvent(formId: string, message: string) {
// Called when a specified message event defined by the form provider is triggered.
interface paramsInterface{
count:number
}
let params=JSON.parse(message) as paramsInterface
let formData:Record<string,Object>={
'cou':params.count+1
}
const formInfo=formBindingData.createFormBindingData(formData)
formProvider.updateForm(formId,formInfo)
}
onRemoveForm(formId: string) {
// Called to notify the form provider that a specified form has been destroyed.
}
onAcquireFormState(want: Want) {
// Called to return a {@link FormState} object.
return formInfo.FormState.READY;
}
}
4)在EntryAbility的onCreate方法中,使用callee监控call事件,并执行对应回调方法,这里的data数据是rpc.MessageSequence类型数据,进程间通信数据类型,从data中读取call事件传递过来的参数。
EntryAbility代码:
import { AbilityConstant, ConfigurationConstant, UIAbility, Want } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { window } from '@kit.ArkUI';
import { formBindingData, formProvider } from '@kit.FormKit';
import { rpc } from '@kit.IPCKit';
const DOMAIN = 0x0000;
export default class EntryAbility extends UIAbility {
// 定义全局变量
private selectPage: string = ''
private currentWindowStage: window.WindowStage | null = null
// 第一次创建UIAbility是会调用onCreate回调函数,创建windowStage舞台
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
this.context.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET);
hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onCreate');
if (want.parameters?.params) {
// 获取拉起卡片router事件中传递的params参数
let params: Record<string, Object> = JSON.parse(want.parameters?.params as string)
this.selectPage = params.targetPage as string
}
//call事件
try {
this.callee.on('funcA',(data)=>{
// 定义获取参数的结构
interface paramsInterface{
formId:string
num:number
}
// 获取参数
let params=JSON.parse(data.readString()) as paramsInterface
const formData:Record<string,Object>={
'num':params.num+10
}
console.log('bbb3',`formId:${JSON.stringify(params.formId)}`)
let formInfo=formBindingData.createFormBindingData(formData)
formProvider.updateForm(params.formId,formInfo)
return null
})
}catch (err) {
console.error(`onCreate failed,err:${JSON.stringify(err)}`)
}
}
// 当前UIAbility已存在,再次拉起当前EntryAbility时,会调用onNewWant回调
onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void {
if (want.parameters?.params) {
let params: Record<string, Object> = JSON.parse(want.parameters?.params as string)
this.selectPage = params.targetPage as string
}
// 收到router事件后秒如果当前WindowStage存在,重新调用onWindowStageCreate创建windowStage
if (this.currentWindowStage !== null) {
this.onWindowStageCreate(this.currentWindowStage)
}
}
onDestroy(): void {
hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onDestroy');
}
onWindowStageCreate(windowStage: window.WindowStage): void {
// Main window is created, set main page for this ability
hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onWindowStageCreate');
let targetPage: string
switch (this.selectPage) {
case 'index':
targetPage = 'pages/Index'
break
case 'second':
targetPage = 'pages/Second'
break
default:
targetPage = 'pages/Index'
break
}
// 第一次创建windowStage时,把回调函数中windowStage参数传递给定义的全局变量currentWindowStage
if (this.currentWindowStage===null){
this.currentWindowStage=windowStage
}
windowStage.loadContent(targetPage, (err) => {
if (err.code) {
hilog.error(DOMAIN, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err));
return;
}
hilog.info(DOMAIN, 'testTag', 'Succeeded in loading the content.');
});
}
onWindowStageDestroy(): void {
// Main window is destroyed, release UI related resources
hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onWindowStageDestroy');
}
onForeground(): void {
// Ability has brought to foreground
hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onForeground');
}
onBackground(): void {
// Ability has back to background
hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onBackground');
}
}