HarmonyOS之卡片详解版--动态卡片router、message、call事件

1、卡片的创建

(1)因为后面我们要改变卡片上的值,所以需要对卡片进行刷新,这里选择Dynamic Widget动态卡片

(2)选择一个模板:

(3)配置信息:

(4)创建完成的工程目录:

①卡片工程目录:

  • FormExtensionAbility:卡片扩展模块,提供卡片创建、销毁、刷新等生命周期回调。
  • FormExtensionContext:FormExtensionAbility的上下文环境,提供FormExtensionAbility具有的接口和能力。
  • formProvider:提供卡片提供方相关的接口能力,可通过该模块提供接口实现更新卡片、设置卡片更新时间、获取卡片信息、请求发布卡片等。
  • formInfo:提供了卡片信息和状态等相关类型和枚举。
  • formBindingData:提供卡片数据绑定的能力,包括FormBindingData对象的创建、相关信息的描述。
  • 页面布局(WidgetCard.ets):基于ArkUI提供卡片UI开发能力。
  • 卡片配置:包含FormExtensionAbility的配置和卡片的配置

②卡片配置文件:

官网有详细讲解:文档中心

其他几个文档详细的去看官方文档吧,写的很详细。

2、卡片交互

针对动态卡片,ArkTS卡片中提供了postCardAction接口用于卡片内部和提供方应用间的交互,当前支持router、message和call三种类型的事件,仅在卡片中可以调用。

针对静态卡片,ArkTS卡片提供了FormLink用于卡片内部和提供方应用间的交互。(自己研究,这里不讲静态卡片)

动态卡片事件能力说明

动态卡片事件的主要使用场景如下:

  • router事件:可以使用router事件跳转到指定UIAbility,并通过router事件刷新卡片内容。
  • call事件:可以使用call事件拉起指定UIAbility到后台,并通过call事件刷新卡片内容。
  • message事件:可以使用message拉起FormExtensionAbility,并通过FormExtensionAbility刷新卡片内容。

(1)router事件

router说白了,就是可以进行卡片跳转,比如我点击卡片不同对应位置,跳转不同页面,页面显示是由EntryAbility操作的,所以我们通过router事件跳转后,应该在EntryAbility的生命周期中进行处理。

比如我现在在pages中创建两个页面,两个页面显示不同内容:

在卡片中创建两个按钮:

现在我要实现:

1、点击‘跳转Index’按钮时,跳转到Index页面,

2、点击‘跳转Second’按钮时,跳转到Second页面。

在卡片页面调用postCardAction方法,定义router事件,进行参数传递:

在EntryAbility接收对应的参数,并在onCreate和OnNewWant回调函数中进行参数的获取,并交给定义的全局变量selectPage。

onCreate回调:UIAbility第一次创建时,回调函数;

onNewWant回调:UIAbility已存在被再次拉起时,调用方法。

在onNewWant回调函数中,定义windowStage的值不为空时,调用onWindowStageCreate方法,重新加载当前窗口。

在onWindowStageCreate方法中,利用switch语句判断获取参数的值,并根据不同值跳转不同页面,并判断,给的全局变量currentWindowStage是否为空(第一次创建UIAbility时变量为空),为空则把当前onWindowStageCreate方法中的参数windowStage进行传递赋值。

EntryAbility.ets完整代码:

import { AbilityConstant, ConfigurationConstant, UIAbility, Want } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { window } from '@kit.ArkUI';
import { formBindingData, formProvider } from '@kit.FormKit';

const DOMAIN = 0x0000;

export default class EntryAbility extends UIAbility {
  // 定义全局变量
  private selectPage: string = ''
  private currentWindowStage: window.WindowStage | null = null

