当前位置: 首页 > article >正文

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

  // 制作对应的卡片数据    返回的类型是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中
  1. 编写自定义的箭头函数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()
          }
      
  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
              //   卡片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中
  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.kler.cn/a/517314.html

相关文章:

  • Linux下php8安装phpredis扩展的方法
  • 想品客老师的第六天:函数
  • 【模拟集成电路】锁相环(phase-locked loops,PLL)设计_环形振荡器相关(简)
  • kafka-保姆级配置说明(broker)
  • linux naive代理设置
  • Java数据库操作指南:快速上手JDBC【学术会议-2025年数字化教育与信息技术(DEIT 2025】
  • # AI绘图中的Embedding、CLIP、Flux中的Clip与LCM SDXL加速生成解析
  • Vue 2 + Element UI 实现密码显示、隐藏切换功能
  • rust学习-宏的定义与使用
  • flutter入门系列教程<三>:tabbar的高度自适用,支持无限滚动
  • UDP/TCP ⑤-KCP || QUIC || 应用场景
  • 【2024年华为OD机试】 (C卷,100分)- 考勤信息(JavaScriptJava PythonC/C++)
  • 【leetcode100】二叉树的右视图
  • 职责链模式
  • MES系统和ERP系统有什么区别?
  • Web 渗透测试工具 - SpideyX
  • Mac 上管理本地 Go 版本
  • PHP防伪溯源一体化管理系统小程序
  • 上位机知识篇---return环境变量.bashrc
  • ios打包:uuid与udid
  • 【山东乡镇界】面图层shp格式乡镇名称和编码wgs84坐标无偏移arcgis数据内容测评
  • LLM基础知识
  • B站pwn教程笔记-1
  • 全连接神经网络(前馈神经网络)
  • 二叉树的存储(下)c++
  • Jmeter使用Request URL请求接口