HarmonyOS 鸿蒙Next小部件(Widget)数据同步

HarmonyOS 鸿蒙Next小部件(Widget)数据同步 如何在 HarmonyOS 应用中实现桌面卡片(Form)?如何使用 @kit.FormKit 实现卡片能力?如何将应用数据同步到卡片?如何实现卡片的实时更新?卡片如何与主应用进行数据交互?(问题来源项目案例整理:https://github.com/heqiyuan35-creator/HydroQuiz.git

3 回复

HarmonyOS 的 FormKit 提供了完整的卡片能力,通过 FormExtensionAbility 和 formProvider 实现卡片与主应用的数据交互。

卡片扩展能力实现(基于 @kit.FormKit)

import { formBindingData, FormExtensionAbility, formInfo, formProvider } from '[@kit](/user/kit).FormKit';
import { Want } from '[@kit](/user/kit).AbilityKit';
import { BusinessError } from '[@kit](/user/kit).BasicServicesKit';
import { preferences } from '[@kit](/user/kit).ArkData';
import { hilog } from '[@kit](/user/kit).PerformanceAnalysisKit';

export default class EntryFormAbility extends FormExtensionAbility {
  // 卡片添加时触发(HarmonyOS 生命周期)
  onAddForm(want: Want): formBindingData.FormBindingData {
    const formId = want.parameters?.[formInfo.FormParam.IDENTITY_KEY] as string;
    const formName = want.parameters?.[formInfo.FormParam.NAME_KEY] as string;
    // 保存 formId 到 Preferences(用于后续更新)
    this.saveFormId(formId);
    // 根据卡片名称返回不同数据
    if (formName === 'StudyProgress') {
      return this.getStudyProgressData();
    } else {
      return this.getStudyReminderData();
    }
  }
  // 卡片更新时触发
  onUpdateForm(formId: string): void {
    this.updateFormData(formId);
  }
  // 卡片事件触发(用户交互)
  onFormEvent(formId: string, message: string): void {
    try {
      const msgObj = JSON.parse(message) as Record<string, string>;
      const action = msgObj['action'];
      if (action === 'refresh') {
        // 刷新卡片数据
        this.updateFormData(formId);
      }
    } catch (error) {
      hilog.error(0xFF00, 'EntryFormAbility', `Parse message error`);
    }
  }
  // 卡片移除时触发
  onRemoveForm(formId: string): void {
    this.removeFormId(formId);
  }
  // 保存 formId(使用 HarmonyOS Preferences API)
  private saveFormId(formId: string): void {
    try {
      const pref = preferences.getPreferencesSync(this.context, {
        name: 'hydro_quiz_prefs'
      });
      const formIdsStr = pref.getSync('formIds', '') as string;
      const formIds = formIdsStr ? formIdsStr.split(',') : [];
      if (!formIds.includes(formId)) {
        formIds.push(formId);
        pref.putSync('formIds', formIds.join(','));
        pref.flush();
      }
    } catch (error) {
      hilog.error(0xFF00, 'EntryFormAbility', `Save formId error`);
    }
  }
  // 获取卡片数据(使用 formBindingData)
  private getStudyProgressData(): formBindingData.FormBindingData {
    const rawData = this.loadStudyData();
    const progressData = {
      todayCount: rawData.todayCount,
      targetCount: rawData.targetCount,
      correctCount: rawData.correctCount,
      wrongCount: rawData.wrongCount,
      accuracy: rawData.todayCount > 0
        ? Math.round((rawData.correctCount / rawData.todayCount) * 100)
        : 0,
      progressPercent: rawData.targetCount > 0
        ? Math.min(100, Math.round((rawData.todayCount / rawData.targetCount) * 100))
        : 0
    };
    // 使用 HarmonyOS API 创建绑定数据
    return formBindingData.createFormBindingData(progressData);
  }
}

主应用数据同步服务(使用 @kit.FormKit)

import { formBindingData, formProvider } from '[@kit](/user/kit).FormKit';
import { preferences } from '[@kit](/user/kit).ArkData';
import { common } from '[@kit](/user/kit).AbilityKit';
import { BusinessError } from '[@kit](/user/kit).BasicServicesKit';

class WidgetDataService {
  private context: common.UIAbilityContext | null = null;
  // 同步数据到 Preferences 并更新卡片
  async syncData(data: WidgetData): Promise<void> {
    if (!this.context) return;
    try {
      // 使用 HarmonyOS Preferences API
      const pref = preferences.getPreferencesSync(this.context, {
        name: 'hydro_quiz_prefs'
      });
      pref.putSync('todayAnswerCount', data.todayCount);
      pref.putSync('dailyTarget', data.targetCount);
      pref.putSync('todayCorrectCount', data.correctCount);
      pref.putSync('todayWrongCount', data.wrongCount);
      pref.putSync('isCheckedInToday', data.isCheckedIn);
      pref.putSync('streakDays', data.streakDays);
      await pref.flush();
      // 更新所有卡片
      await this.updateAllWidgets(data);
    } catch (error) {
      Logger.error('Failed to sync data', error as Error);
    }
  }
  // 更新所有卡片(使用 HarmonyOS formProvider API)
  private async updateAllWidgets(data: WidgetData): Promise<void> {
    if (!this.context) return;
    try {
      const pref = preferences.getPreferencesSync(this.context, {
        name: 'hydro_quiz_prefs'
      });
      const formIdsStr = pref.getSync('formIds', '') as string;
      const formIds = formIdsStr.split(',').filter(id => id.length > 0);
      // 构建更新数据
      const updateData = {
        todayCount: data.todayCount,
        targetCount: data.targetCount,
        correctCount: data.correctCount,
        wrongCount: data.wrongCount,
        accuracy: data.todayCount > 0
          ? Math.round((data.correctCount / data.todayCount) * 100)
          : 0,
        progressPercent: data.targetCount > 0
          ? Math.min(100, Math.round((data.todayCount / data.targetCount) * 100))
          : 0
      };
      // 使用 HarmonyOS formBindingData API
      const formData = formBindingData.createFormBindingData(updateData);
      // 使用 HarmonyOS formProvider API 更新卡片
      for (const formId of formIds) {
        formProvider.updateForm(formId, formData)
          .then(() => {
            Logger.info(`Updated form: ${formId}`);
          })
          .catch((err: BusinessError) => {
            Logger.error(`Failed to update form ${formId}: ${err.message}`);
          });
      }
    } catch (error) {
      Logger.error('Failed to update widgets', error as Error);
    }
  }
}

卡片页面实现(ArkUI 组件)

@Entry
@Component
struct StudyProgressCard {
  @State todayCount: number = 0;
  @State targetCount: number = 20;
  @State progressPercent: number = 0;
  aboutToAppear(): void {
    // 从 Preferences 读取数据(使用 HarmonyOS API)
    const pref = preferences.getPreferencesSync(getContext(this), {
      name: 'hydro_quiz_prefs'
    });
    this.todayCount = pref.getSync('todayAnswerCount', 0) as number;
    this.targetCount = pref.getSync('dailyTarget', 20) as number;
    this.progressPercent = this.targetCount > 0
      ? Math.min(100, Math.round((this.todayCount / this.targetCount) * 100))
      : 0;
  }
  build() {
    Column() {
      Text('今日学习')
        .fontSize(14)
        .fontColor('#333333')

      Text(`${this.todayCount}/${this.targetCount}`)
        .fontSize(24)
        .fontWeight(FontWeight.Bold)
        .margin({ top: 8 })
      // HarmonyOS Progress 组件
      Progress({ value: this.progressPercent, total: 100 })
        .width('100%')
        .height(8)
        .margin({ top: 12 })
    }
    .padding(16)
    .width('100%')
    .height('100%')
  }
}

更多关于HarmonyOS 鸿蒙Next小部件(Widget)数据同步的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


鸿蒙Next小部件数据同步主要通过ArkTS声明式UI框架实现。数据更新使用状态管理机制,如@State@Prop@Link等装饰器,驱动UI自动刷新。小部件与主应用共享同一数据源,通过AppStorage或LocalStorage进行跨组件状态同步。系统提供持久化存储能力,确保数据一致性。小部件支持定时更新和事件触发更新两种模式,具体配置在配置文件中定义。

在HarmonyOS Next中,桌面卡片(Form)通过@kit.FormKit提供能力。以下是关键实现步骤:

1. 创建卡片

  • entry/src/main/ets/entryformability/目录下创建FormAbility。
  • 使用FormExtensionAbility生命周期方法管理卡片(如onAddForm创建,onRemoveForm销毁)。
  • 通过formBindingData.createFormBindingData()绑定初始数据。

2. 数据同步与更新

  • 主动更新:主应用调用formProvider.updateForm()推送数据到卡片。
  • 定时更新:配置updateDuration实现周期更新。
  • 事件触发更新:通过postCardAction()发送自定义事件到FormAbility触发更新。

3. 卡片与主应用交互

  • 卡片通过postCardAction()触发主应用指定的want(如打开应用页面)。
  • 主应用通过formProvider接口管理卡片数据。

4. 实时更新关键代码示例

// 主应用更新卡片数据
import formProvider from '@ohos.app.form.formProvider';

let formId = '123456'; // 实际formId
let data = {
  temperature: '26℃',
  city: '北京'
};
let formData = formBindingData.createFormBindingData(data);
formProvider.updateForm(formId, formData).catch(err => {
  console.error(`Failed to update form. Code: ${err.code}, message: ${err.message}`);
});

// 卡片端接收事件更新
import FormExtensionAbility from '@ohos.app.form.FormExtensionAbility';

export default class FormAbility extends FormExtensionAbility {
  onFormEvent(formId, message) {
    // 根据message事件类型,获取新数据并调用updateForm
  }
}

5. 项目适配建议 参考你提供的HydroQuiz项目,需要:

  • module.json5中声明extensionAbilitiesFormExtensionAbility
  • 将业务数据(如答题结果)通过formBindingData封装后同步。
  • 利用卡片onFormEvent()处理用户交互(如点击刷新),回调主应用获取最新数据。

通过以上方式,可实现卡片数据动态同步、低功耗定时更新及双向交互能力。注意卡片生命周期独立于主应用,需通过FormExtensionAbility单独管理资源。

回到顶部