当前位置: 首页 > 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.cnnetsun.cn/news/3080934.html

相关文章:

  • 别再乱用iPerf3的-P参数了!一个参数搞懂TCP/UDP打流瓶颈在哪
  • 魔珐星云 SDK 实战:从基础代码到具身交互终端成品
  • 门店私域客户管理升级:AI智能检索客户功能使用科普
  • MCP协议全面落地:AI Agent如何改变软件开发流程
  • 别再死记公式了!用PyTorch代码直观理解nn.Conv3d的参数量与计算量
  • 告别车载ECU耗电焦虑:手把手教你配置AUTOSAR NM的Partial Network功能
  • 让外贸网站询盘翻倍的新概念GEO,90%的技术人还没注意到
  • AI 智能体商用落地测评报告:多模态全能平台选型与团队管理实战经验
  • 别再为485通信干扰头疼了!手把手教你用ADM2486搭建隔离电路(附实测波形)
  • SAP ATP检查里那个不起眼的‘确认可用部分数量’,到底怎么用?一个真实案例带你搞懂
  • 别再傻傻分不清了!PN结的‘空间电荷区’和‘耗尽区’到底有啥区别?用大白话给你讲明白
  • NAT端口转发总失败?教你用vmnetcfg+iptables+guestinfo校验三重验证法,5分钟定位真实瓶颈,
  • 告别体素和固定窗口:用OctFormer的八叉树注意力高效处理大规模3D点云
  • OV5640寄存器配置详解:从DVP到MIPI接口,手把手教你调出720p@60fps(附完整代码)
  • 计算机毕业设计之高校教材管理平台的设计与实现
  • 告别ECU休眠唤醒烦恼:手把手教你用TJA1145实现汽车CAN网络的低功耗管理
  • 手把手教你用EmEditor和dtc工具拆解Linux设备树dtb文件(附二进制查看技巧)
  • 别再乱用--privileged了!手把手教你安全配置Docker in Docker(DinD)的两种姿势
  • 可观测与高容错:大模型驱动的异步工作流引擎持久化设计
  • 5步掌握OpenDog:从零构建开源四足机器人完整指南
  • 别再乱用gc.collect()了!Python内存管理的正确姿势与实战避坑指南
  • 企业级考研互助交流平台管理系统源码|SpringBoot+Vue+MyBatis架构+MySQL数据库【完整版】
  • 别再死记硬背了!用一张图彻底搞懂RocketMQ里的Topic、Queue和Tag
  • 3步解决流媒体保存难题:N_m3u8DL-RE实战指南
  • 2026年AI Agent开发学习路线:从核心原理到业务落地的实战指南
  • PromptSRC论文精读:我们是如何让提示学习不再‘过拟合’的?
  • C++的内存布局
  • 从VSCode到Rider:一个Unity开发者关于调试工具的真实心路历程与切换指南
  • 给汽车软件工程师的ASPICE入门指南:从SYS.1到SWE.6,搞懂过程模型到底在管什么
  • Beyondcompare4