  // 第一次创建UIAbility是会调用onCreate回调函数,创建windowStage舞台
  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    this.context.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET);
    hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onCreate');
    if (want.parameters?.params) {
      //   获取拉起卡片router事件中传递的params参数
      let params: Record<string, Object> = JSON.parse(want.parameters?.params as string)
      this.selectPage = params.targetPage as string
    }
  }

  // 当前UIAbility已存在,再次拉起当前EntryAbility时,会调用onNewWant回调
  onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    if (want.parameters?.params) {
      let params: Record<string, Object> = JSON.parse(want.parameters?.params as string)
      this.selectPage = params.targetPage as string
    }
    // 收到router事件后秒如果当前WindowStage存在,重新调用onWindowStageCreate创建windowStage
    if (this.currentWindowStage !== null) {
      this.onWindowStageCreate(this.currentWindowStage)
    }
  }

  onDestroy(): void {
    hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onDestroy');
  }

  onWindowStageCreate(windowStage: window.WindowStage): void {
    // Main window is created, set main page for this ability
    hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onWindowStageCreate');
    let targetPage: string
    switch (this.selectPage) {
      case 'index':
        targetPage = 'pages/Index'
        break
      case 'second':
        targetPage = 'pages/Second'
        break
      default:
        targetPage = 'pages/Index'
        break
    }
    // 第一次创建windowStage时,把回调函数中windowStage参数传递给定义的全局变量currentWindowStage
    if (this.currentWindowStage===null){
      this.currentWindowStage=windowStage
    }
    windowStage.loadContent(targetPage, (err) => {
      if (err.code) {
        hilog.error(DOMAIN, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err));
        return;
      }
      hilog.info(DOMAIN, 'testTag', 'Succeeded in loading the content.');
    });
  }

  onWindowStageDestroy(): void {
    // Main window is destroyed, release UI related resources
    hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onWindowStageDestroy');
  }

  onForeground(): void {
    // Ability has brought to foreground
    hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onForeground');
  }

  onBackground(): void {
    // Ability has back to background
    hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onBackground');
  }
}

(2)message事件

message事件基本就是卡片页面和卡片提供方EntryFormAbility进行交互的过程。例如:现在我要给卡片页面中的Button('+1')设置一个点击事件,点击让count里面的文本数量+1,使用message事件拉起EntryFromAbility,在EntryFormAbility中实现这个功能。

在卡片页面,设置文本,并设置按钮点击事件,这里需要改变的数据需要由@LocalStorageProp装饰,才能由EntryAbility中传递到当前页面,发生数据改变。

在EntryFormAbility中的onFormEvent回调(专门用来处理message事件的回调函数)中,接收传递过来的数据,并对数据实现+1效果,然后把改变后的值由formProvider传递给卡片。

卡片页面代码WidgetTestCard.ets:

let storage = new LocalStorage()

@Entry(storage)
@Component
export struct WidgetTestCard {
  @LocalStorageProp('cou') count: number = 0
  build() {
    Column() {
      Column() {
        Button('跳转Index').fontSize(14)
          .onClick(() => {
            postCardAction(this, {
              action: 'router',
              abilityName: 'EntryAbility',
              params: {
                targetPage: 'index'
              }
            })
          })
        Button('跳转Second').fontSize(14)
          .onClick(() => {
            postCardAction(this, {
              action: 'router',
              abilityName: 'EntryAbility',
              params: {
                targetPage: 'second'
              }
            })
          })
      }

      Row() {
        Text(`总数:${this.count}`).fontSize(20)
        Button('+1').fontSize(12)
          .onClick(() => {
            postCardAction(this, {
              action: 'message',
              params: {
                count: this.count,
              }
            })
          })
      }
    }
    .height('100%')
    .width('100%')
  }
}

EntryFormAbility.ets代码:

import { formBindingData, FormExtensionAbility, formInfo, formProvider } from '@kit.FormKit';
import { Want } from '@kit.AbilityKit';

export default class EntryFormAbility extends FormExtensionAbility {
  onAddForm(want: Want) {
    // Called to return a FormBindingData object.
    const formData = '';
    return formBindingData.createFormBindingData(formData);
  }



  onCastToNormalForm(formId: string) {
    // Called when the form provider is notified that a temporary form is successfully
    // converted to a normal form.
  }

  onUpdateForm(formId: string) {
    // Called to notify the form provider to update a specified form.
  }

