鸿蒙HarmonyOS服务卡片实战

引言

在现代开发中,服务卡片是不可或缺的一部分,比如音乐,天气类等应用,官网的介绍中写道:卡片让您便捷地预览服务信息,例如查看天气或日历日程等内容。您可将卡片添加到屏幕上,让这类信息触手可及。您还可按喜好选取不同样式和排列方式,打造个性化桌面。

大体思路

  1. 创建服务卡片
  2. 应用与服务卡片的交互
  3. 刷新卡片内容
  4. 应用端主动更新卡片信息

创建服务卡片

首先,我们需要打开Edit Configurations,选中Deploy Multi Hap,将DEploy Muliti Hap Packages 的框打上勾

然后应用就可以愉快的进行服务卡片的开发了:

创建步骤:选中Entry,new,Service Widget

在弹出框中选选择想要创建的服务卡片类型,本文中选中默认的文本卡片

创建完成后除了serviceCard页面还会生成一个EntryFormAbility文件,和EntryAbility不同的是,EntryFormAbility继承了FormExtensionAbility,而EntryAbility继承了UIAbility。可以理解他们是2个进程。

创建完服务卡片,先运行起来后效果:

我们可以观察到,创建的服务卡片,在点击卡片后有如下代码

postCardAction(this, {action: this.ACTION_TYPE,abilityName: this.ABILITY_NAME,params: {message: this.MESSAGE}});

postCardAction是给卡片添加意图,this代表当前卡片,action是类型,abilityName是打开哪个Ability,params是需要给应用进程传递的数据(服务卡片在另外一个进程),点击完服务卡片后发现,跳转到了应用首页,那能否跳转到指定页面呢?

应用与服务卡片的交互

在卡片上添加2 个按钮,去掉原来的整个卡片的onClick,添加2个按钮

Button('首页').onClick(()=>{postCardAction(this,{'action':'router','abilityName':'EntryAbility','params':{targetPage:"Index"},})})
Button('我的').onClick(()=>{postCardAction(this,{'action':'router','abilityName':'EntryAbility','params':{targetPage:"MinePage"},})})}

在点击按钮的时候把我们需要跳转的page名称传给应用,那如何接受这targetPage呢?

我们在EntryAbility里看到很多空方法,其实就是Ability的生命周期,有过原生安卓开发经验的,可以将它理解为application的生命周期回调函数。

