HarmonyOS基于ArkTS卡片服务
卡片服务
前言
- 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
// 制作对应的卡片数据 返回的类型是CardListItemData
static 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都不一样)
// 生命周期一旦创建需要拿到id
formId: string = ''
// 时间(更新,为了之后稳定触发@Watch)
formTime: string = ''
// 图片相关
iamgeItem?: ImageItem
// 索引
index?: number = 0
//卡片信息列表
cardList: Array<CardListItemData> = []
// 是否喜爱
isFavor?:boolean = false
constructor(formId: string) {
this.formId = formId
}
}
// 定义类需要初始化(constructor)
@Observed
export class ImageItem {
id: number = 0
image: ResourceStr = ''
isFavor: boolean = false
constructor(id: number, image: ResourceStr, isFavor: boolean) {
this.id = id
this.image = image
this.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和message
let 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) {
// 定义变量接收卡片ID
let formId: string = params.formId
// 只有formId使用了构建器,只可以对其进行初始化,其他只能进行修改
// 定义卡片数据,并将接收的卡片ID进行初始化赋值
let formData = new FormData(formId)
// 将常量卡片数据赋值给FormData对象
formData.cardList = CommonData.getData()
// 赋值完毕后,将flage++,为下一次赋值做准备
CommonData.changeFlage()
// 此时为普通数据,需要转换为卡片数据,然后进行卡片数据的更新
// 3
this.updateFormData(formId,formData)
// 需要的类型
return new MyParcelable()
}
return new MyParcelable()
}
// 更新卡片数据,将普通数据转变为卡片数据
// 2
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}`)
})
}
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 取消订阅
// 4
this.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 ability
hilog.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)
// 将获取到的顶部避让区域的高度存入AppStorage
AppStorage.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 resources
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageDestroy');
}
onForeground(): void {
// Ability has brought to foreground
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onForeground');
}
onBackground(): void {
// Ability has back to background
hilog.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
// 卡片id
let 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
}
});
}
//卡片按钮
@Builder
buttonBuilder(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
}
});
})
}
//卡片列表
@Builder
cardListBuilder() {
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和message let 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) { // 定义变量接收卡片ID let 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 // 卡片id let 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的渲染