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
相关推荐
汽车仪器仪表相关领域38 分钟前
PSN-1:氮气加速 + 空燃比双控仪 ——NOS 系统的 “安全性能双管家”
大数据·linux·服务器·人工智能·功能测试·汽车·可用性测试
残花月伴40 分钟前
天机学堂-day3(学习计划和进度)
java
周倦岚1 小时前
【HarmonyOS】用户通知服务
华为·harmonyos
杰 .1 小时前
Linux vim
linux·服务器
小宝哥Code1 小时前
UE5在布局自定义上的UE4ClassicLayout.ini文件源码解读分析
java·ue5·ue4
晚霞的不甘1 小时前
开源鸿蒙(OpenHarmony)实战入门:从 Hello World 到 UI 交互设计
ui·开源·harmonyos
残花月伴1 小时前
天机学堂-day2(我的课表)
java
汽车仪器仪表相关领域1 小时前
PSB-1:安全增压与空燃比双监控仪表 - 高性能引擎的 “双重安全卫士“
java·人工智能·功能测试·单元测试·汽车·可用性测试·安全性测试
c***21291 小时前
删除文件夹,被提示“需要来自 TrustedInstaller 的权限。。。”的解决方案
java