//要访问的页面
let selectPage =''
export default class EntryAbility extends UIAbility {onCreate(want, launchParam) {//启动程序的生命周期if(want.parameters.params!==undefined){let params = JSON.parse(want.parameters.params);console.log('onCreate router targetPage:'+params.targetPage);selectPage = params.targetPage;}}onWindowStageCreate(windowStage: window.WindowStage) {//默认进入首页let target = selectPage || 'Index';target = 'pages/'+target;// Main window is created, set main page for this abilityhilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate');//存储窗口实例windowStage.loadContent(target, (err, data) => {if (err.code) {hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');return;}hilog.info(0x0000, 'testTag', 'Succeeded in loading the content. Data: %{public}s', JSON.stringify(data) ?? '');});}}

我们可以设置一个参数selectPage,通过onCreate方法里的want.parameters.params获取到我们上面点击服务卡片的时候传递的params对象。通过JSON解析后拿到targetPage,在onWindowStageCreate中组合拼装一下路由地址,currentWindow.loadContent(tatgetPage)执行后,有值就根据参数拼接rute地址跳转,否则就默认首页。

运行后当kill掉应用的进程,能够正常跳转到首页和我的页面,但是有个问题,如果只是把应用推到后台,再点击我的,发现无效无法跳转,这是什么原因呢?有些眼尖的同学可能就发现了,我们接受和打开的方法都放在了onCreate里。只有创建的时候才会接收到,如果Ability已经创建了,就无法接收到卡片的通知回调。

那么改如何做呢?答案是通过onNewWant函数,这个函数的意思是,如果UIAbility已在后台运行,在收到Router事件后会触发,改写后的代码:

//要访问的页面
let selectPage =''
//当前的windown对象
let currentWindow = null
export default class EntryAbility extends UIAbility {onCreate(want, launchParam) {//启动程序的生命周期if(want.parameters.params!==undefined){let params = JSON.parse(want.parameters.params);console.log('onCreate router targetPage:'+params.targetPage);selectPage = params.targetPage;}}//如果UIAbility已在后台运行,在收到Router事件后会触发onNewWant生命周期的回调onNewWant(want, launchParam){if(want.parameters.params!==undefined){let params = JSON.parse(want.parameters.params);console.log('onCreate router targetPage:'+params.targetPage);selectPage = params.targetPage;}//进入对应页面let target = selectPage || 'AddressPage';target = 'pages/'+target;currentWindow.loadContent(target, (err, data) => {if (err.code) {hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');return;}hilog.info(0x0000, 'testTag', 'Succeeded in loading the content. Data: %{public}s', JSON.stringify(data) ?? '');});}onWindowStageCreate(windowStage: window.WindowStage) {//默认进入首页let target = selectPage || 'AddressPage';target = 'pages/'+target;// Main window is created, set main page for this abilityhilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate');//存储窗口实例currentWindow = windowStagecurrentWindow.loadContent(target, (err, data) => {if (err.code) {hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');return;}hilog.info(0x0000, 'testTag', 'Succeeded in loading the content. Data: %{public}s', JSON.stringify(data) ?? '');});}}

创建了一个变量currentWindow用以存储onWindowStageCreate中的windown对象,然后在onNewWant中获取相应的信息,再执行跳转。

刷新卡片内容

到目前为止卡片上的信息都是静态展示的,那我们如何更新卡片上的信息呢?

首先需要在EntryFormAbility的onAddForm里创建一对要更新的数据,通过formBindingData.createFormBindingData创建一个FormBindingData的实力返回回去

 onAddForm(want: Want) {let formData = {title : 'jay'}return formBindingData.createFormBindingData(formData);}

此时卡片作为接收方需要通过LocalStorage获取传递的数据,具体做法如下:

let storage = new LocalStorage();
@Entry(storage)
@Component
struct MessageCardCard {
@LocalStorageProp('title') title:string='zhoujielun'build() {Row() {Column({space:20}) {Text('服务卡片:'+this.title).fontSize($r('app.float.font_size')).fontWeight(FontWeight.Medium).fontColor($r('app.color.item_title_font'))}.width('100%')}.height('100%  ')}
}

创建一个LocalStorage实例,然后在Entry中传入storage,在通过@LocalStorageProp('title') title:string='zhoujielun'读取数据,需要注意:1必须要有默认值,2LocalStorageProp包裹的字符串名需要和onAddForm函数中的formData的key保持一致,才能正确接受到数据的更新。

运行后发现,卡片上的数据已经变成了jay

卡片方主动更新服务卡片信息

依然是调用postCardAction 方法,action改为message

   Button('更新数据') .onClick(()=>{postCardAction(this,{action:'message',params:{}})})

这时候就需要用到EntryFormAbility的onFormEvent函数了

