鸿蒙OS开发实例:【手撸服务卡片】

news2024/11/15 11:13:24

介绍

服务卡片指导文档位于“开发/应用模型/Stage模型开发指导/Stage模型应用组件”路径下,说明其极其重要。

本篇文章将分享实现服务卡片的过程和代码

准备

  1. 请参照[官方指导],创建一个Demo工程,选择Stage模型
鸿蒙OS开发更多内容↓点击HarmonyOS与OpenHarmony技术
鸿蒙技术文档开发知识更新库gitee.com/li-shizhen-skin/harmony-os/blob/master/README.md在这。或+mau123789学习,是v喔

搜狗高速浏览器截图20240326151547.png

  1. 熟读HarmonyOS 官方指导 “[创建一个ArkTS卡片]”

实践总结

  1. 应用打包时,不能选择“Deploy Muti Hap Packages”方式, 否则服务卡片不会显示任何内容
  2. 官方指导中没有提示添加权限“ohos.permission.KEEP_BACKGROUND_RUNNING”,如果不添加,则无法使用call方式来刷新卡片数据
  3. 卡片首次创建时,卡片Id无法传入到卡片中,需通过延时机制,二次更新

效果

Screenshot_20231201173024131.png

卡片元素说明

  1. 卡片共有使用到了7个控件
  2. 5个Text, 1个Image,  1个Rect
  3. Text('call') :  可点击,实验call事件刷新卡片内容
  4. Text('router'): 可点击,实验router事件刷新卡片内容
  5. Text('message'): 可点击,实验message事件刷新卡片内容
  6. Rect(): 实验卡片动画效果

服务卡片教程

  1. 请完全按照“创建一个ArkTS卡片”

  2. 修改生成的卡片代码(WidgetCard.ets)

let storageCard = new LocalStorage()

@Entry(storageCard)
@Component
struct WidgetCard {
  /*
   * The mini title.
   */
  readonly MINI_TITLE: string = 'Title';

  /*
   * The item title.
   */
  @LocalStorageProp('ITEM_TITLE')ITEM_TITLE: string = '标题';

  /*
   * The item content.
   */
  @LocalStorageProp('ITEM_CONTENT') ITEM_CONTENT: string = '天气不错';

  /*
   * The action type.
   */
  readonly ACTION_TYPE: string = 'router';

  /*
   * The ability name.
  */
  readonly ABILITY_NAME: string = 'EntryAbility';

  /*
   * The message.
   */
  readonly MESSAGE: string = '来自服务卡片';

  /*
   * The mini display priority.
   */
  readonly MINI_DISPLAY_PRIORITY: number = 2;

  /*
   * The max line.
   */
  readonly MAX_LINES: number = 1;

  /*
   * The with percentage setting.
   */
  readonly FULL_WIDTH_PERCENT: string = '100%';

  /*
   * The height percentage setting.
   */
  readonly FULL_HEIGHT_PERCENT: string = '100%';

  /*
   * Image height percentage setting.
   */
  readonly IMAGE_HEIGHT_PERCENT: string = '64%';
  @State mini: boolean = false;

  @State rectWidth: string = '30%'

  @LocalStorageProp('formId') formId: string = '0';

