HarmonyOS 鸿蒙Next服务卡片

发布于 1周前 作者 bupafengyu 来自 鸿蒙OS

HarmonyOS 鸿蒙Next服务卡片

介绍

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

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

准备

  1. 请参照官方指导,创建一个Demo工程,选择Stage模型
  2. 熟读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(<span class="hljs-string"><span class="hljs-string">'router'</span></span>)
            .onClick(()=&gt;{
              postCardAction(<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>, {
                <span class="hljs-string"><span class="hljs-string">'action'</span></span>: <span class="hljs-string"><span class="hljs-string">'router'</span></span>,
                <span class="hljs-string"><span class="hljs-string">'abilityName'</span></span>: <span class="hljs-string"><span class="hljs-string">'EntryAbility'</span></span>,
                <span class="hljs-string"><span class="hljs-string">'params'</span></span>: {
                  <span class="hljs-string"><span class="hljs-string">'msgTest'</span></span>: <span class="hljs-string"><span class="hljs-string">'messageEvent'</span></span>
                }
              });
            })
        }

        Column(){
          Text(<span class="hljs-string"><span class="hljs-string">'message'</span></span>)
            .fontColor(Color.Green)
            .onClick(()=&gt;{
              postCardAction(<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>, {
                <span class="hljs-string"><span class="hljs-string">'action'</span></span>: <span class="hljs-string"><span class="hljs-string">'message'</span></span>,
                <span class="hljs-string"><span class="hljs-string">'params'</span></span>: {
                  <span class="hljs-string"><span class="hljs-string">'msgTest'</span></span>: <span class="hljs-string"><span class="hljs-string">'messageEvent'</span></span>
                }
              });
            })
        }

      }.height(<span class="hljs-string"><span class="hljs-string">'100%'</span></span>)
    }
    .width(<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.FULL_WIDTH_PERCENT)
    .height(<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.IMAGE_HEIGHT_PERCENT)

    Blank()
    Text(<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.ITEM_TITLE)
      .fontSize($r(<span class="hljs-string"><span class="hljs-string">'app.float.normal_title_font_size'</span></span>))
    Text(<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.ITEM_CONTENT)
      .maxLines(<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.MAX_LINES)
      .fontSize($r(<span class="hljs-string"><span class="hljs-string">'app.float.normal_content_font_size'</span></span>))
      .textOverflow({ overflow: TextOverflow.Ellipsis })
  }
  .width(<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.FULL_WIDTH_PERCENT)
  .height(<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.FULL_HEIGHT_PERCENT)
  .alignItems(HorizontalAlign.Start)
  .backgroundColor($r(<span class="hljs-string"><span class="hljs-string">'app.color.start_window_background'</span></span>))
}
.height(<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.FULL_HEIGHT_PERCENT)
.alignItems(VerticalAlign.Top)
.padding($r(<span class="hljs-string"><span class="hljs-string">'app.float.row_padding'</span></span>))
.onClick(() =&gt; {
  postCardAction(<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>, {
    <span class="hljs-string"><span class="hljs-string">"action"</span></span>: <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.ACTION_TYPE,
    <span class="hljs-string"><span class="hljs-string">"abilityName"</span></span>: <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.ABILITY_NAME,
    <span class="hljs-string"><span class="hljs-string">"params"</span></span>: {
      <span class="hljs-string"><span class="hljs-string">"message"</span></span>: <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.MESSAGE
    }
  });
})

} }<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>


     

3. 修改应用入口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(<span class="hljs-string"><span class="hljs-string">'onCreate '</span></span> + params[<span class="hljs-string"><span class="hljs-string">'message'</span></span>])
  <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.storage = <span class="hljs-keyword"><span class="hljs-keyword">new</span></span> LocalStorage({<span class="hljs-string"><span class="hljs-string">'ext'</span></span>: params[<span class="hljs-string"><span class="hljs-string">'message'</span></span>]})
} <span class="hljs-keyword"><span class="hljs-keyword">catch</span></span> (e){
  console.log(e)
}

