HarmonyOS基于ArkTS卡片服务

卡片服务

前言

  1. Form Kit(卡片开发框架)提供了一种在桌面、锁屏等系统入口嵌入显示应用信息的开发框架和API,可以将应用内用户关注的重要信息或常用操作抽取到服务卡片(以下简称“卡片”)上,通过将卡片添加到桌面上,以达到信息展示、服务直达的便捷体验效果
  2. 使用场景
    • 支持设备类型:卡片可以在手机、平板等设备上使用。
    • 支持开发卡片应用类型:应用和元服务内均支持开发卡片。
    • 支持卡片使用位置:用户可以在桌面、锁屏等系统应用上添加使用,暂不支持在普通应用内嵌入显示卡片
  3. 卡片类型
    • 静态卡片(Static Widget):规格默认只能是2*4,静态卡片展示的信息是固定的,通常是一次性加载后就不再更新的内容
    • 动态卡片(Dynamic Widget):规格任意,动态卡片展示的信息是实时更新的,内容随着时间、事件、状态等变化而变化

总体思路

在这里插入图片描述

卡片创建

  1. 创建app项目

    在这里插入图片描述
    在这里插入图片描述

  2. 创建卡片项目

    在这里插入图片描述

    卡片分为静态卡片和动态卡片
    静态卡片和动态卡片的区别:静态卡片不接受事件,通过FormLink组件来触发卡片动作动态卡片接受事件,通过事件来接触postCardAction对象触发相应的卡片动作
  3. 卡片设置

    • 卡片项目生成后,在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中
  1. 编写自定义的箭头函数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()}
      
  2. onCardData_bridge_call接收卡片发送过来的卡片id(formId)将其赋值到自定义的卡片对象(FormData)中,通过自定义的updateFormData函数,将普通类型转变味卡片类型数据

  3. 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}`)})}
      
  4. 调用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中
  1. 卡片创建触发 onAddForm,并将卡片id存储在want中

  2. 读取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中
  1. LocalStage获取数据,对formTime进行@Watch监听

  2. 若时间发生变化 使用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}});}
      
  3. 在build中进行卡片UI的渲染

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/68006.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

运算放大器应用电路设计笔记(六)

6.1输出失调电压发生的原因与计算 6.1.1用噪声增益进行评价 若运算放大器两个输入端接地&#xff0c;则理想运放输出为零&#xff0c;但实际的运放输出不为零&#xff0c;有一定的直流输出电压。这种直流电压称为输出失调电压。发生的原因是&#xff0c;运算放大器内部元件尤…

【Vim Masterclass 笔记25】S10L45:Vim 多窗口的常用操作方法及相关注意事项

文章目录 S10L45 Working with Multiple Windows1 水平分割窗口2 在水平分割的新窗口中显示其它文件内容3 垂直分割窗口4 窗口的关闭5 在同一窗口水平拆分出多个窗口6 关闭其余窗口7 让四个文件呈田字形排列8 光标在多窗口中的定位9 调节子窗口的尺寸大小10 变换子窗口的位置11…

【算法】经典博弈论问题——巴什博弈 python

目录 前言巴什博弈(Bash Game)小试牛刀PN分析实战检验总结 前言 博弈类问题大致分为&#xff1a; 公平组合游戏、非公平组合游戏&#xff08;绝大多数的棋类游戏&#xff09;和 反常游戏 巴什博弈(Bash Game) 一共有n颗石子&#xff0c;两个人轮流拿&#xff0c;每次可以拿1~m颗…

电脑如何访问手机文件?

手机和电脑已经深深融入了我们的日常生活&#xff0c;无时无刻不在为我们提供服务。除了电脑远程操控电脑外&#xff0c;我们还可以在电脑上轻松地访问Android或iPhone手机上的文件。那么&#xff0c;如何使用电脑远程访问手机上的文件呢&#xff1f; 如何使用电脑访问手机文件…

CTF随题思路—简单的base编码

打开是一大段base64编码&#xff0c;多次解码后再用base92解码

顺序表和链表(详解)

线性表 线性表&#xff08; linear list&#xff09;是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构&#xff0c;常见的线性表&#xff1a;顺序表、链表、栈、队列、字符串... 线性表在逻辑上是线性结构&#xff0c;也就说是连续的一条直线。…

vxe-table和element表尾合计行

1、vxe-table vxe-table的表尾合计&#xff0c;需要show-footer和footer-method搭配使用。 <vxe-table:data"tableData"ref"vxeRef"border resizable :footer-method"footerMethod":show-footer"true" >…

Grafana系列之Dashboard:新增仪表板、新增变量、过滤变量、变量查询、导入仪表板、变量联动、Grafana Alert

概述 关于Prometheus和Grafana的安装&#xff0c;略过。 写在前面 Dashboard&#xff1a;仪表板&#xff0c;可包含多个PanelPanel&#xff1a;面板&#xff0c;Dashboard中的组件 如有写得不对的地方&#xff0c;烦请指出。 新增仪表板 点击右上角的 选择New dashboard…

使用 Ansys Discovery 对离心风机进行仿真

了解设置模拟并获得有用结果的步骤。 离心风机&#xff1a;基础知识和重要性 离心风机&#xff0c;也称为径流式风机&#xff0c;是旨在通过将动能转化为势能来增加空气或气体的压力和流量的机械装置。它们的工作原理是利用旋转叶轮产生的离心力轴向吸入空气&#xff0c;然后…

客户案例:向导ERP与金蝶云星空集成方案

一、客户背景 该客户公司主要致力于黄金、铂金、金镶玉首饰的研发设计、生产加工、批发及直营加盟业务。公司总部占地面积目前已达6000多平方米&#xff0c;拥有标准生产厂房和现代化生产设施&#xff0c;拥有一支完善的企业管理团队和专业技工队伍。 该企业目前同时采用向导 E…

机器学习-K近邻算法

文章目录 一. 数据集介绍Iris plants dataset 二. 代码三. k值的选择 一. 数据集介绍 鸢尾花数据集 鸢尾花Iris Dataset数据集是机器学习领域经典数据集&#xff0c;鸢尾花数据集包含了150条鸢尾花信息&#xff0c;每50条取自三个鸢尾花中之一&#xff1a;Versicolour、Setosa…

【豆包MarsCode蛇年编程大作战】花样贪吃蛇

目录 引言 展示效果 prompt提示信息 第一次提示&#xff08;实现基本功能&#xff09; 初次实现效果 第二次提示&#xff08;美化UI&#xff09; 第一次美化后的效果 第二次美化后的效果 代码展示 实现在线体验链接 码上掘金使用教程 体验地址&#xff1a; 花样贪吃蛇…

小白爬虫——selenium入门超详细教程

目录 一、selenium简介 二、环境安装 2.1、安装Selenium 2.2、浏览器驱动安装 三、基本操作 3.1、对页面进行操作 3.1.1、初始化webdriver 3.1.2、打开网页 3.1.3、页面操作 3.1.4、页面数据提取 3.1.5、关闭页面 ?3.1.6、综合小案例 3.2、对页面元素进行操作 3…

U3D的.Net学习

Mono&#xff1a;这是 Unity 最初采用的方式&#xff0c;它将 C# 代码编译为中间语言 (IL)&#xff0c;然后在目标平台上使用虚拟机 (VM) 将其转换为本地机器码执行。 IL2CPP&#xff1a;这是一种较新的方法&#xff0c;它会将 C# 代码先编译为 C 代码&#xff0c;再由 C 编译器…

Java集合学习:HashMap的原理

一、HashMap里的Hash是什么&#xff1f; 首先&#xff0c;我们先要搞清楚HashMap里的的Hash是啥意思。 当我们在编程过程中&#xff0c;往往需要对线性表进行查找操作。 在顺序表中查找时&#xff0c;需要从表头开始&#xff0c;依次遍历比较a[i]与key的值是否相等&#xff…

SOAFEE 技术研讨会:汽车软件定义与自动驾驶技术探讨

在本次技术研讨会上&#xff0c;来自汽车与科技领域的专家们围绕汽车软件定义及自动驾驶技术展开了深入交流与探讨。从 SOAFEE 蓝图计划的创新性理念&#xff0c;到 Autoware 开源项目及 Open AD Kit 在实际应用中的探索&#xff0c;再到 Edge Workload Abstraction and Orches…

FastJson很快,有什么用?

FastJson 在国内的热度还是挺高的&#xff0c;受到了很多开发者的喜欢。不过&#xff0c;我自己倒没有在项目中用过。我记得刚工作那会新做的一个项目有明确规定禁止使用 FastJson。 昨天看到一篇关于 FastJson 的文章&#xff0c;这位朋友分享了自己在使用 FastJson 遇到的一…

Jetson nano 安装 PCL 指南

本指南帮助 ARM64 架构的 Jetson Nano 安装 PCL&#xff08;点云库&#xff09;。 安装步骤 第一步&#xff1a;安装依赖 在终端中运行以下命令&#xff0c;安装 PCL 所需的依赖&#xff1a; sudo apt-get update sudo apt-get install git build-essential linux-libc-dev s…

ansible自动化运维实战--软件包管理模块、服务模块、文件模块和收集模块setup(4)

文章目录 一、软件包管理模块1.1、功能1.2、常用参数1.3、示例 二、服务模块2.1、功能2.2、服务模块常用参数2.3、示例 三、文件与目录模块3.1、file功能3.2、常用参数3.3、示例 四、收集模块-setup4.1、setup功能4.2、示例 一、软件包管理模块 1.1、功能 Ansible 提供了多种…

终极的复杂,是简单

软件仿真拥有最佳的信号可见性和调试灵活性,能够高效捕获很多显而易见的常见错误,被大多数工程师熟练使用。 空间领域应用的一套数据处理系统(Data Handling System),采用抗辐FPGA作为主处理器,片上资源只包含10752个寄存器,软仿也是个挺花时间的事。 Few ms might take …