  build() {
    Row() {
      Column() {
        if (this.mini) {
          Column() {
            Text(this.MINI_TITLE)
              .fontSize($r('app.float.mini_title_font_size'))
              .fontColor($r('app.color.mini_text_font_color'))
              .margin({
                left: $r('app.float.mini_title_margin'),
                bottom: $r('app.float.mini_title_margin')
              })
          }
          .width(this.FULL_WIDTH_PERCENT)
          .alignItems(HorizontalAlign.End)
          .backgroundImageSize(ImageSize.Cover)
          .backgroundImage($r("app.media.ic_widget"), ImageRepeat.NoRepeat)
          .displayPriority(this.MINI_DISPLAY_PRIORITY)
        }
        Stack(){
          Image($r("app.media.ic_widget"))
            .width(this.FULL_WIDTH_PERCENT)
            .height('100%')
            .objectFit(ImageFit.Cover)
            .borderRadius($r('app.float.image_border_radius'))
          Rect()
            .width(this.rectWidth)
            .height('100%')
            .fill('#60ff0000')
            .animation({
              duration: 3000,
              curve: Curve.Linear,
              playMode: PlayMode.Normal,
              iterations: -1,
              onFinish:()=>{
                  if(this.rectWidth == '30%'){
                    this.rectWidth = '50%'
                  } else {
                    this.rectWidth = '30%'
                  }
              }})
          Row(){
            Column({space: 20}){
              Text('call')
                .fontColor(Color.Red)
                .onClick(()=>{
                  console.log('formId: '+this.formId)
                  postCardAction(this, {
                    'action': 'call',
                    'abilityName': 'EntryAbility',
                    'params': {
                      'method': 'funA',
                      'formId': this.formId
                    }
                  });
                })

              Text('router')
                .onClick(()=>{
                  postCardAction(this, {
                    'action': 'router',
                    'abilityName': 'EntryAbility',
                    'params': {
                      'msgTest': 'messageEvent'
                    }
                  });
                })
            }

            Column(){
              Text('message')
                .fontColor(Color.Green)
                .onClick(()=>{
                  postCardAction(this, {
                    'action': 'message',
                    'params': {
                      'msgTest': 'messageEvent'
                    }
                  });
                })
            }

          }.height('100%')
        }
        .width(this.FULL_WIDTH_PERCENT)
        .height(this.IMAGE_HEIGHT_PERCENT)

        Blank()
        Text(this.ITEM_TITLE)
          .fontSize($r('app.float.normal_title_font_size'))
        Text(this.ITEM_CONTENT)
          .maxLines(this.MAX_LINES)
          .fontSize($r('app.float.normal_content_font_size'))
          .textOverflow({ overflow: TextOverflow.Ellipsis })
      }
      .width(this.FULL_WIDTH_PERCENT)
      .height(this.FULL_HEIGHT_PERCENT)
      .alignItems(HorizontalAlign.Start)
      .backgroundColor($r('app.color.start_window_background'))
    }
    .height(this.FULL_HEIGHT_PERCENT)
    .alignItems(VerticalAlign.Top)
    .padding($r('app.float.row_padding'))
    .onClick(() => {
      postCardAction(this, {
        "action": this.ACTION_TYPE,
        "abilityName": this.ABILITY_NAME,
        "params": {
          "message": this.MESSAGE
        }
      });
    })
  }
}
  1. 修改应用入口EntryAbility.ets
import window from '@ohos.window';
import UIAbility from '@ohos.app.ability.UIAbility';
import formBindingData from '@ohos.app.form.formBindingData';
import formProvider from '@ohos.app.form.formProvider';
import formInfo from '@ohos.app.form.formInfo';

export default class EntryAbility extends UIAbility {
  storage: LocalStorage

  onCreate(want, launchParam) {
    try{
      let params = JSON.parse(want.parameters.params);

      console.log('onCreate ' + params['message'])
      this.storage = new LocalStorage({'ext': params['message']})
    } catch (e){
      console.log(e)
    }

    try{
      // 监听call事件所需的方法
      this.callee.on('funA', FunACall);
    } catch (e){
      console.log(e)
    }

    if (want.parameters[formInfo.FormParam.IDENTITY_KEY] !== undefined) {
      let curFormId = want.parameters[formInfo.FormParam.IDENTITY_KEY];
      updateCardContent(curFormId, "EntryAbility", "router-welcome")
    }

  }

  onNewWant(want, launchParam) {
    try{
      let params = JSON.parse(want.parameters.params);
      console.log('onNewWant ' + params['message'])
      this.storage = new LocalStorage({'ext': params['message']})
    } catch (e){
       console.log(e)
    }

  }

  onWindowStageCreate(windowStage: window.WindowStage) {
    windowStage.loadContent('pages/Index', this.storage, (err, data) => {
     });
  }

  onDestroy(){
    console.log('onDestroy')
    // this.callee.off('funA')
  }

}

// 在收到call事件后会触发callee监听的方法
function FunACall(data) {
  // 获取call事件中传递的所有参数
  try{
    let params = JSON.parse(data.readString())
    if (params.formId !== undefined) {
      let curFormId = params.formId;
      updateCardContent(curFormId, "EntryAbility", "caller-welcome")
    }
  } catch (e){
    console.log(e)
  }

  return null;
}