<span class="hljs-keyword"><span class="hljs-keyword">try</span></span>{
  <span class="hljs-comment"><span class="hljs-comment">// 监听call事件所需的方法</span></span>
  <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.callee.on(<span class="hljs-string"><span class="hljs-string">'funA'</span></span>, FunACall);
} <span class="hljs-keyword"><span class="hljs-keyword">catch</span></span> (e){
  console.log(e)
}

<span class="hljs-keyword"><span class="hljs-keyword">if</span></span> (want.parameters[formInfo.FormParam.IDENTITY_KEY] !== <span class="hljs-literal"><span class="hljs-literal">undefined</span></span>) {
  <span class="hljs-keyword"><span class="hljs-keyword">let</span></span> curFormId = want.parameters[formInfo.FormParam.IDENTITY_KEY];
  updateCardContent(curFormId, <span class="hljs-string"><span class="hljs-string">"EntryAbility"</span></span>, <span class="hljs-string"><span class="hljs-string">"router-welcome"</span></span>)
}

}

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)); }) }

<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>

4. 修改应用入入口页面Index.ets

 

let storage = new LocalStorage()

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

aboutToAppear(){

console.log(<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.extLocalStorageParms)

<span class="hljs-keyword"><span class="hljs-keyword">if</span></span>(<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.extLocalStorageParms){
  <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.message = <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.extLocalStorageParms
}

}

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

}<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>

5. 修改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”];

<span class="hljs-keyword"><span class="hljs-keyword">let</span></span> formData: Record&lt;string, string&gt; = {
  <span class="hljs-string"><span class="hljs-string">'formId'</span></span>: formId
};

console.log(<span class="hljs-string"><span class="hljs-string">'onAddForm '</span></span>+formId)

<span class="hljs-keyword"><span class="hljs-keyword">let</span></span> data = formBindingData.createFormBindingData(formData);

setTimeout(()=&gt;{
  formProvider.updateForm(formId, data).then((data) =&gt; {
    console.info(<span class="hljs-string"><span class="hljs-string">'FormAbility updateForm success.'</span></span> + <span class="hljs-built_in"><span class="hljs-built_in">JSON</span></span>.stringify(data));
  }).catch((error) =&gt; {
    console.error(<span class="hljs-string"><span class="hljs-string">'FormAbility updateForm failed: '</span></span> + <span class="hljs-built_in"><span class="hljs-built_in">JSON</span></span>.stringify(error));
  })
}, <span class="hljs-number"><span class="hljs-number">1500</span></span>)

<span class="hljs-keyword"><span class="hljs-keyword">return</span></span> 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)

<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.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) =&gt; {
  console.info(<span class="hljs-string"><span class="hljs-string">'FormAbility updateForm success.'</span></span> + <span class="hljs-built_in"><span class="hljs-built_in">JSON</span></span>.stringify(data));
}).catch((error) =&gt; {
  console.error(<span class="hljs-string"><span class="hljs-string">'FormAbility updateForm failed: '</span></span> + <span class="hljs-built_in"><span class="hljs-built_in">JSON</span></span>.stringify(error));
})

} };<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>

6. 在module.json5中添加权限

“requestPermissions”: [
{
“name”:  “ohos.permission.KEEP_BACKGROUND_RUNNING”,
“usedScene”: {
“abilities”: [“EntryAbility”],
“when”: “inuse”
}
}
]<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>

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

2 回复

取消勾选“Deploy Multi Hap Packages”,卡片内容还是空白。

运行时,选OpenHarmony App 》entry  ,卡片内容才可见,与是否勾选Deploy Multi Hap Packages  没关系。

HarmonyOS 鸿蒙Next服务卡片是一种轻量级的服务组件,允许用户快速访问应用的特定功能或服务信息。这些卡片通常放置在设备桌面上,展示实时更新的信息或快速操作入口。开发者可以定义卡片的布局、内容以及交互方式,并通过HarmonyOS提供的API进行实现。卡片背后连接服务,实现数据动态更新。如需了解更多开发细节,请参考官方文档或教程。如果问题依旧没法解决请加我微信,我的微信是itying888。

回到顶部