  onFormEvent(formId: string, message: string) {
    // Called when a specified message event defined by the form provider is triggered.
    interface paramsInterface{
      count:number
    }
    let params=JSON.parse(message) as paramsInterface
    let formData:Record<string,Object>={
      'cou':params.count+1
    }
    const formInfo=formBindingData.createFormBindingData(formData)
    formProvider.updateForm(formId,formInfo)
  }

  onRemoveForm(formId: string) {
    // Called to notify the form provider that a specified form has been destroyed.
  }

  onAcquireFormState(want: Want) {
    // Called to return a {@link FormState} object.
    return formInfo.FormState.READY;
  }
}

定时和定点刷新不再说明,有需要请看我上一篇文章卡片......

(3)call事件

call事件也是拉起UIAbility,它和router事件、message事件不一样的是:

message:message卡片更新自己卡片页面数据,不会拉起应用,一定时间内(10s)如果没有更新的事件,会被销毁,适合做不太耗时的任务。

call:在服务卡片拉起应用的功能,例如音乐卡片界面点击暂停/播放上一首/下一首场景下一般使用call事件。只在onCreate回调中。

router:只是拉起UIAbility,可以在首次和非首次进行数据重新渲染

1)当拉起事件,"action"为"call" 类型时,

所以需要在module.json配置文件中获取权限

"requestPermissions": [
      {
        "name": "ohos.permission.KEEP_BACKGROUND_RUNNING"
      }
    ],

2)在卡片页面设置call事件

卡片WidgetTestCard.ets代码:

let storage = new LocalStorage()

@Entry(storage)
  @Component
  export struct WidgetTestCard {
    @LocalStorageProp('cou') count: number = 0
    @LocalStorageProp('num') num: number = 666
    @LocalStorageProp('formId') formId: string='12345678'
    build() {
      Column() {
        Column() {
          Button('跳转Index').fontSize(14)
            .onClick(() => {
              postCardAction(this, {
                action: 'router',
                abilityName: 'EntryAbility',
                params: {
                  targetPage: 'index'
                }
              })
            })
          Button('跳转Second').fontSize(14)
            .onClick(() => {
              postCardAction(this, {
                action: 'router',
                abilityName: 'EntryAbility',
                params: {
                  targetPage: 'second'
                }
              })
            })
        }

        Row() {
          Text(`总数:${this.count}`).fontSize(20)
          Button('+1').fontSize(12)
            .onClick(() => {
              postCardAction(this, {
                action: 'message',
                params: {
                  count: this.count,
                }
              })
            })
        }
        Row() {
          Text(`总数:${this.num}`).fontSize(20)
          Button('+10').fontSize(12)
            .onClick(() => {
              postCardAction(this, {
                action: 'call',
                abilityName:'EntryAbility',
                params: {
                  method:'funcA',
                  formId:this.formId,
                  num: this.num,
                }
              })
              console.log('bbb2',`formId:${JSON.stringify(this.formId)}`)
            })
        }
      }
      .height('100%')
        .width('100%')
    }
  }

3)在卡片EntryFormAbility中,在onAddForm(卡片创建时调用)回调函数中,通过want获取当前创建卡片的唯一key值,并把这个key值作为formId,传递给卡片页面。

EntryFormAbility.ets页面代码:

import { formBindingData, FormExtensionAbility, formInfo, formProvider } from '@kit.FormKit';
import { Want } from '@kit.AbilityKit';

export default class EntryFormAbility extends FormExtensionAbility {
  onAddForm(want: Want) {
    // 获取当前添加到桌面的卡片id
    let formId: string = want.parameters![formInfo.FormParam.IDENTITY_KEY].toString()
    console.log('bbb1',`formId:${JSON.stringify(formId)}`)

    // 准备一个临时数据结构 存放formId 和数字
    interface FromData {
      formId: string,
    }
    // 准备初始化数据
    let formData: FromData = {
      'formId': formId,
    }
    // 返回给卡片
    return formBindingData.createFormBindingData(formData);
  }



  onCastToNormalForm(formId: string) {
    // Called when the form provider is notified that a temporary form is successfully
    // converted to a normal form.
  }

  onUpdateForm(formId: string) {
    // Called to notify the form provider to update a specified form.
  }