function updateCardContent(formId, method, content){
  let formData = {
    'ITEM_TITLE': method, // 和卡片布局中对应
    'ITEM_CONTENT': content, // 和卡片布局中对应
  };
  let formInfo = formBindingData.createFormBindingData(formData)

  formProvider.updateForm(formId, formInfo).then((data) => {
    console.info('FormAbility updateForm success.' + JSON.stringify(data));
  }).catch((error) => {
    console.error('FormAbility updateForm failed: ' + JSON.stringify(error));
  })
}
修改应用入入口页面Index.ets
let storage = new LocalStorage()

@Entry(storage)
@Component
struct Page {
  @State message: string = 'Hello World'
  @LocalStorageProp('ext') extLocalStorageParms: string = '';

  aboutToAppear(){

    console.log(this.extLocalStorageParms)

    if(this.extLocalStorageParms){
      this.message = this.extLocalStorageParms
    }

  }

  build() {
    Row() {
      Column() {
        Text(this.message)
          .fontSize(50)
          .fontWeight(FontWeight.Bold)
      }
      .width('100%')
    }
    .height('100%')
  }

}
  1. 修改应用入入口页面Index.ets
let storage = new LocalStorage()

@Entry(storage)
@Component
struct Page {
  @State message: string = 'Hello World'
  @LocalStorageProp('ext') extLocalStorageParms: string = '';

  aboutToAppear(){

    console.log(this.extLocalStorageParms)

    if(this.extLocalStorageParms){
      this.message = this.extLocalStorageParms
    }

  }

  build() {
    Row() {
      Column() {
        Text(this.message)
          .fontSize(50)
          .fontWeight(FontWeight.Bold)
      }
      .width('100%')
    }
    .height('100%')
  }

}
  1. 修改EntryFormAbility.ets
import formInfo from '@ohos.app.form.formInfo';
import formBindingData from '@ohos.app.form.formBindingData';
import FormExtensionAbility from '@ohos.app.form.FormExtensionAbility';
import formProvider from '@ohos.app.form.formProvider';

export default class EntryFormAbility extends FormExtensionAbility {

  onAddForm(want) {
    // Called to return a FormBindingData object.
    let formId = want.parameters["ohos.extra.param.key.form_identity"];

    let formData: Record<string, string> = {
      'formId': formId
    };

    console.log('onAddForm '+formId)

    let data = formBindingData.createFormBindingData(formData);

    setTimeout(()=>{
      formProvider.updateForm(formId, data).then((data) => {
        console.info('FormAbility updateForm success.' + JSON.stringify(data));
      }).catch((error) => {
        console.error('FormAbility updateForm failed: ' + JSON.stringify(error));
      })
    }, 1500)

    return data
  }

  onCastToNormalForm(formId) {
    // Called when the form provider is notified that a temporary form is successfully
    // converted to a normal form.
    console.log('onCastToNormalForm')
  }

  onUpdateForm(formId) {
    // Called to notify the form provider to update a specified form.
    console.log('onUpdateForm')
  }

  onChangeFormVisibility(newStatus) {
    // Called when the form provider receives form events from the system.
    console.log('onChangeFormVisibility')
  }

  onFormEvent(formId, message) {
    // Called when a specified message event defined by the form provider is triggered.
    console.log(message)

    this.updateCardContent(formId)
  }

  onRemoveForm(formId) {
    // Called to notify the form provider that a specified form has been destroyed.
    console.log('onRemoveForm')
  }

  onAcquireFormState(want) {
    // Called to return a {@link FormState} object.
    return formInfo.FormState.READY;
  }

  updateCardContent(formId){
    let formData = {
      'ITEM_TITLE': 'EntryFormAbility', // 和卡片布局中对应
      'ITEM_CONTENT': 'welcome', // 和卡片布局中对应
    };
    let formInfo = formBindingData.createFormBindingData(formData)

    formProvider.updateForm(formId, formInfo).then((data) => {
      console.info('FormAbility updateForm success.' + JSON.stringify(data));
    }).catch((error) => {
      console.error('FormAbility updateForm failed: ' + JSON.stringify(error));
    })
  }
};
  1. 在module.json5中添加权限
"requestPermissions": [
      {
        "name":  "ohos.permission.KEEP_BACKGROUND_RUNNING",
        "usedScene": {
          "abilities": ["EntryAbility"],
          "when": "inuse"
        }
      }
    ]

 7. 最后一步,请确认你的打包方式没有选择“Deploy Multi Hap Packages”,  否则将无法看到服务卡片内容

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