 //对应卡片的message事件onFormEvent(formId: string, message: string) {let fromData={'title':'黑色毛衣'};//构建传入的数据对象let fromInfo  = formBindingData.createFormBindingData(fromData)//指定卡片id,传入最新的数据formProvider.updateForm(formId,fromInfo)}

此时运行后点击卡片上的更新数据的按钮,数据已经更新过来了

应用方更新服务卡片信息

因为应用方无法知道服务卡片的formId,那么我就要在服务卡片创建的时候把fromId存起来,然后统一发送事件更新信息。

// @ts-nocheck
import formInfo from '@ohos.app.form.formInfo';
import formBindingData from '@ohos.app.form.formBindingData';
import FormExtensionAbility from '@ohos.app.form.FormExtensionAbility';
import Want from '@ohos.app.ability.Want';
import formProvider from '@ohos.app.form.formProvider';
import dataPreferences from '@ohos.data.preferences';
export default class EntryFormAbility extends FormExtensionAbility {onAddForm(want: Want) {//在want中取出卡片的唯一标识formIdlet formId:string = want.parameters[formInfo.FormParam.IDENTITY_KEY];//把formId保存到首选项中;(async ()=>{let pre = await dataPreferences.getPreferences(this.context,'formIds');// @ts-ignorelet formIds:string = await pre.get('formIds','[]');let formIdsArray = JSON.parse(formIds);formIdsArray.push(formId);await pre.put('formIds',JSON.stringify(formIdsArray));await pre.flush();})()let formData = {title : 'jay'}return formBindingData.createFormBindingData(formData);}onCastToNormalForm(formId: string) {// Called when the form provider is notified that a temporary form is successfully// converted to a normal form.}onUpdateForm(formId: string) {// Called to notify the form provider to update a specified form.}onChangeFormVisibility(newStatus: Record<string, number>) {// Called when the form provider receives form events from the system.}//对应卡片的message事件onFormEvent(formId: string, message: string) {// Called when a specified message event defined by the form provider is triggered.let fromData={'title':'黑色毛衣'};//构建传入的数据对象let fromInfo  = formBindingData.createFormBindingData(fromData)//指定卡片id,传入最新的数据formProvider.updateForm(formId,fromInfo)}onRemoveForm(formId: string) {// Called to notify the form provider that a specified form has been destroyed.}onAcquireFormState(want: Want) {// Called to return a {@link FormState} object.return formInfo.FormState.READY;}
};

在Page页面根据取出首选项中的formId,循环调用updateForm更新数据:

import router from '@ohos.router'
import dataPreferences from '@ohos.data.preferences';
import formBindingData from '@ohos.app.form.formBindingData';
import formProvider from '@ohos.app.form.formProvider';@Entry
@Component
struct AddressPage {store: any = {}@State city: string = ''t:number = 1async aboutToAppear() {setInterval(async ()=>{let formData={time: ++this.t};//读取首选项中的id集合let pre = await dataPreferences.getPreferences(getContext(this),'formIds');// @ts-ignorelet formIds:string = await pre.get('formIds','[]');let formMsg = formBindingData.createFormBindingData(formData)//遍历集合,将新数据传到当前应用的所有卡片JSON.parse(formIds).forEach((currentFormId)=>{formProvider.updateForm(currentFormId,formMsg)});},1000)}build() {Row() {Column() {Text('首页')Button('更新卡片').onClick(async ()=>{let formData={title:'听妈妈的话'};//读取首选项中的id集合let pre = await dataPreferences.getPreferences(getContext(this),'formIds');// @ts-ignorelet formIds:string = await pre.get('formIds','[]');let formMsg = formBindingData.createFormBindingData(formData)//遍历集合,将新数据传到当前应用的所有卡片JSON.parse(formIds).forEach((currentFormId)=>{formProvider.updateForm(currentFormId,formMsg)});})}.width('100%')}.height('100%')}
}

上面的例子中,我直接通过定时间,每隔一秒更新卡片上的数字,模拟充电或者歌曲播放进度效果,运行后:

总结

服务卡片的创建交互,以及刷新卡片信息的内容到此结束,相信应该能满足大部分业务需求,差别无非就是在UI上。

今天是HDC2024华为开发者大会,谨借此篇博客提前庆祝纯血鸿蒙的到来,华为加油,HarmonyOS 加油💪🏻💪🏻💪🏻

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/359557.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【面试题】前端 移动端自适应?_前端移动端适配面试题

设备像素比 设备像素比 (DevicePixelRatio) 指的是设备物理像素和逻辑像素的比例 。比如 iPhone6 的 DPR 是2。 设备像素比 物理像素 / 逻辑像素。可通过 window.devicePixelRatio 获取&#xff0c;CSS 媒体查询代码如下 media (-webkit-min-device-pixel-ratio: 3), (min-…

初识 GPT-4 和 ChatGPT

文章目录 LLM 概述理解 Transformer 架构及其在 LLM 中的作用解密 GPT 模型的标记化和预测步骤 想象这样⼀个世界&#xff1a;在这个世界里&#xff0c;你可以像和朋友聊天⼀样快速地与计算机交互。那会是怎样的体验&#xff1f;你可以创造出什么样的应用程序&#xff1f;这正是…

【大数据】Hadoop学习笔记

基本概念 Hadoop组成 HDFS: Hadoop分布式文件存储系统, 在Haddop中处于底层/核心地位YARN: 分布式通用的集群资源管理系统和任务调度平台, 支撑各种计算引擎执行MapReduce: 第一代分布式计算引擎, 但因为部分原因, 许多企业都不直接使用MapReduce, 但许多底层软件仍然在使用Ma…

小程序wx.uploadFile异步问题

问题&#xff1a;小程序上传文件后我需要后端返回的一个值&#xff0c;但这个值总是在最后面导致需要这个值的方法总是报错&#xff0c;打印测试后发现这它是异步的。但直接使用 await来等待也不行。 uploadImg.wxml <view class"upload-wrap"><view clas…

2024北京智源大会开幕,智源推出大模型全家桶及全栈开源技术基座新版图,大模型先锋集结共探AGI之路

2024年6月14日&#xff0c;第六届“北京智源大会”在中关村展示中心开幕。 北京智源大会是智源研究院主办的“AI内行顶级盛会”&#xff0c;以“全球视野、思想碰撞、前沿引领”为特色&#xff0c;汇聚海内外研究者分享研究成果、探寻前沿知识、交流实践经验。2024北京智源大会…

自定义平台后台登录地址前缀的教程

修改平台后台地址默认的 admin 前缀 修改后端 config/admin.php 配置文件,为自定义的后缀修改 平台后台前端源码中 src/settings.js 文件,修改为和上面一样的配置修改后重新打包前端代码,并且覆盖到后端的 public 目录下重启 swoole 服务即可

Charles配置与API数据抓取

2024软件测试面试刷题&#xff0c;这个小程序&#xff08;永久刷题&#xff09;&#xff0c;靠它快速找到工作了&#xff01;&#xff08;刷题APP的天花板&#xff09;-CSDN博客跳槽涨薪的朋友们有福了&#xff0c;今天给大家推荐一个软件测试面试的刷题小程序。https://blog.c…

Crypto++ 入门

一、简介 Crypto&#xff08;也称为CryptoPP、libcrypto或cryptlib&#xff09;是一个免费的开源C库&#xff0c;提供了多种加密方案。它由Wei Dai开发和维护&#xff0c;广泛应用于需要强大加密安全的各种应用程序中。该库提供了广泛的加密算法和协议的实现&#xff0c;包括&…

板凳--------第20章-信号:基本概念1

tlpi_hdr.h头文件使用及设置 liao__ran 于 2020-09-29 15:12:01 发布 阅读量1.6k 收藏 5 点赞数 1 分类专栏&#xff1a; linux系统编程手册 版权 linux系统编程手册 专栏收录该内容 7 篇文章 1 订阅 订阅专栏 使用的头文件&#xff0c;主要如下&#xff1a; ename.c.inc erro…

视频融合共享平台LntonCVS视频监控管理平台技术方案详细介绍

LntonCVS国标视频综合管理平台是一款以视频为核心的智慧物联应用平台。它基于分布式、负载均衡等流媒体技术进行开发&#xff0c;提供广泛兼容、安全可靠、开放共享的视频综合服务。该平台具备多种功能&#xff0c;包括视频直播、录像、回放、检索、云存储、告警上报、语音对讲…

Linux:多线程中的互斥与同步

多线程 线程互斥互斥锁互斥锁实现的原理封装原生线程库封装互斥锁 死锁避免死锁的四种方法 线程同步条件变量 线程互斥 在多线程中&#xff0c;如果存在有一个全局变量&#xff0c;那么这个全局变量会被所有执行流所共享。但是&#xff0c;资源共享就会存在一种问题&#xff1…

基于Pytorch框架构建LeNet-5模型

Pytorch 一、训练模型1.导入必要的库2.设置超参数3.数据预处理4.读取数据 二、定义卷积神经网络1.定义卷积神经网络2.定义学习率3.实例化模型并且移动到GPU4.选择优化器 三、定义调整学习率的函数1.定义调整学习率的函数 四、训练模型1.设置模型为训练模式2.遍历训练数据加载器…

揭秘循环购:消费即收益,如何助力商家月销百万?

大家好&#xff0c;我是吴军&#xff0c;今天要和大家分享一种颠覆性的商业模式——循环购。你是否听说过“消费1000送2000”这样的促销活动&#xff1f;是不是觉得太不可思议&#xff0c;商家岂不是在“送钱”&#xff1f;别急&#xff0c;让我为你揭开这背后的秘密。 循环购&…

RN开发搬砖经验之—“Calculated frame index should never be lower than 0“崩溃问题分析

问题重现 崩溃堆栈&#xff1a; Back traces starts. java.lang.RuntimeException: java.lang.IllegalStateException: Calculated frame index should never be lower than 0at com.facebook.react.animated.NativeAnimatedModule$1.doFrameGuarded(NativeAnimatedModule.ja…

计算机组成原理 | CPU子系统(1)基本概述

基本结构模型 运算与缓存部件 数据寄存部件 PSW不是很清楚 存储器是什么&#xff1f;属于那个结构里&#xff1f; 时序处理部件 cpu是大脑&#xff0c;控制器是神经元 ①通过硬件产生控制信号 ②通过软件产生控制信号 外频&#xff08;系统时钟信号&#xff09;&#xff0c;…

Tesseract-OCR 5.0LSTM训练

准备工作 1.安装tesseract5.0版本 2.配置tesserac环境变量 3.jTessBoxEditor(需要java环境) 很多博客已有详细教程&#xff0c;不再赘述&#xff0c;本文以训练为主 最终文件目录: --tif 需要训练的tif文件 --lstmf 后文会讲到生成的方式 --txt 后文会讲到生成的方式 --box 后文…

【Day02】0基础微信小程序入门-学习笔记

文章目录 模板与配置学习目标WXML 模板语法1.数据绑定&#xff08;类似于 Vue2 &#xff09;2. 事件绑定3. 条件渲染4.列表渲染 WXSS模板样式1. rpx尺寸单位2.样式导入3. 全局样式和局部样式 全局配置1. window2. tabBar 页面配置网络数据请求总结 持续更新~ 模板与配置 学习目…

Databend 开源周报第 149 期

Databend 是一款现代云数仓。专为弹性和高效设计&#xff0c;为您的大规模分析需求保驾护航。自由且开源。即刻体验云服务&#xff1a;https://app.databend.cn 。 Whats On In Databend 探索 Databend 本周新进展&#xff0c;遇到更贴近你心意的 Databend 。 支持递归公共表…

Nvidia Isaac Sim图编程OmniGraph 入门教程 2024(6)

Nvidia Isaac Sim 入门教程 2024 版权信息 Copyright 2023-2024 Herman YeAuromix. All rights reserved.This course and all of its associated content, including but not limited to text, images, videos, and any other materials, are protected by copyright law. …

创建一个Django项目

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 本小节我们将开始讲解如何使用Django创建一个项目&#xff0c;步骤如下&#xff1a; &#xff08;1&#xff09;首先在D盘&#xff08;读者可以根据…