0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

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

jf_46214456 来源:jf_46214456 作者:jf_46214456 2024-03-28 22:11 次阅读

介绍

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

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

准备

  1. 请参照[官方指导],创建一个Demo工程,选择Stage模型

实践总结

  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));
  })
}
  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"
        }
      }
    ]
  1. 最后一步,请确认你的打包方式没有选择“Deploy Multi Hap Packages”, 否则将无法看到服务卡片内容

审核编辑 黄宇

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 鸿蒙
    +关注

    关注

    55

    文章

    1629

    浏览量

    42120
  • 鸿蒙OS
    +关注

    关注

    0

    文章

    140

    浏览量

    4279
收藏 人收藏

    评论

    相关推荐

    鸿蒙OS实战开发:【多设备自适应服务卡片

    服务卡片的布局和使用,其中卡片内容显示使用了一次开发,多端部署的能力实现多设备自适应。 用到了卡片扩展模块接口,[@ohos.app.
    的头像 发表于 04-09 09:20 483次阅读
    <b class='flag-5'>鸿蒙</b><b class='flag-5'>OS</b>实战<b class='flag-5'>开发</b>:【多设备自适应<b class='flag-5'>服务</b><b class='flag-5'>卡片</b>】

    鸿蒙os系统是什么意思 鸿蒙os系统有什么作用

    毫秒级。鸿蒙OS实现模块化耦合,对应不同设备可弹性部署,鸿蒙OS有三层架构,第一层是内核,第二层是基础服务,第三层是程序框架,可用于手机、平
    发表于 12-17 11:34

    HarmonyOS服务卡片快速开发

    HarmonyOS服务卡片快速开发
    发表于 06-19 13:52

    #HarmonyOS征文#鸿蒙卡片-物联网DTU污水液位计卡片

    onCreateForm、onUpdateForm和onDeleteForm处理创建卡片、更新卡片以及删除卡片等请求,提供相应的卡片服务
    发表于 06-26 08:59

    一文看懂HarmonyOS服务卡片运行原理和开发方法

    一、服务卡片概述1、什么是服务卡片HarmonyOS 2的到来,让很多开发者眼前一亮。HarmonyOS 2推出的
    发表于 07-27 17:21

    木棉花:【资料汇集】服务卡片相关学习资料的汇总

    前言木棉花组织是对鸿蒙学习的相关资源进行整合,由深圳大学学生团队负责,长期更新。此次是将电子发烧友上的有关鸿蒙服务卡片学习相关资料进行汇总,以下是部分资料的展示,更多资料收纳在我们组织
    发表于 08-12 23:48

    完整服务卡片项目开发,为Bilibili添加服务卡片

    已经被鸿蒙刷屏了。从安卓到鸿蒙,最直观的变化应该就是服务卡片了。我也是在学习鸿蒙的同时,实际体验一下服务
    发表于 09-03 20:49

    HarmonyOS卡片开发--服务卡片概述

    服务卡片提供方实例管理模块:由卡片提供方开发者实现,负责对卡片管理
    发表于 09-22 14:10

    求助,请问鸿蒙卡片如何去掉应用的桌面图标?

    服务的用户应用程序形态这是官网的介绍,我们创建一个APP,再创建一个卡片,然后我们要怎么让桌面只有卡片存在,而没有APP存在之所以会有这个问题,是因为当我们原来的APP是安卓的,然后可以安装到
    发表于 06-14 10:18

    鸿蒙原生应用开发-折叠屏、平板设备服务卡片适配

    一、多设备卡片适配原则 为不同尺寸的卡片提供不同的功能 在卡片开发过程中请考虑适配不同尺寸的设备,特别是在折叠屏和平板设备上,设备屏幕尺寸的变化直接影响了
    发表于 11-16 10:10

    鸿蒙OS千呼万唤始出来

    从2019年在华为开发者大会上公布鸿蒙OS开始,围绕着鸿蒙OS的话题始终没有停止过,而华为也坚持不懈地为
    的头像 发表于 06-01 15:56 2335次阅读

    B站添加鸿蒙服务卡片教程

    ‍‍‍‍‍‍‍‍ 6 月 2 日鸿蒙发布,今年的六月已经被鸿蒙刷屏了。从安卓到鸿蒙,最直观的变化应该就是服务卡片了。我也是在学习
    的头像 发表于 08-12 10:07 2362次阅读
    B站添加<b class='flag-5'>鸿蒙</b><b class='flag-5'>服务</b><b class='flag-5'>卡片</b>教程

    一款鸿蒙版的哔哩哔哩服务卡片应用案例

    介绍 这是一款纯鸿蒙版的哔哩哔哩服务卡片应用。 6月2日鸿蒙发布,今年的六月已经被鸿蒙刷屏了。从安卓到鸿
    发表于 04-07 09:42 0次下载

    用Java开发HarmonyOS服务卡片

    卡片服务:由卡片提供方开发者实现,开发者实现 onCreateForm、onUpdateForm 和 onDeleteForm 处理创建
    的头像 发表于 04-26 11:04 1244次阅读

    如何在OpenHarmony上开发服务卡片

    本篇文章我们将分享如何在 OpenHarmony 上开发服务卡片
    的头像 发表于 04-10 11:12 675次阅读