当前位置: 首页 > news >正文

HarmonyOS技术精讲-Form Kit(卡片开发服务)第2篇:搭建ArkTS卡片开发环境与创建第一个卡片

先搞清楚:卡片开发要解决什么问题

卡片的核心价值是“在桌面或负一屏,以轻量级、低功耗的方式,快速呈现用户关心的信息”。它不是应用主界面的替代品,而是应用核心功能的精华摘录。

适用场景:

  • 天气、时钟、日历等需要实时刷新信息的桌面小组件。
  • 音乐播放器的快捷控制面板。
  • 待办事项、记步数等无需打开应用即可查看的信息卡片。

不适用场景:

  • 需要复杂用户交互(如多级页面跳转)的场景。
  • 对性能要求极高,需要大量GPU渲染的效果。

ArkTS卡片是当前推荐的开发方式,相比JS卡片,它拥有更好的开发体验和性能表现。如果你还在用老的JS卡片方案,建议尽快迁移。

环境说明

  • DevEco Studio 版本:DevEco Studio 6.1.0 及以上
  • HarmonyOS SDK 版本:HarmonyOS 6.1.0(23) 及以上
  • 目标设备:手机、平板(推荐使用模拟器或真机进行调试)

注意:如果你使用的是早期版本的DevEco Studio,可能无法找到卡片模板,或者创建成功后编译报错。确保你的开发环境是最新的,能省去很多麻烦。

核心实现:从零创建你的第一个ArkTS卡片

第一步:选择正确的项目模板

打开DevEco Studio,创建一个新项目。这里有个常见的坑:很多人直接选择“Empty Ability”模板,然后手动往里面加卡片代码,结果发现配置死活不对。

正确做法是:选择“Empty Ability”模板后,在“Device”选项卡里,务必勾选“Show in Service Center”和“Widget”。

勾选“Widget”后,IDE会自动在工程里为你生成卡片相关的代码和资源配置文件,这一步能节省大量手动配置的时间。项目创建成功后的目录结构里,你会看到entry/src/main/ets/FormAbility这个文件夹,里面就是我们操作卡片的核心文件。

第二步:理解卡片模块的文件结构

一个标准的ArkTS卡片项目,核心文件包括:

  1. form_config.json:卡片配置信息,决定了卡片的外观(尺寸、是否可刷新等)。
  2. FormAbility.ets:卡片生命周期管理入口,负责监听卡片的创建、更新、销毁等事件。
  3. card.ets:卡片的UI界面,使用ArkTS编写。

第三步:编写第一个卡片UI(card.ets)

这是你最关心的部分。我们写一个最简单的“Hello World”卡片,包含一个文本和一个按钮(但注意:卡片内按钮只能触发点击事件,不能直接做跳转)。

// entry/src/main/ets/FormAbility/pages/card.ets@Entry@Componentstruct CardWidget{@Statemessage:string='Hello HarmonyOS Card!'// 点击事件回调onWidgetClick(){this.message='You clicked me!';}build(){// 这里用了一个栈容器,让文字居中显示Stack(){Column(){Text(this.message).fontSize(16).fontWeight(FontWeight.Bold).fontColor('#FFFFFF').textAlign(TextAlign.Center)}.width('100%').height('100%').justifyContent(FlexAlign.Center)}.width('100%').height('100%').backgroundColor('#007DFF')// 卡片背景色// 绑定点击事件.onClick(()=>{this.onWidgetClick();})}}

代码说明

  • @Entry@Component是ArkTS组件的基本装饰器,没什么好说的。
  • @State message是卡片的状态变量,当它改变时,UI会自动更新。这一点非常关键,它决定了卡片能否实时响应用户操作或数据更新。
  • 卡片内部的onClick事件是可行的,但注意它只能执行简单的状态切换或调用postCardAction(用于向应用发送消息),不能做页面跳转。

第四步:配置卡片生命周期入口(FormAbility.ets)

IDE生成的FormAbility.ets已经包含了基本框架,但为了让卡片能够正确运行,我们需要理解并修改几个关键回调。

// entry/src/main/ets/FormAbility/FormAbility.etsimportAbilityConstantfrom'@ohos.app.ability.AbilityConstant';importhilogfrom'@ohos.hilog';importwindowfrom'@ohos.window';importWantfrom'@ohos.app.ability.Want';exportdefaultclassFormAbilityextendsAbilityConstant.Ability{// 卡片创建时调用onCreate(want:Want,abilityParam:AbilityConstant.AbilityStartParams){hilog.info(0x0000,'testTag','%{public}s','FormAbility onCreate');// 这里可以对卡片进行初始化设置,比如从网络拉取数据}// 当卡片需要更新时调用(比如设置了定时刷新)onUpdate(formId:string){hilog.info(0x0000,'testTag','%{public}s','FormAbility onUpdate');// 更新卡片数据this.updateForm(formId);}// 卡片被销毁时调用onDestroy(formId:string){hilog.info(0x0000,'testTag','%{public}s','FormAbility onDestroy');}// 这是一个自定义方法,用于更新卡片UI// 实际开发中,需要在这里调用 widget.getWidget().updateForm() 来更新卡片数据// 但本示例为了简化,不做数据更新privateupdateForm(formId:string){// 更新代码}}