cookie,sessionStorage,localStorage的区别及应用场景、http状态码含义、使用token登录、无感登录

浏览器的缓存机制提供了可以将用户数据存储在客户端上的方式&#xff0c;可以利用cookie&#xff0c;session等跟服务端进行数据交互。 浏览器的存储方式有哪些&#xff1f; 1.cookiesH5标准前的本地存储方式兼容性好&#xff0c;请求头自带cookie存储量小&#xff0c;资源浪费…

Apache Hive的基本使用语法(二)

Hive SQL操作 7、修改表 表重命名 alter table score4 rename to score5;修改表属性值 # 修改内外表属性 ALTER TABLE table_name SET TBLPROPERTIES("EXTERNAL""TRUE"); # 修改表注释 ALTER TABLE table_name SET TBLPROPERTIES (comment new_commen…

某某消消乐增加步数漏洞分析

一、漏洞简介 1&#xff09; 漏洞所属游戏名及基本介绍&#xff1a;某某消消乐&#xff0c;三消游戏&#xff0c;类似爱消除。 2&#xff09; 漏洞对应游戏版本及平台&#xff1a;某某消消乐Android 1.22.22。 3&#xff09; 漏洞功能&#xff1a;增加游戏步数。 4&#xf…

zabbix通过jmx监控Tongweb7企业版(by lqw)

一.tongweb配置相关启动参数 参考Zabbix 监控 Tomcat 服务 可以在控制台页面&#xff0c;或者在tongweb的安装目录的bin目录下&#xff0c;找到external.vmoptions&#xff0c;进行配置&#xff1a; 配置内容如下&#xff1a; -Dcom.sun.management.jmxremote -Dcom.sun.mana…

ES学习日记(一)-------单节点安装启动

基于ES7.4.1编写,其实一开始用的最新的8.1,但是问题太多了!!!!不稳定,降到7.4 下载好的安装包上传到服务器或虚拟机,创建ES目录,命令mkdir -p /路径xxxx 复制安装包到指定路径并解压: tar zxvf elasticsearch-8.1.0-linux-x86_64.tar.gz -C /usr/local/es/ 进入bin目录安装,命…

学习transformer模型-矩阵乘法;与点积dot product的关系;计算attention

矩阵乘法&#xff1a; 1、当矩阵A的列数&#xff08;column&#xff09;等于矩阵B的行数&#xff08;row&#xff09;时&#xff0c;A与B可以相乘。 Ankie的评论&#xff1a;一个人是站着的&#xff0c;一个人是躺着的&#xff0c;站着的高度躺着的长度。 在计算attention的时候…

基于单片机工业生产现场的光照强度控制系统设计

**单片机设计介绍&#xff0c;基于单片机工业生产现场的光照强度控制系统设计 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于单片机工业生产现场的光照强度控制系统设计概要主要包括以下几个关键部分&#xff1a;硬件设计、…

Android Studio不显示ADB Wi-Fi和Device Explorer的解决办法

我一直使用Android Studio。最近发现打开个别项目时&#xff0c;不显示ADB Wi-Fi和Device Explorer的图标&#xff0c;而有的项目就会显示。开始以为是插件错误&#xff0c;于是卸载了ADB Wi-Fi插件&#xff0c;并重新安装。但问题依旧。 后来发现&#xff0c;原来是在菜单的“…

二维双指针,滑动窗口

二维双指针 思路&#xff1a;考虑暴力做法&#xff0c;我们统计前缀和&#xff0c;然后枚举以 ( x 1 , y 1 ) (x_1,y_1) (x1​,y1​), ( x 2 , y 2 ) (x_2,y_2) (x2​,y2​)为左上&#xff0c;右下顶点的矩阵有多少是合法的&#xff0c;那么&#xff0c;这样的时间复杂度为 n 4…

【MySQL】15. 事务管理(重点) -- 1

1. CURD不加控制&#xff0c;会有什么问题&#xff1f; 2. CURD满足什么属性&#xff0c;能解决上述问题&#xff1f; 买票的过程得是原子的 ?买票互相应该不能影响 ?买完票应该要永久有效 ?买前&#xff0c;和买后都要是确定的状态? 3. 什么是事务&#xff1f; 事务就是…

