Harmony os 卡片传递消息给应用(message 事件)详细介绍

卡片传递消息给应用(message 事件)

鸿蒙第四期活动

1. message 事件是什么?

场景:

动态卡片上有一个「刷新」「切换状态」「点赞」之类的按钮,不想跳转页面,也不需要后台长时间运行 UIAbility,只是想:

  • 点一下按钮 → 通知应用「我被点了」;
  • 应用(FormExtensionAbility)根据这条消息更新数据;
  • 然后刷新卡片 UI

这种交互就用 postCardActionmessage 事件

  • 发送方:卡片页面(WidgetCard.ets)
  • 处理方:FormExtensionAbility(EntryFormAbility.ets)onFormEvent(formId, message)
  • 刷新方式:在 onFormEvent 里调用 formProvider.updateForm(formId, formBindingData) 更新卡片绑定数据

2. 整体流程(顺序记一下)

  1. 卡片创建时
    • EntryFormAbility.onAddForm() 通过 formBindingData 把初始数据(如 titledetail)传给卡片;
    • 卡片用 @LocalStorageProp 接收这些数据并显示。
  2. 用户点击卡片按钮
    • 在按钮的 onClick 中调用 postCardAction(this, { action: 'message', params: {...} })
    • 这会触发 FormExtensionAbility 的 onFormEvent(formId, message) 回调。
  3. FormExtensionAbility.onFormEvent 收到消息
    • formId:是哪张卡片;
    • message:卡片发来的参数(字符串,通常是 JSON / 简单值);
    • 在里面根据业务生成新的卡片数据对象,调用 updateForm(formId, newBinding) 刷新卡片内容。
  4. 卡片 UI 自动刷新
    • 卡片结构中绑定的 LocalStorage 字段(@LocalStorageProp('title') 等)会被系统更新;
    • 页面自动重新渲染,不需要你手动 setState。

3. 基础示例(官方风格版本整理)

3.1 卡片页面:发送 message 事件

ts 复制代码
// 存储卡片数据的 LocalStorage
let storageUpdateByMsg = new LocalStorage();

@Entry(storageUpdateByMsg)
@Component
struct UpdateByMessageCard {
  @LocalStorageProp('title') title: ResourceStr = $r('app.string.default_title');
  @LocalStorageProp('detail') detail: ResourceStr = $r('app.string.DescriptionDefault');

  build() {
    Column() {
      // 上半部分:显示标题和详情
      Column() {
        Text(this.title)
          .fontColor('#FFFFFF')
          .opacity(0.9)
          .fontSize(14)
          .margin({ top: '8%', left: '10%' })

        Text(this.detail)
          .fontColor('#FFFFFF')
          .opacity(0.6)
          .fontSize(12)
          .margin({ top: '5%', left: '10%' })
      }
      .width('100%')
      .height('50%')
      .alignItems(HorizontalAlign.Start)

      // 下半部分:一个"刷新"按钮
      Row() {
        Button() {
          Text($r('app.string.update'))
            .fontColor('#45A6F4')
            .fontSize(12)
        }
        .width(120)
        .height(32)
        .margin({ top: '30%', bottom: '10%' })
        .backgroundColor('#FFFFFF')
        .borderRadius(16)
        .onClick(() => {
          // 关键:发送 message 事件
          postCardAction(this, {
            action: 'message',
            params: { msgTest: 'messageEvent' }  // 自定义参数
          });
        })
      }
      .width('100%')
      .height('40%')
      .justifyContent(FlexAlign.Center)
    }
    .width('100%')
    .height('100%')
    .alignItems(HorizontalAlign.Start)
    .backgroundImage($r('app.media.CardEvent'))         // 背景图
    .backgroundImageSize(ImageSize.Cover)
  }
}

要点:

  • @Entry(storage) + @LocalStorageProp() 管住卡片显示的数据;
  • 按钮点击时只需要 action: 'message' 即可,无需填 abilityName,默认会唤起当前卡片的 FormExtensionAbility
  • params 是你自定义的消息内容(可以是简单字符串,也可以是结构化对象序列化后的字符串)。