重点onCreate方法里拿到的want参数里包含了卡片ID、卡片名称等信息。在onUpdate回调里,你需要通过updateForm方法来刷新卡片UI。但很多初学者会发现,在onUpdate里直接调用widget对象会报错,因为这个对象需要在UI线程中获取。这个问题我们在踩坑章节细说。

第五步:配置卡片信息(form_config.json)

这个文件告诉系统,你的卡片长什么样,有什么行为。

{"forms":[{"name":"HelloCard","description":"这是第一个卡片","src":"./pages/card.ets",// 指向我们的UI文件"window":{"designWidth":720,// 设计稿宽度"autoDesignWidth":true},"formConfig":{"landscapeLayout":"default",// 横屏布局"portraitLayout":"default"// 竖屏布局},"updateEnabled":true,// 允许卡片定时更新"scheduledUpdateTime":"10:30",// 每天10:30更新(需要配合updateDuration使用)"updateDuration":2,// 更新周期,单位小时,最少2小时"defaultDimension":"2*2",// 卡片尺寸,2x2 网格"supportDimensions":["2*2"]// 支持的尺寸}]}

踩坑点updateDuration的最小值是2,即最少每2小时更新一次。如果你想实现更快的刷新(比如天气卡片每分钟刷新),这种卡片上的定时刷新机制是达不到的。你需要用到coprocessor(协处理器)或者应用后台跑到前台时手动更新。

第六步:在module.json5中声明卡片

这一步很多人会忽略,导致卡片在桌面上找不到。在entry/src/main/module.json5文件中,你需要将FormAbility声明进去。

{"module":{// ... 其他配置"abilities":[{"name":"FormAbility","srcEntry":"./ets/FormAbility/FormAbility.ets","description":"卡片能力","icon":"$media:icon","label":"$string:entry_FormAbility","startWindowIcon":"$media:icon","startWindowBackground":"$color:start_window_background","visible":true,"skills":[{"entities":["entity.system.home"],"actions":["action.system.home"]}]}]}}

关键点在于"visible": true"skills"字段。skills定义了该Ability可以处理action.system.home动作,这表示它可以在桌面显示。

第七步:运行和调试

在模拟器或真机上运行你的应用。应用安装成功后,不要直接点击应用图标。正确的做法是:

  1. 回到桌面。
  2. 长按桌面空白处,点击“服务卡片”。
  3. 在卡片列表中找到你的应用,然后选择“HelloCard”。
  4. 卡片就会被添加到桌面。

如果卡片没有出现,检查:

  • 编译是否成功,控制台有无报错。
  • module.json5配置是否正确。
  • 模拟器是否支持卡片功能(大部分模拟器是支持的)。

真正有价值的踩坑

坑1:卡片UI更新不生效

现象:在FormAbilityonUpdate回调中,通过widget.getWidget()获取Widget对象并调用updateForm,但UI没有任何变化。

原因onUpdate方法运行在卡片的生命周期线程中,并不是UI线程。直接在该线程里调用UI更新API是不被允许的,或者调用时机不对。

解决方案:正确的做法是在FormAbility中通过widget模块的updateFormByKeyValueupdateForm方法来异步更新。而且,确保你已经正确获取了widget对象。

// 正确的更新方式importwidgetfrom'@ohos.arkui.widget';exportdefaultclassFormAbilityextendsAbilityConstant.Ability{onUpdate(formId:string){// 构造新的数据letformData={"message":"更新后的文本"// 这里的key必须与card.ets中@State变量名对应};// 异步更新widget.updateForm(formId,{"formData":JSON.stringify(formData)},(err,data)=>{if(err){hilog.error(0x0000,'testTag','Failed to update form. Cause: %{public}s',JSON.stringify(err));}else{hilog.info(0x0000,'testTag','Succeeded in updating form. Data: %{public}s',JSON.stringify(data));}});}}

关键点formData的key必须和card.ets中的@State状态变量名完全一致,否则UI不会刷新。这是ArkTS卡片状态同步的机制,官方文档虽然提了,但没强调这个一致性要求,很多人踩坑。

坑2:卡片状态在返回后丢失

现象:从应用跳转到卡片,滑动一下卡片,再回到桌面,卡片的message状态又变回了'Hello HarmonyOS Card!'

原因:卡片的@State状态是存放在内存中的,当卡片被销毁或资源回收后,状态就丢失了。卡片不像应用有完整的saveState机制。

解决方案:将需要持久化的状态写入到LocalStorageAppStorage中,在卡片初始化时恢复。

// card.ets@Entry@Componentstruct CardWidget{@Statemessage:string=AppStorage.get('cardMessage')||'Hello HarmonyOS Card!';onWidgetClick(){this.message='You clicked me!';AppStorage.set('cardMessage',this.message);}}

注意AppStorage的读写虽然简单,但它是一个全局存储,不同卡片之间的数据可能会污染。更推荐的做法是使用LocalStorage,在FormAbility创建卡片时为每个卡片创建一个独立的LocalStorage实例。

最佳实践

  1. 不要在build()中创建复杂对象build()方法在UI需要刷新时会被频繁调用。如果里面创建了复杂的对象或进行了耗时计算,会直接拖垮UI性能,导致掉帧。尽量将这类操作放到@State变量变化时的回调或FormAbilityonUpdate中。
  2. 优先使用updateFormByKeyValue:相比updateForm,前者是更细粒度的更新。你只需要传入改变的状态值,框架会帮你完成diff,从而减少不必要的UI重绘。
  3. 合理设置updateDuration:不要为了实时性把这个值设成10,系统会忽略。请根据你的业务场景选择合理的更新周期,比如2(2小时)或4(4小时)。如果要求更高频率,考虑使用其他后台同步方案。

FAQ

Q:为什么我在模拟器上看到卡片是空白的?
A:最常见的原因是card.ets文件中没有正确导出@Entry装饰器,或者form_config.json里的src路径写错了。另外,检查模拟器是否成功创建了卡片,有时候模拟器会崩溃导致卡片无法加载。

Q:为什么onCreate里拿到的want参数是空的?
A:这在早期版本的DevEco Studio中比较常见。通常是由于want的序列化出现问题。可以尝试在module.json5中给FormAbility添加launchTypesingleton,但这并不是一个根治的方法。如果遇到,建议升级DevEco Studio和SDK到最新版。

Q:卡片可以添加列表吗?可以异步加载图片吗?
A:可以。ArkTS卡片完全支持ListGrid等复杂布局,也支持通过Image组件异步加载网络图片,但需要配置networkAccess权限。性能优化是关键,不要在卡片主线程中做过多网络操作,否则容易导致卡片卡死。

示例代码地址:项目地址

http://www.jsqmd.com/news/1101181/

相关文章:

  • 别再乱用iPerf3的-P参数了!一个参数搞懂TCP/UDP打流瓶颈在哪
  • 告别环境卡壳!macOS下Claude Code从0到1安装与API模型连接
  • GEO词库迭代的RAG适配机制与全域语义稳定方案
  • 计算机毕业设计之基于web的房屋租赁管理系统
  • 微前端架构落地实战:用qiankun轻松拆分巨石应用
  • 库存扣减的并发难题:超卖·悲观锁·乐观锁·Redis 预扣减 4 种方案实战
  • 2026企业级AI接口统一调度平台实测排行 | 专业选型指南(避坑+成本对比)
  • 从2D到3D:WINNER+信道模型如何用仰角信息提升无线仿真精度(附场景参数对比)
  • 从纠错到5G:卷积码与维特比译码是如何塑造现代通信的?
  • VMware多机通信故障排查:7种常见组网失败场景及秒级修复方案
  • YOLOv10模型改进-卷积层改进-第11篇:YOLOv10改进策略【卷积层】| CVPR2025 GBConv轻量门控瓶颈卷积
  • 别再只调fit_intercept了!手把手教你用Python实战scikit-learn LinearRegression的4个隐藏参数
  • 上海闵行区做半包的公寓装修公司靠谱推荐
  • 5分钟搞定Unity游戏实时翻译:XUnity.AutoTranslator完整指南
  • 别再死记硬背了!用这3个核心公式,彻底搞懂电感在Buck电路里的工作模式(CCM/DCM/BCM)
  • MySQL两小时半快速入门:从安装到增删改查完整实战
  • SpringBoot+Vue学生宿舍报修系统:从零搭建到功能测试全流程指南
  • 前端XSS攻击防御全解析:从原理到实战的完整安全方案
  • NVIDIA显卡隐藏设置完全解锁:Profile Inspector深度调优指南
  • 从零搭建个人AI工作台:我用玄鉴AI把日常效率翻了3倍
  • 0元打造家用云盘:旧笔记本变身NAS
  • 别再死记硬背了!用一张图搞懂Xilinx 7系列FPGA的CLB与Slice结构(附资源速查表)
  • 【毕业设计】基于JavaWeb技术的在线考试系统设计与实现 SpringBoot+Vue 完整源码(含论文+数据库,可运行)
  • 2026年企业AI API数据安全实战:你的Prompt可能正在裸奔
  • YOLO目标检测实战:从原理到部署的完整指南
  • 把人像抠图交给NAS:image-matting部署与远程访问实践
  • ADM云GPU私有化部署MOSS-TTS+远程API访问
  • 户外恶劣环境(如矿山、沙漠)如何保证不掉线?跨境IoT极端工况通信方案
  • AntiDupl.NET:基于SSIM算法的重复图片检测引擎架构解析
  • 诚邀莅临 WAIC 2026丨破局边缘 AI 碎片化,全栈硬件矩阵重磅登场