ROUYI框架地址

1、原版系统地址与文档 https://gitee.com/dromara/RuoYi-Cloud-Plus?_fromgitee_search 源码地址 https://plus-doc.dromara.org/#/ruoyi-cloud-plus/home 后端地址 https://plus-doc.dromara.org/#/plus-ui/home 前端地址 前端代码地址&#xff1a; RuoYi-Vue-Plus: 多租户…

边缘计算与云计算总结

一. EdgeGallery 简介 MEC场景下的EdgeGallery是让资源边缘化&#xff0c;实时完成移动网络边缘的业务处理&#xff0c;MEC场景下的EdgeGallery让开发者能更便捷地使用 5G 网络能力&#xff0c;让5G能力在边缘触手可及。 EdgeGallery是由华为、信通院、中国移动、中国联通、…

玫瑰图和雷达图(自备)

目录 玫瑰图 数据格式 绘图基础 绘图升级&#xff08;文本调整&#xff09; 玫瑰图 下载数据data/2020/2020-11-24 mirrors_rfordatascience/tidytuesday - 码云 - 开源中国 (gitee.com) R语言绘图—南丁格尔玫瑰图 - 知乎 (zhihu.com) 数据格式 rm(list ls()) libr…

jmockit-01-test 之 jmockit 入门使用案例

拓展阅读 jmockit-01-jmockit 入门使用案例 jmockit-02-概览 jmockit-03-Mocking 模拟 jmockit-04-Faking 伪造 jmockit-05-代码覆盖率 mockito-01-入门介绍 mockito-02-springaop 整合遇到的问题&#xff0c;失效 jmockit 说明 jmockit 可以提供基于 mock 的测试能力…

​python学习之变量类型​

print单纯输中的十种数据类型只需要用print()函数即可&#xff0c;()里面直接写变量名。 下面重点介绍print格式输出&#xff1a; 第一种方法&#xff1a;一个萝卜一个坑&#xff0c;下面的代码中&#xff0c;{0}、{1}、{2}分别表示j,i,j*i&#xff0c;单引号里面是输出格式。…

【网安小白成长之路】3.MySQL环境配置以及常用命令(增删改查)

&#x1f42e;博主syst1m 带你 acquire knowledge&#xff01; ✨博客首页——syst1m的博客&#x1f498; &#x1f51e; 《网安小白成长之路(我要变成大佬&#x1f60e;&#xff01;&#xff01;)》真实小白学习历程&#xff0c;手把手带你一起从入门到入狱&#x1f6ad; &…

深度学习语义分割篇——DeepLabV2原理详解篇

&#x1f34a;作者简介&#xff1a;秃头小苏&#xff0c;致力于用最通俗的语言描述问题 &#x1f34a;专栏推荐&#xff1a;深度学习网络原理与实战 &#x1f34a;近期目标&#xff1a;写好专栏的每一篇文章 &#x1f34a;支持小苏&#xff1a;点赞&#x1f44d;&#x1f3fc;、…

R语言做两次分类,再做两两T检验,最终输出均值和pvalue

1.输入文件&#xff1a; 2.代码&#xff1a; setwd("E:/R/Rscripts/rG4相关绘图")# 加载所需的库 library(tidyverse)# 读取CSV文件 data <- read.csv("box-cds-ABD-不同类型rg4-2.csv", stringsAsFactors FALSE)# 组合Type1和Type2&#xff1a;通过…

GeoServer 2.25.0 发布新功能及升级

GeoServer 2.25.0版本现已提供下载&#xff08;bin、 war、 windows&#xff09;以及 文档和 扩展。 这是推荐用于生产用途的 GeoServer 的稳定版本。GeoServer 2.25.0 是与 GeoTools 31.0 和 GeoWebCache 1.25.0 结合使用的。 安全升级 此版本解决了多个安全漏洞&#xff0c…

3723. 字符串查询:做题笔记

目录 思路 代码 注意点 3723. 字符串查询 思路 这道题感觉和常见的前缀和问题不太一样&#xff0c;前缀和的另一种应用&#xff1a;可以统计次数。 这道题我们想判断一个单词的其中一段子序列A是否可以通过重新排列得到另一段子序列B。 我看到这道题的时候想着可能要判…