  onFormEvent(formId: string, message: string) {
    // Called when a specified message event defined by the form provider is triggered.
    interface paramsInterface{
      count:number
    }
    let params=JSON.parse(message) as paramsInterface
    let formData:Record<string,Object>={
      'cou':params.count+1
    }
    const formInfo=formBindingData.createFormBindingData(formData)
    formProvider.updateForm(formId,formInfo)
  }

  onRemoveForm(formId: string) {
    // Called to notify the form provider that a specified form has been destroyed.
  }

  onAcquireFormState(want: Want) {
    // Called to return a {@link FormState} object.
    return formInfo.FormState.READY;
  }
}

4)在EntryAbility的onCreate方法中,使用callee监控call事件,并执行对应回调方法,这里的data数据是rpc.MessageSequence类型数据,进程间通信数据类型,从data中读取call事件传递过来的参数。

EntryAbility代码:

import { AbilityConstant, ConfigurationConstant, UIAbility, Want } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { window } from '@kit.ArkUI';
import { formBindingData, formProvider } from '@kit.FormKit';
import { rpc } from '@kit.IPCKit';

const DOMAIN = 0x0000;

export default class EntryAbility extends UIAbility {
  // 定义全局变量
  private selectPage: string = ''
  private currentWindowStage: window.WindowStage | null = null

  // 第一次创建UIAbility是会调用onCreate回调函数,创建windowStage舞台
  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    this.context.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET);
    hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onCreate');
    if (want.parameters?.params) {
      //   获取拉起卡片router事件中传递的params参数
      let params: Record<string, Object> = JSON.parse(want.parameters?.params as string)
      this.selectPage = params.targetPage as string
    }
    //call事件
    try {
      this.callee.on('funcA',(data)=>{
        // 定义获取参数的结构
        interface paramsInterface{
          formId:string
          num:number
        }
        // 获取参数
        let params=JSON.parse(data.readString()) as paramsInterface
        const formData:Record<string,Object>={
          'num':params.num+10
        }
        console.log('bbb3',`formId:${JSON.stringify(params.formId)}`)
        let formInfo=formBindingData.createFormBindingData(formData)
        formProvider.updateForm(params.formId,formInfo)
        return null
      })
    }catch (err) {
      console.error(`onCreate failed,err:${JSON.stringify(err)}`)
    }
  }

  // 当前UIAbility已存在,再次拉起当前EntryAbility时,会调用onNewWant回调
  onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    if (want.parameters?.params) {
      let params: Record<string, Object> = JSON.parse(want.parameters?.params as string)
      this.selectPage = params.targetPage as string
    }
    // 收到router事件后秒如果当前WindowStage存在,重新调用onWindowStageCreate创建windowStage
    if (this.currentWindowStage !== null) {
      this.onWindowStageCreate(this.currentWindowStage)
    }
  }

  onDestroy(): void {
    hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onDestroy');
  }

  onWindowStageCreate(windowStage: window.WindowStage): void {
    // Main window is created, set main page for this ability
    hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onWindowStageCreate');
    let targetPage: string
    switch (this.selectPage) {
      case 'index':
        targetPage = 'pages/Index'
        break
      case 'second':
        targetPage = 'pages/Second'
        break
      default:
        targetPage = 'pages/Index'
        break
    }
    // 第一次创建windowStage时,把回调函数中windowStage参数传递给定义的全局变量currentWindowStage
    if (this.currentWindowStage===null){
      this.currentWindowStage=windowStage
    }
    windowStage.loadContent(targetPage, (err) => {
      if (err.code) {
        hilog.error(DOMAIN, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err));
        return;
      }
      hilog.info(DOMAIN, 'testTag', 'Succeeded in loading the content.');
    });
  }

  onWindowStageDestroy(): void {
    // Main window is destroyed, release UI related resources
    hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onWindowStageDestroy');
  }

  onForeground(): void {
    // Ability has brought to foreground
    hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onForeground');
  }

  onBackground(): void {
    // Ability has back to background
    hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onBackground');
  }
}

gitee代码地址:harmonyOS: 鸿蒙项目备份gitee,方便协同查看bug... - Gitee.com

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值