HarmonyOS鸿蒙Next FormKit卡片开发如何实现数据刷新?桌面小组件开发指南

HarmonyOS鸿蒙Next FormKit卡片开发如何实现数据刷新?桌面小组件开发指南 HarmonyOS 5.0,DevEco Studio 5.0

  • 需要开发桌面小组件(卡片),显示应用数据
  • 不清楚如何实现卡片数据的定时刷新
  • 希望了解卡片与应用之间的数据通信方式

希望了解HarmonyOS FormKit卡片开发的完整流程,包括卡片创建、数据绑定和刷新机制

3 回复

1. 卡片配置文件

form_config.json 中配置卡片:

{
  "forms": [
    {
      "name": "FishCard",
      "displayName": "$string:fish_card_name",
      "description": "$string:fish_card_desc",
      "src": "./ets/widget/pages/FishCard.ets",
      "uiSyntax": "arkts",
      "window": {
        "designWidth": 720,
        "autoDesignWidth": true
      },
      "colorMode": "auto",
      "isDefault": true,
      "updateEnabled": true,
      "scheduledUpdateTime": "10:30",
      "updateDuration": 1,
      "defaultDimension": "2*2",
      "supportDimensions": ["2*2", "2*4", "4*4"]
    }
  ]
}

2. 卡片UI实现

// widget/pages/FishCard.ets

@Entry
@Component
struct FishCard {
  @LocalStorageProp('fishCount') fishCount: number = 0
  @LocalStorageProp('lastFeedTime') lastFeedTime: string = '--'
  @LocalStorageProp('waterTemp') waterTemp: number = 0

  build() {
    Column({ space: 8 }) {
      // 标题
      Row() {
        Text('🐟')
          .fontSize(20)
        Text('我的鱼缸')
          .fontSize(16)
          .fontWeight(FontWeight.Bold)
          .fontColor('#ffffff')
          .margin({ left: 8 })
      }
      .width('100%')
     
      // 数据展示
      Row({ space: 16 }) {
        // 鱼数量
        Column() {
          Text(this.fishCount.toString())
            .fontSize(24)
            .fontWeight(FontWeight.Bold)
            .fontColor('#36e27b')
          Text('鱼数量')
            .fontSize(10)
            .fontColor('#9eb7a8')
        }
       
        // 水温
        Column() {
          Text(`${this.waterTemp}°C`)
            .fontSize(24)
            .fontWeight(FontWeight.Bold)
            .fontColor('#3b82f6')
          Text('水温')
            .fontSize(10)
            .fontColor('#9eb7a8')
        }
      }
     
      // 上次喂食时间
      Text(`上次喂食: ${this.lastFeedTime}`)
        .fontSize(12)
        .fontColor('#9eb7a8')
    }
    .width('100%')
    .height('100%')
    .padding(12)
    .backgroundColor('#1C2E24')
    .borderRadius(16)
  }
}

3. 卡片EntryFormAbility

// entryformability/EntryFormAbility.ets

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

export default class EntryFormAbility extends FormExtensionAbility {

  // 卡片创建时调用
  onAddForm(want: Want): formBindingData.FormBindingData {
    const formId = want.parameters?.['ohos.extra.param.key.form_identity'] as string
    
    // 获取初始数据
    const formData = this.getFormData()
    
    // 保存卡片ID用于后续更新
    this.saveFormId(formId)
    
    return formBindingData.createFormBindingData(formData)
  }

  // 卡片更新时调用
  onUpdateForm(formId: string): void {
    const formData = this.getFormData()
    const bindingData = formBindingData.createFormBindingData(formData)
    formProvider.updateForm(formId, bindingData)
  }

  // 卡片删除时调用
  onRemoveForm(formId: string): void {
    this.removeFormId(formId)
  }

  // 获取卡片数据
  private getFormData(): Record<string, Object> {
    // 从持久化存储获取数据
    const prefs = preferences.getPreferencesSync(this.context, { name: 'fish_data' })
    
    return {
      fishCount: prefs.getSync('fishCount', 0) as number,
      lastFeedTime: prefs.getSync('lastFeedTime', '--') as string,
      waterTemp: prefs.getSync('waterTemp', 25) as number
    }
  }

  // 保存卡片ID
  private saveFormId(formId: string): void {
    const prefs = preferences.getPreferencesSync(this.context, { name: 'form_ids' })
    const ids = prefs.getSync('ids', '[]') as string
    const idArray = JSON.parse(ids) as string[]
    if (!idArray.includes(formId)) {
      idArray.push(formId)
      prefs.putSync('ids', JSON.stringify(idArray))
      prefs.flush()
    }
  }

  // 移除卡片ID
  private removeFormId(formId: string): void {
    const prefs = preferences.getPreferencesSync(this.context, { name: 'form_ids' })
    const ids = prefs.getSync('ids', '[]') as string
    const idArray = JSON.parse(ids) as string[]
    const index = idArray.indexOf(formId)
    if (index > -1) {
      idArray.splice(index, 1)
      prefs.putSync('ids', JSON.stringify(idArray))
      prefs.flush()
    }
  }
}

4. 从应用主动更新卡片

// 在应用中更新卡片数据
import { formProvider, formBindingData } from '@kit.FormKit'
import { preferences } from '@kit.ArkData'

class FormUpdateService {