3.2 FormExtensionAbility:接收 message & 更新卡片

ts 复制代码
// EntryFormAbility.ets
import { BusinessError } from '@kit.BasicServicesKit';
import { formBindingData, FormExtensionAbility, formProvider } from '@kit.FormKit';
import { hilog } from '@kit.PerformanceAnalysisKit';

const TAG: string = 'EntryFormAbility';
const DOMAIN_NUMBER: number = 0xFF00;

export default class EntryFormAbility extends FormExtensionAbility {
  // 当卡片调用 postCardAction(..., { action: 'message' }) 时会回调到这里
  onFormEvent(formId: string, message: string): void {
    hilog.info(
      DOMAIN_NUMBER,
      TAG,
      `FormAbility onFormEvent, formId = ${formId}, message: ${JSON.stringify(message)}`
    );

    // 1. 根据 message 自定义业务逻辑,这里简单演示更新 title/detail
    class FormDataClass {
      title: string = 'Title Update.';                 // 对应卡片上的 title
      detail: string = 'Description update success.'; // 对应卡片上的 detail
    }

    // 2. 创建新的卡片数据绑定对象
    let formData = new FormDataClass();
    let formInfo: formBindingData.FormBindingData =
      formBindingData.createFormBindingData(formData);

    // 3. 调用 updateForm 刷新卡片
    formProvider.updateForm(formId, formInfo).then(() => {
      hilog.info(DOMAIN_NUMBER, TAG, 'FormAbility updateForm success.');
    }).catch((error: BusinessError) => {
      hilog.info(
        DOMAIN_NUMBER,
        TAG,
        `Operation updateForm failed. Cause: ${JSON.stringify(error)}`
      );
    })
  }

  // ... 还会有 onAddForm / onRemoveForm / onUpdateForm 等生命周期
}

要点:

  • formId:告诉你是哪一张卡片触发了事件;
  • message:就是卡片传进来的 params 内容,通常是字符串。如果你传的是 JSON,可以 JSON.parse(message) 再按字段处理;
  • updateForm(formId, formBindingData)刷新卡片 UI 的关键一步;
  • FormDataClass 里字段名要和卡片 @LocalStorageProp('xxx') 一一对应。

4. 在官方示例的基础上稍微升级一个实战版

4.1 卡片页面:发送 "toggle" 指令

ts 复制代码
let storageUpdateByMsg = new LocalStorage();

@Entry(storageUpdateByMsg)
@Component
struct UpdateByMessageCard {
  @LocalStorageProp('title') title: string = '当前状态:未学习';
  @LocalStorageProp('detail') detail: string = '点击下方按钮开始学习打卡';

  build() {
    Column() {
      Column() {
        Text(this.title)
          .fontColor('#FFFFFF')
          .opacity(0.9)
          .fontSize(16)
          .margin({ top: '8%', left: '10%' })

        Text(this.detail)
          .fontColor('#FFFFFF')
          .opacity(0.6)
          .fontSize(12)
          .margin({ top: '5%', left: '10%' })
      }
      .width('100%')
      .height('50%')
      .alignItems(HorizontalAlign.Start)

      Row() {
        Button() {
          Text('切换状态')
            .fontColor('#45A6F4')
            .fontSize(12)
        }
        .width(120)
        .height(32)
        .margin({ top: '30%', bottom: '10%' })
        .backgroundColor('#FFFFFF')
        .borderRadius(16)
        .onClick(() => {
          // 发送一个"toggle"操作
          postCardAction(this, {
            action: 'message',
            params: { op: 'toggleStatus' }
          });
        })
      }
      .width('100%')
      .height('40%')
      .justifyContent(FlexAlign.Center)
    }
    .width('100%')
    .height('100%')
    .backgroundImage($r('app.media.CardEvent'))
    .backgroundImageSize(ImageSize.Cover)
  }
}

4.2 FormExtensionAbility:根据 op 切换不同内容

ts 复制代码
import { BusinessError } from '@kit.BasicServicesKit';
import { formBindingData, FormExtensionAbility, formProvider } from '@kit.FormKit';
import { hilog } from '@kit.PerformanceAnalysisKit';

