卡片服务
前言
- Form Kit(卡片开发框架)提供了一种在桌面、锁屏等系统入口嵌入显示应用信息的开发框架和API,可以将应用内用户关注的重要信息或常用操作抽取到服务卡片(以下简称“卡片”)上,通过将卡片添加到桌面上,以达到信息展示、服务直达的便捷体验效果
- 使用场景
- 支持设备类型:卡片可以在手机、平板等设备上使用。
- 支持开发卡片应用类型:应用和元服务内均支持开发卡片。
- 支持卡片使用位置:用户可以在桌面、锁屏等系统应用上添加使用,暂不支持在普通应用内嵌入显示卡片
- 卡片类型
- 静态卡片(Static Widget):规格默认只能是2*4,静态卡片展示的信息是固定的,通常是一次性加载后就不再更新的内容
- 动态卡片(Dynamic Widget):规格任意,动态卡片展示的信息是实时更新的,内容随着时间、事件、状态等变化而变化
总体思路
卡片创建
-
创建app项目
-
创建卡片项目
卡片分为静态卡片和动态卡片 静态卡片和动态卡片的区别:静态卡片不接受事件,通过FormLink组件来触发卡片动作动态卡片接受事件,通过事件来接触postCardAction对象触发相应的卡片动作
-
卡片设置
- 卡片项目生成后,在resource中的profile中有 form_config配置卡片信息【可以设置规定规格的卡片supportDimensions】
- 卡片项目生成后,在module.json5中有 EntryFormAbility与卡片有关【第一次创建卡片时会新建一个生命周期在此处,该生命周期为卡片独有的生命周期】
- 每次构建卡片都会新增一个widget文件,此文件为卡片的页面文件
- 同一个模块的卡片共享一个生命周期
前期准备
- 在资源resource中加入float资源文件和midea资源,string资源(默认,中文,英文)
卡片数据层
- 创建common公共资源目录,将卡片数据存放在公共资源目录中
CommonConstants
//卡片更新的数据常量import { CardListItemData } from './CommonData'//工具常量数据
export class CommonConstants{//第一列卡片清单数据static readonly CARD_LIST_DATA_FIRST: Array<CardListItemData> = [{id: 1,title: $r('app.string.title1'),content: $r('app.string.content1'),icon: $r('app.media.item1')},{id: 2,title: $r('app.string.title2'),content: $r('app.string.content2'),icon: $r('app.media.item2')},{id: 3,title: $r('app.string.title3'),content: $r('app.string.content3'),icon: $r('app.media.item3')},{id: 4,title: $r('app.string.title4'),content: $r('app.string.content4'),icon: $r('app.media.item4')}]//第二列卡片清单数据static readonly CARD_LIST_DATA_SECOND: Array<CardListItemData> = [{id: 1,title: $r('app.string.title5'),content: $r('app.string.content5'),icon: $r('app.media.item5')},{id: 2,title: $r('app.string.title6'),content: $r('app.string.content6'),icon: $r('app.media.item6')},{id: 3,title: $r('app.string.title7'),content: $r('app.string.content7'),icon: $r('app.media.item7')},{id: 4,title: $r('app.string.title8'),content: $r('app.string.content8'),icon: $r('app.media.item8')}];//第三列卡片清单数据static readonly CARD_LIST_DATA_THIRD: Array<CardListItemData> = [{id: 1,title: $r('app.string.title9'),content: $r('app.string.content9'),icon: $r('app.media.item9')},{id: 2,title: $r('app.string.title10'),content: $r('app.string.content10'),icon: $r('app.media.item10')},{id: 3,title: $r('app.string.title11'),content: $r('app.string.content11'),icon: $r('app.media.item11')},{id: 4,title: $r('app.string.title12'),content: $r('app.string.content12'),icon: $r('app.media.item12')}];
}
CommonData
// 卡片更新的数据模型
// 存放数据模型以及数据处理
import { CommonConstants } from "./CommonConstants"export class CommonData {// 根据改值与3的余数决定显示那个板块static flag: number = 0// 制作对应的卡片数据 返回的类型是CardListItemDatastatic getData() :Array<CardListItemData>{// 先制作数据模型// 判断flag值,与3的余数if (CommonData.flag % 3 === 0) {// 前往CommonConstants制作数据return CommonConstants.CARD_LIST_DATA_FIRST} else if (CommonData.flag % 3 === 1) {return CommonConstants.CARD_LIST_DATA_SECOND} else {return CommonConstants.CARD_LIST_DATA_THIRD}}static changeFlage() {// 每调取一次数据,flage加一CommonData.flag++}
}//卡片列表数据模型
export interface CardListItemData {id: number// 标题title: ResourceStr// 内容content: ResourceStr// 图标icon?: Resource// 是否喜爱favour?: boolean
}/** 只接收卡片数据 = 卡片id+卡片数据本身* */
//卡片本身数据模型
export class FormData {// 卡片id 会自动生成id(卡片生命周期里面有个want里面藏有id,且每一次运行id都不一样)// 生命周期一旦创建需要拿到idformId: string = ''// 时间(更新,为了之后稳定触发@Watch)formTime: string = ''// 图片相关iamgeItem?: ImageItem// 索引index?: number = 0//卡片信息列表cardList: Array<CardListItemData> = []// 是否喜爱isFavor?:boolean = falseconstructor(formId: string) {this.formId = formId}
}// 定义类需要初始化(constructor)
@Observed
export class ImageItem {id: number = 0image: ResourceStr = ''isFavor: boolean = falseconstructor(id: number, image: ResourceStr, isFavor: boolean) {this.id = idthis.image = imagethis.isFavor = isFavor}
}
卡片功能的实现
EntryAbility
import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { window } from '@kit.ArkUI';
import { rpc } from '@kit.IPCKit';
import { CommonData, FormData } from '../common/CommonData';
import { formBindingData, formProvider } from '@kit.FormKit';
import { BusinessError } from '@kit.BasicServicesKit';export default class EntryAbility extends UIAbility {// 构建程序应用于卡片之间数据的桥梁(在桥梁上将数据准备好)// 数据处理/** @ohos.rpc(rpc通信)* 该模块提供进程间通信的能力,包括设备内的进程通信IPC 和设备间的进程间通信RPC* *//** 在RPC或者IPC过程中,发送方使用MessageSequence提供写方法* 接收方使用MessageSequence提供的读方法从该对象中读取特定格式的数据* */// 1:卡片的更新的接收方触发的回调private oneCardData_bridge_call = (data: rpc.MessageSequence) => {// readString 从MessageSequence读取字符串值// JSON.parse 将数据转为对象格式,同时还有解码的功能// params:有卡片id和messagelet params: Record<string, string> = JSON.parse(data.readString())console.log(`<<<<EntryAbility页面,接收卡片方数据为${JSON.stringify(params)}`)console.log(`<<<<EntryAbility页面,接收方的卡片id为:${params.formId}`)// 如果接收卡片数据中有ID存在if (params.formId !== undefined) {// 定义变量接收卡片IDlet formId: string = params.formId// 只有formId使用了构建器,只可以对其进行初始化,其他只能进行修改// 定义卡片数据,并将接收的卡片ID进行初始化赋值let formData = new FormData(formId)// 将常量卡片数据赋值给FormData对象formData.cardList = CommonData.getData()// 赋值完毕后,将flage++,为下一次赋值做准备CommonData.changeFlage()// 此时为普通数据,需要转换为卡片数据,然后进行卡片数据的更新// 3this.updateFormData(formId,formData)// 需要的类型return new MyParcelable()}return new MyParcelable()}// 更新卡片数据,将普通数据转变为卡片数据// 2private updateFormData(formId: string, formData: FormData) {//formBindingData 卡片数据绑定模块,提供卡片数据绑定的能力 包括对象创建相关信息描述//FormBindingData 卡片要展示的数据,可以是若干键值对的Object或者json格式的字符串let formMsg: formBindingData.FormBindingData = formBindingData.createFormBindingData(formData)/** formProvider模块提供了卡片提供方相关接口的能力* 通过该模块实现更新卡片,设置卡片时间,获取卡片信息,请求发布卡片* updateForm 更新指定卡片 formId 卡片标识,来自卡片创建时的生命周期want* */// 更新卡片数据,localStage里面会进行存储formProvider.updateForm(formId,formMsg).then(()=>{console.log('更新卡片数据成功')}).catch((err:BusinessError)=>{console.log(`<<<更新卡片失败${err.message}`)})}onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate');// 订阅数据 callee(UIAbility中的订阅方式)/** 此处的回调函数需要返回的类型为CalleeCallback* 返回值类型rpc.Parcelable* this.oneCardData_bridge_call 接收卡片返回过来的值** this.callee.on 相当于拦截器,会订阅一切想找updateCardInfo的,拦截到了,触发callback回调** */// on 订阅 of 取消订阅// 4this.callee.on('updateCardInfo',this.oneCardData_bridge_call)}onDestroy(): void {hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onDestroy');}onWindowStageCreate(windowStage: window.WindowStage): void {// Main window is created, set main page for this abilityhilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate');// 视频横竖屏播放。获取屏幕安全区域,并将安全区域高度存入AppStorage中let windowClass:window.Window = windowStage.getMainWindowSync()// getWindowAvoidArea 获取当前窗口内容规避区域,系统栏,刘海屏,手势,软键盘等可能与窗口内容重叠需要内容避让的区域// TYPE_SYSTEM 系统默认区域 包含状态栏,导航栏等let area:window.AvoidArea = windowClass.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM)// 将获取到的顶部避让区域的高度存入AppStorageAppStorage.setOrCreate('statusBarHeight',px2vp(area.topRect.height))// 'pages/Index'windowStage.loadContent('pages/VideoDetail', (err) => {if (err.code) {hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');return;}hilog.info(0x0000, 'testTag', 'Succeeded in loading the content.');});}onWindowStageDestroy(): void {// Main window is destroyed, release UI related resourceshilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageDestroy');}onForeground(): void {// Ability has brought to foregroundhilog.info(0x0000, 'testTag', '%{public}s', 'Ability onForeground');}onBackground(): void {// Ability has back to backgroundhilog.info(0x0000, 'testTag', '%{public}s', 'Ability onBackground');}
}class MyParcelable implements rpc.Parcelable{/** 可看成序列化与反序列化* */// 封可序列对象marshalling(dataOut: rpc.MessageSequence): boolean {// throw new Error('Method not implemented.');return true}// 从MessageSequence解封此可序列对象unmarshalling(dataIn: rpc.MessageSequence): boolean {throw new Error('Method not implemented.');}}
index
/** 相关权限* KEEP_BACKGROUND_RUNNING* 允许Service Ability在后台持续运行* call事件需要提供方(应用)具备后台运行的权限* */// 卡片相对于原应用相当于是两个进程,but卡片时依托于程序的
//卡片更新的应用页面import { preferences } from '@kit.ArkData';
import { FormData, ImageItem } from '../common/CommonData';
import { formBindingData, formProvider } from '@kit.FormKit';
import { BusinessError } from '@kit.BasicServicesKit';@Entry
@Component
struct IndexPage {//获取首选项中的值@StorageLink('myPreferences') myPreferences: preferences.Preferences | undefined = undefined;//读取/存放图片@StorageLink('imageArr') imageInfoArray: ImageItem[] = [//三张轮播图图片new ImageItem(0, $r('app.media.ic_social_circle1'), false),new ImageItem(1, $r('app.media.ic_social_circle2'), false),new ImageItem(2, $r('app.media.ic_social_circle3'), false)];aboutToAppear(): void {//如果首选项中存在值if (this.myPreferences) {if (this.myPreferences.hasSync('imageArr')) {//将用户首选项中的图片读取出来this.imageInfoArray = this.myPreferences.getSync('imageArr', []) as ImageItem[];return;//如果不存在} else {//将图片存入用户首选项并做持久化处理this.myPreferences?.putSync('imageArr', this.imageInfoArray);this.myPreferences?.flush();}}}build() {Navigation(){Swiper(){ForEach(this.imageInfoArray,(item:ImageItem,index) => {//调用图片子组件ImageView({imageItem: item,isFavor: item.isFavor,index: index})},(item: ImageItem) => JSON.stringify(item))}.width('100%').borderRadius(24)}.mode(NavigationMode.Stack).title($r('app.string.EntryAbility_label')).height('100%').width('100%').margin({top: 16}).padding({left: 16,right: 16})}
}//图片视图
@Component
struct ImageView{@StorageLink('myPreferences') myPreferences: preferences.Preferences | undefined = undefined;//图片信息@ObjectLink imageItem: ImageItem;//是否喜爱@State isFavor: boolean = false;//索引index: number = 0;build() {Stack(){Image(this.imageItem.image).objectFit(ImageFit.Auto).width('100%').height('33%')//喜好图标Image(this.isFavor ? $r('app.media.ic_public_favor_filled') : $r('app.media.ic_public_favor')).height(30).aspectRatio(1).margin({right: 8,bottom: 8})}.alignContent(Alignment.BottomEnd)}
}
EntryFromAbility
// 卡片的生命周期
import { formBindingData, FormExtensionAbility, formInfo, formProvider } from '@kit.FormKit';
import { Want } from '@kit.AbilityKit';
import { CommonData, FormData } from '../common/CommonData';
import { BusinessError, systemDateTime } from '@kit.BasicServicesKit';export default class EntryFormAbility extends FormExtensionAbility {onAddForm(want: Want) {// Called to return a FormBindingData object./** 使用方(用户)创建卡片时触发,提供方需要返回卡片数据绑定类* */let formData = '';// 经过卡片数据更新之后,会往LocalStorageProp中存值if (want && want.parameters) {console.log(`<<<卡片创建生命周期${JSON.stringify(want)}`)// 卡片名称let formName: string = want.parameters[`ohos.extra.param.key.form_name`] as string// 卡片idlet formId: string = want.parameters[`ohos.extra.param.key.form_identity`] as string// 检索是否是对应的卡片 做数据发送的区分if (formName === 'widget') {let formData = new FormData(formId)// 系统时间,毫秒,从1970年1.1至今formData.formTime = systemDateTime.getTime().toString()formData.index = 110// 创建封装成formBindingData对象let formInfo: formBindingData.FormBindingData = formBindingData.createFormBindingData(formData)// Provider:卡片提供者// 会向localStage中存储formData对应数据 formTime,formId...formProvider.updateForm(formId, formInfo)return formInfo}}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) {// 由卡片行为中的message事件触发// Called when a specified message event defined by the form provider is triggered.let formData = new FormData(formId)formData.cardList = CommonData.getData()CommonData.changeFlage()let formInfo: formBindingData.FormBindingData = formBindingData.createFormBindingData(formData)// 向卡片页面中的本地存储添加内容formProvider.updateForm(formId, formInfo).then(() => {console.log(`<<<<message刷新卡片成功`)}).catch((err: BusinessError) => {console.log(err.message)})}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;}
};
卡片UI模块的实现
WidgetCard
- 卡片pages目录下的 WidgetCard.ets 文件
import { CardListParameter } from '../viewmodel/CardListParameter';
import { CardListItemData } from '../../common/CommonData'let storageUpdate = new LocalStorage();@Entry(storageUpdate)
@Component
struct WidgetCard {/** 动作类型。*/readonly ACTION_TYPE: string = 'router';/** 能力名称。*/readonly ABILITY_NAME: string = 'EntryAbility';/** 消息*/readonly MESSAGE: string = 'add detail';/** 高度百分比设置。*/readonly FULL_HEIGHT_PERCENT: string = '100%';/** 宽度百分比设置。*/readonly FULL_WIDTH_PERCENT: string = '100%';//读取本地存储中的卡片时间 formTime formId的值由卡片创建生命周期给予@LocalStorageProp('formTime') @Watch('onFormTimeChange') formTime: string = '';@LocalStorageProp('formId') formId: string = '';//由发送完毕后在EntryAbility的回调处给予@LocalStorageProp('cardList') cardList: Array<CardListItemData> = [];@State cardListParameter: CardListParameter = new CardListParameter($r('sys.color.ohos_id_color_background'),$r('app.string.card_list_title'), '', ImageSize.Cover, $r('app.media.logo'), false,$r('sys.color.ohos_id_color_background'), true, this.cardList.length, $r('sys.color.ohos_id_color_emphasize'),$r('app.color.list_item_count_background'), '', false);//卡片时间改变onFormTimeChange() {/** postCardAction 用于卡片内部与提供方应用间的交互* 当前支持三种类型事件 router,message,call* router* 跳转提供方指定的UIAbility* message* 自定义消息,触发后调用卡片生命周期EntryFormAbility中的onFormEvent生命周期钩子函数* call* 后台启动提供方的应用,触发后会拉起提供方应用的指定UIAbility(仅支持跳转类型为singleton的UIAbility)* 但不会调度到前台。提供方需要具备后台运行的权限* *///用于卡片内部和提供方应用间的交互,当前支持router、message和call三种类型的事件,仅在卡片中可以调用。//component 当前自定义组件的实例,通常传入this。//action/** action action的类型,支持三种预定义的类型* router:跳转到提供方应用的指定UIAbility。message:自定义消息,触发后会调用提供方FormExtensionAbility的onFormEvent()生命周期回调。* call:后台启动提供方应用。* 触发后会拉起提供方应用的指定UIAbility(仅支持launchType为singleton的UIAbility,* 即启动模式为单实例的UIAbility),但不会调度到前台。提供方应用需要具备后台运行权限* bundleName action为router / call 类型时跳转的包名。* moduleName action为router / call 类型时跳转的模块名。* abilityName action为router / call 类型时跳转的UIAbility名。* uri11+ action为router 类型时跳转的UIAbility的统一资源标识符。uri和abilityName同时存在时,abilityName优先。* params 当前action携带的额外参数,内容使用JSON格式的键值对形式。* */console.log(`Widget页面时间发生了改变${this.formId}`)postCardAction(this, {action: 'call',abilityName: 'EntryAbility',// 传递的参数 准备需要发往EntryAbility的数据params: {formId: this.formId,// 与EntryAbility中this.callee.on里面的标识符进行匹配method: 'updateCardInfo',message: '更新卡片.' //自定义要发送的message}});}//卡片按钮@BuilderbuttonBuilder(text: ResourceStr, action: string, message: string, method?: string) {Column() {//刷新图标Image($r('app.media.refresh')).width($r('app.float.refresh_image_size')).height($r('app.float.refresh_image_size'))//文字Text(text).fontColor($r('app.color.refresh_color')).fontSize($r('app.float.item_content_font_size')).margin({ top: $r('app.float.text_image_space') })}.justifyContent(FlexAlign.Center).height($r('app.float.refresh_area_height')).width($r('app.float.refresh_area_width')).borderRadius($r('app.float.border_radius')).backgroundColor($r('sys.color.comp_background_focus'))//触发点击方法需要创建动态卡片.onClick(() => {postCardAction(this, {action: action,abilityName: 'EntryAbility',params: {formId: this.formId,method: method,message: message}});})}//卡片列表@BuildercardListBuilder() {if (this.cardList.length > 0) {Column() {Column() {ForEach(this.cardList, (item: CardListItemData) => {ListItem() {Row() {Column() {Text(item.title).maxLines(1).textOverflow({ overflow: TextOverflow.Ellipsis }).fontSize($r('app.float.item_content_font_size')).fontWeight(FontWeight.Medium).fontColor(Color.Black).height($r('app.float.item_text_height')).margin({ top: $r('app.float.item_text_margin') })Text(item.content).maxLines(1).fontSize($r('app.float.item_content_font_size')).textOverflow({ overflow: TextOverflow.Ellipsis }).fontWeight(FontWeight.Regular).height($r('app.float.item_text_height'))Divider().strokeWidth(0.38).lineCap(LineCapStyle.Square).margin({ top: $r('app.float.list_divider_margin') }).visibility(item.id === 4 ? Visibility.None : Visibility.Visible)}.margin({ right: $r('app.float.list_row_margin') }).alignItems(HorizontalAlign.Start).layoutWeight(1)Image(item.icon).width($r('app.float.item_image_size')).height($r('app.float.item_image_size')).borderRadius($r('app.float.border_radius'))}.alignItems(VerticalAlign.Center).width(this.FULL_WIDTH_PERCENT)}.width(this.FULL_WIDTH_PERCENT).height($r('app.float.item_height'))}, (item: number, index) => index + JSON.stringify(item))}Row() {this.buttonBuilder($r('app.string.router'), 'router', 'Router refresh card.')this.buttonBuilder($r('app.string.call'), 'call', 'Call refresh card.', 'updateCardInfo')this.buttonBuilder($r('app.string.message'), 'message', 'Message refresh card.')}.width(this.FULL_WIDTH_PERCENT).justifyContent(FlexAlign.SpaceBetween)}.height(this.FULL_HEIGHT_PERCENT).justifyContent(FlexAlign.SpaceBetween)}}build() {Row() {this.cardListBuilder()}.height(this.FULL_HEIGHT_PERCENT).onClick(() => {postCardAction(this, {action: this.ACTION_TYPE,abilityName: this.ABILITY_NAME,params: {message: this.MESSAGE}});})}
}
结尾
功能实现思路
技术栈实现
EntryAbility中
-
编写自定义的箭头函数onCardData_bridge_call(返回值rpc.Parcelable,接收的参数为data: rpc.MessageSequence)来更新卡片
-
private oneCardData_bridge_call = (data: rpc.MessageSequence) => {// readString 从MessageSequence读取字符串值// JSON.parse 将数据转为对象格式,同时还有解码的功能// params:有卡片id和messagelet params: Record<string, string> = JSON.parse(data.readString())console.log(`<<<<EntryAbility页面,接收卡片方数据为${JSON.stringify(params)}`)console.log(`<<<<EntryAbility页面,接收方的卡片id为:${params.formId}`)// 如果接收卡片数据中有ID存在if (params.formId !== undefined) {// 定义变量接收卡片IDlet formId: string = params.formId// 定义卡片数据,并将接收的卡片ID进行初始化赋值let formData = new FormData(formId)// 将常量卡片数据赋值给FormData对象formData.cardList = CommonData.getData()// 赋值完毕后,将flage++,为下一次赋值做准备CommonData.changeFlage()// 此时为普通数据,需要转换为卡片数据,然后进行卡片数据的更新this.updateFormData(formId,formData)// 需要的类型return new MyParcelable()}return new MyParcelable()}
-
-
onCardData_bridge_call接收卡片发送过来的卡片id(formId)将其赋值到自定义的卡片对象(FormData)中,通过自定义的updateFormData函数,将普通类型转变味卡片类型数据
-
updateFormData函数调用 formProvider.updateForm进行卡片数据更新,同时也会将数据存储到LocalStage中
-
// 更新卡片数据,将普通数据转变为卡片数据private updateFormData(formId: string, formData: FormData) {//formBindingData 卡片数据绑定模块,提供卡片数据绑定的能力 包括对象创建相关信息描述//FormBindingData 卡片要展示的数据,可以是若干键值对的Object或者json格式的字符串let formMsg: formBindingData.FormBindingData = formBindingData.createFormBindingData(formData)/** formProvider模块提供了卡片提供方相关接口的能力* 通过该模块实现更新卡片,设置卡片时间,获取卡片信息,请求发布卡片* updateForm 更新指定卡片 formId 卡片标识,来自卡片创建时的生命周期want* */// 更新卡片数据,localStage里面会进行存储formProvider.updateForm(formId,formMsg).then(()=>{console.log('更新卡片数据成功')}).catch((err:BusinessError)=>{console.log(`<<<更新卡片失败${err.message}`)})}
-
-
调用EntryAbility中的 this.callee.on方法进行数据拦截
-
// 订阅数据 callee(UIAbility中的订阅方式)/** 此处的回调函数需要返回的类型为CalleeCallback* 返回值类型rpc.Parcelable* this.oneCardData_bridge_call 接收卡片返回过来的值** this.callee.on 相当于拦截器,会订阅一切想找updateCardInfo的,拦截到了,触发callback回调** */// on 订阅 of 取消订阅this.callee.on('updateCardInfo',this.oneCardData_bridge_call)
-
EntryFormAbility中
-
卡片创建触发 onAddForm,并将卡片id存储在want中
-
读取want中卡片的id和formName,进行判断后获取系统时间,将formId和formTime封装,通过 formProvider.updateForm进行卡片更新且向LocalStage中存储
-
onAddForm(want: Want) {// Called to return a FormBindingData object./** 使用方(用户)创建卡片时触发,提供方需要返回卡片数据绑定类* */let formData = '';// 经过卡片数据更新之后,会往LocalStorageProp中存值if (want && want.parameters) {console.log(`<<<卡片创建生命周期${JSON.stringify(want)}`)// 卡片名称let formName: string = want.parameters[`ohos.extra.param.key.form_name`] as string// 卡片idlet formId: string = want.parameters[`ohos.extra.param.key.form_identity`] as string// 检索是否是对应的卡片 做数据发送的区分if (formName === 'widget') {let formData = new FormData(formId)// 系统时间,毫秒,从1970年1.1至今formData.formTime = systemDateTime.getTime().toString()formData.index = 110// 创建封装成formBindingData对象let formInfo: formBindingData.FormBindingData = formBindingData.createFormBindingData(formData)// Provider:卡片提供者// 会向localStage中存储formData对应数据 formTime,formId...formProvider.updateForm(formId, formInfo)return formInfo}}return formBindingData.createFormBindingData(formData);}
-
WidgetCard中
-
向 LocalStage获取数据,对formTime进行@Watch监听
-
若时间发生变化 使用postCardAction对卡片内部和提供方进行交互
-
//卡片时间改变onFormTimeChange() {//用于卡片内部和提供方应用间的交互,当前支持router、message和call三种类型的事件,仅在卡片中可以调用。//component 当前自定义组件的实例,通常传入this。//action/** action action的类型,支持三种预定义的类型* router:跳转到提供方应用的指定UIAbility。message:自定义消息,触发后会调用提供方FormExtensionAbility的onFormEvent()生命周期回调。* call:后台启动提供方应用。* 触发后会拉起提供方应用的指定UIAbility(仅支持launchType为singleton的UIAbility,* 即启动模式为单实例的UIAbility),但不会调度到前台。提供方应用需要具备后台运行权限* bundleName action为router / call 类型时跳转的包名。* moduleName action为router / call 类型时跳转的模块名。* abilityName action为router / call 类型时跳转的UIAbility名。* uri11+ action为router 类型时跳转的UIAbility的统一资源标识符。uri和abilityName同时存在时,abilityName优先。* params 当前action携带的额外参数,内容使用JSON格式的键值对形式。* */console.log(`Widget页面时间发生了改变${this.formId}`)postCardAction(this, {action: 'call',abilityName: 'EntryAbility',// 传递的参数 准备需要发往EntryAbility的数据params: {formId: this.formId,// 与EntryAbility中this.callee.on里面的标识符进行匹配method: 'updateCardInfo',message: '更新卡片.' //自定义要发送的message}});}
-
-
在build中进行卡片UI的渲染