  // 更新所有卡片
  static async updateAllForms(context: Context): Promise<void> {
    try {
      // 获取所有卡片ID
      const prefs = await preferences.getPreferences(context, { name: 'form_ids' })
      const ids = await prefs.get('ids', '[]') as string
      const idArray = JSON.parse(ids) as string[]
      
      // 获取最新数据
      const dataPrefs = await preferences.getPreferences(context, { name: 'fish_data' })
      const formData = {
        fishCount: await dataPrefs.get('fishCount', 0),
        lastFeedTime: await dataPrefs.get('lastFeedTime', '--'),
        waterTemp: await dataPrefs.get('waterTemp', 25)
      }
      
      // 更新每个卡片
      const bindingData = formBindingData.createFormBindingData(formData)
      for (const formId of idArray) {
        await formProvider.updateForm(formId, bindingData)
      }
      
    } catch (err) {
      console.error('更新卡片失败:', err)
    }
  }

  // 更新单个数据并刷新卡片
  static async updateFishCount(context: Context, count: number): Promise<void> {
    const prefs = await preferences.getPreferences(context, { name: 'fish_data' })
    await prefs.put('fishCount', count)
    await prefs.flush()
    
    await this.updateAllForms(context)
  }
}

// 使用示例
Button('喂食完成')
  .onClick(async () => {
    const now = new Date()
    const timeStr = `${now.getHours()}:${now.getMinutes().toString().padStart(2, '0')}`
    
    const prefs = await preferences.getPreferences(getContext(this), { name: 'fish_data' })
    await prefs.put('lastFeedTime', timeStr)
    await prefs.flush()
    
    await FormUpdateService.updateAllForms(getContext(this))
  })

5. 卡片点击跳转应用

// 卡片中添加点击事件
Column() {
  // ... 卡片内容
}
.onClick(() => {
  postCardAction(this, {
    action: 'router',
    abilityName: 'EntryAbility',
    params: {
      page: 'FishDetail'
    }
  })
})

更多关于HarmonyOS鸿蒙Next FormKit卡片开发如何实现数据刷新?桌面小组件开发指南的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


鸿蒙Next FormKit卡片数据刷新通过FormExtensionAbility实现。开发者需在onFormEvent生命周期中调用updateForm接口,传递formId和FormBindingData对象。FormBindingData封装需更新的键值对数据,支持字符串、数字等基础类型。刷新触发方式包括定时刷新、点击事件刷新和主动推送刷新。定时刷新需在config.json中配置updateDuration字段,最小间隔30分钟。

在HarmonyOS Next中,FormKit卡片(桌面小组件)的数据刷新主要依靠FormExtensionAbilityFormProvider来实现,并通过ArkTS声明式UI进行数据绑定。以下是核心流程和要点:

1. 卡片创建与数据绑定

  • 定义卡片UI:在resources/base/profile/目录下创建form_config.json,配置卡片尺寸、布局等信息。UI使用ArkTS编写,通过[@Component](/user/Component)定义组件。
  • 数据绑定:在ArkUI中使用@State@Prop@Link装饰器管理状态。卡片UI通过绑定这些状态变量来显示数据,例如:
    [@Entry](/user/Entry)
    [@Component](/user/Component)
    struct WidgetCard {
      @State data: string = '初始数据'
      
      build() {
        Text(this.data)
          .onClick(() => {
            // 点击触发更新
            this.data = '新数据'
          })
      }
    }
    

2. 数据刷新机制

  • 主动请求更新:在FormExtensionAbility中,通过formProvider.updateForm()主动向卡片推送新数据。适用于应用内事件触发(如按钮点击)。
  • 定时刷新:在form_config.json中配置updateDuration字段(单位分钟),系统会按间隔自动触发onUpdateForm回调,在此回调中更新数据。注意:为节省功耗,最短间隔通常为30分钟。
  • 被动刷新:通过formProvider.requestForm()向卡片发起数据更新请求,卡片端在onUpdateForm中响应并返回新数据。

3. 卡片与应用通信

  • 应用向卡片推数据:使用formProvider.updateForm(),将新数据打包为formBindingData对象推送至卡片。
  • 卡片向应用拉数据:在卡片UI中触发事件(如点击),通过postFormAction()向应用发送自定义消息,应用在FormExtensionAbility的onAcquireFormState()onEvent()中处理并返回数据。
  • 数据共享:可使用分布式数据对象(DistributedDataObject)或应用内数据库(RDB/Preferences)实现应用与卡片的数据同步。

4. 完整开发流程

  1. 工程配置:在module.json5中声明extensionAbilities,类型为form,并关联FormExtensionAbility。
  2. 实现FormExtensionAbility:重写onAddFormonUpdateForm等方法,处理卡片的创建、更新和事件。
  3. 设计卡片UI:使用ArkTS编写UI组件,并绑定动态数据。
  4. 测试与调试:在DevEco Studio中使用预览器或真机调试,验证数据刷新效果。

注意事项

  • 卡片刷新频率受系统省电策略限制,高频率实时刷新需结合被动更新(如用户点击)实现。
  • 避免在卡片中执行耗时操作,保持UI线程流畅。
  • 卡片数据应轻量化,建议使用JSON格式传递关键信息。

通过上述方式,可高效实现FormKit卡片的数据动态刷新与通信。

回到顶部