const TAG: string = 'EntryFormAbility';
const DOMAIN_NUMBER: number = 0xFF00;

// 这里简单用内存 map 模拟每个 formId 的状态(生产环境一般用持久化存储)
const statusMap: Map<string, boolean> = new Map();

export default class EntryFormAbility extends FormExtensionAbility {
  onFormEvent(formId: string, message: string): void {
    hilog.info(
      DOMAIN_NUMBER,
      TAG,
      `onFormEvent, formId = ${formId}, raw message = ${JSON.stringify(message)}`
    );

    let payload: { op?: string } = {};
    try {
      payload = JSON.parse(message);     // 如果 params 本身就是对象字符串,可以 JSON.parse
    } catch (e) {
      // 如果 message 不是 JSON,可以自己按格式解析,这里简单忽略
      hilog.info(DOMAIN_NUMBER, TAG, `parse message failed: ${JSON.stringify(e)}`);
    }

    if (payload.op === 'toggleStatus') {
      // 1. 读取当前状态(默认 false 表示"未学习")
      const oldStatus = statusMap.get(formId) ?? false;
      const newStatus = !oldStatus;
      statusMap.set(formId, newStatus);

      // 2. 根据新状态生成不同的显示内容
      class FormDataClass {
        title: string;
        detail: string;

        constructor(status: boolean) {
          if (status) {
            this.title = '当前状态:已学习 ✅';
            this.detail = '继续保持,每天都来打卡吧!';
          } else {
            this.title = '当前状态:未学习 ❌';
            this.detail = '点击按钮开始今天的学习打卡。';
          }
        }
      }

      const formData = new FormDataClass(newStatus);
      const binding = formBindingData.createFormBindingData(formData);

      // 3. 刷新卡片
      formProvider.updateForm(formId, binding).then(() => {
        hilog.info(DOMAIN_NUMBER, TAG, 'updateForm success.');
      }).catch((error: BusinessError) => {
        hilog.error(
          DOMAIN_NUMBER,
          TAG,
          `updateForm failed. Cause: ${JSON.stringify(error)}`
        );
      });
    } else {
      hilog.info(DOMAIN_NUMBER, TAG, 'Unknown op, ignore.');
    }
  }
}

5. 和 call 事件的简单对比(帮你在脑子里分清)

特性 message call
触发方式 postCardAction(..., {action:'message'}) postCardAction(..., {action:'call'})
目标 FormExtensionAbility.onFormEvent 指定 UIAbility(比如 XXXEntryAbility
是否拉起 UIAbility ❌(只是激活 FormExtensionAbility) ✅(后台启动某个 UIAbility)
场景 刷新卡片、轻量交互 后台执行复杂逻辑、长任务
权限要求 无特殊权限要求 需要 KEEP_BACKGROUND_RUNNING
相关推荐
前端Hardy2 天前
面试官:JS数组的常用方法有哪些?这篇总结让你面试稳了!
javascript·面试
yuki_uix2 天前
Props、Context、EventBus、状态管理:组件通信方案选择指南
前端·javascript·react.js
日月云棠2 天前
各版本JDK对比:JDK 25 特性详解
java
全栈老石2 天前
手写无限画布4 —— 从视觉图元到元数据对象
前端·javascript·canvas
用户8307196840822 天前
Spring Boot 项目中日期处理的最佳实践
java·spring boot
JavaGuide2 天前
Claude Opus 4.6 真的用不起了!我换成了国产 M2.5,实测真香!!
java·spring·ai·claude code
Leon2 天前
新手引导 intro.js 的使用
前端·javascript·vue.js
IT探险家2 天前
Java 基本数据类型:8 种原始类型 + 数组 + 6 个新手必踩的坑
java
花花无缺2 天前
搞懂new 关键字(构造函数)和 .builder() 模式(建造者模式)创建对象
java
用户908324602732 天前
Spring Boot + MyBatis-Plus 多租户实战:从数据隔离到权限控制的完整方案
java·后端