HarmonyOS实战一原子化服务初尝试(ClockFACardDemo学习)

2023-10-30 00:38

本文主要是介绍HarmonyOS实战一原子化服务初尝试(ClockFACardDemo学习),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

【本文正在参与“有奖征文 | HarmonyOS征文大赛”活动】


写在前面

  • 看到有一个HarmonyOS征文大赛”的活动,所以准备在学习下,拥抱国产操作系统, 之前学HarmonyOS 照着官网写了一个Holle World(关于HarmonyOS的环境搭建,基本目录结构,简单Holle World实现)
  • 博文主题是关于服务卡片,原子化服务的,关于这个,在HarmonyOS 2.0发布会上有看到有讲。
  • 博文由两部分内容构成: 服务卡片(原子化服务)的一些基本概念,官网Demo的代码学习
  • 依旧,把HarmonyOS Developer的地址留在这里,博文好多都是文档里的东西,建议小伙伴看文档学习,对于英语很垃圾的我来讲,这回不用翻译啦,有中文版,没障碍。嗯,博文有理解不对的地方请小伙伴积极留言

时光不能倒流,如果人可以从80岁开始倒过来活的话,人生一定会更加精彩。--------任正非


在最新发布的Harmonyos 2版本的新系统中, "服务卡片"服务成为一大亮点:1

  • 全新的Harmonyos桌面简洁有序,上滑App生成服务卡片,在桌面即可呈现更丰富的信息。
  • 卡片内容实时更新,只需一管即可获取所需信息,省去了打开App的时间。
  • 卡片可大可小、可藏可显,还能够个性化定制,让每个桌面独一无二。
  • 同时,卡片也是原子化服务的载体,在服务中心可以轻松获取随时分享,无需下载安装,一步到位获取各种服务。

嗯,下面开始我们愉快的HarmonyOS----服务卡片之旅吧!

什么是原子化服务

在学习服务卡片之前,我们先来了解一下什么是原子化服务

原子化服务HarmonyOS 提供的一种面向未来服务提供方式,是有独立入口的(用户可通过点击碰一碰扫一扫等方式直接触发)、免安装的(无需显式安装,由系统程序框架后台安装后即可使用)、可为用户提供一个或多个便捷服务的用户程序形态。原子化服务基于 HarmonyOS API 开发,支持运行在1+8+N设备上,供用户在合适的场景、合适的设备上便捷使用。2

例如:某传统方式的需要安装的“购物应用A”,在按照原子化服务理念调整设计后,成为由“商品浏览”“购物车”“支付”等多个便捷服务组成的、可以免安装“购物原子化服务A”。

文档里说的有些官方,个人理解,原子化服务本质还是终端应用(我们先这样理解它,后面在具体描述),一个代替终端应用提供新服务提供方式的存在。抛去服务流转/分享设备控制之类的分布式能力不说,在设计上类似于微信小程序流应用的优点的结合体,在入口设计等方面感觉像微信小程序一样便捷,但是不需要载体(微信,浏览器等),在整体体验方面又像流应用一样,不需要显示安装。但是(体验应该要好于流应用),个人理解不谈分布式能力,像是用瘦客户的方式有了胖客户的体验。

所谓原子化服务,个人理解,即将原来的终端应用功能为粒度细化(原子化),分离成一个个服务,多个服务之间通信完成需求,不在依托于具体的终端应用。类似于一种极限的思想,细化功能粒度,逐渐减少对终端应用总体的依赖。

关于原子化服务的更多内容,小伙伴移步HarmonyOS Developer:什么是原子化服务

原子化服务具有如下基本要素 必须设计开发

  • 基础信息
  • 服务卡片

简单了解一下基础信息:即每个原子化服务有独立的图标名称描述快照基础信息应能够准确反映服务提供方的特征服务的核心体验,并与其他关联应用和服务保持同步最新

其他的没问题,我们重点看一下快照这里快照为与原子化服务关联小尺寸服务卡片截图。截图应为理想的服务状态,让用户一眼可知服务内容。需提供直角图片,由展示快照的应用进行圆角裁切.,
在这里插入图片描述

快照为与原子化服务关联小尺寸服务卡片截图.下面我们看看服务卡片是什么

什么是服务卡片,存在意义。

关于服务卡片,我们看看HarmonyOS Developer中设计文档是怎么讲的。https://developer.harmonyos.com/cn/docs/design/des-guides/service-widget-about-0000001144696239

服务卡片介绍

原子化服务/应用的重要信息卡片的形式展示在桌面,用户可通过快捷手势使用卡片,通过轻量交互行为实现服务直达减少层级跳转的目的。
在这里插入图片描述
这是我的手机的时钟应用的类似的服务卡片的这样一个东西,嗯,这个不算是服务卡片的。和实际的卡片还是有很大差距的.
在这里插入图片描述
这是P40模拟器上的时钟,带有服务卡片选项的,真正的服务卡片

服务卡片核心理念在于提供用户容易使用且一目了然的信息内容,将智慧化能力融入到服务卡片的体验中供用户选择使用,同时满足在不同终端设备上的展示和自适应

服务卡片的构成

服务卡片的显示主要由内容主体归属的 App 名称构成,在临时态下会出现Pin钮的操作特征,点击按钮用户可快捷将·卡片固定·在桌面显示。开发者应该借助卡片内容和卡片名称清晰地向用户传递所要提供的服务信息。

嗯,关于服务卡片的其他这里不多介绍,小伙伴了解更多移步到
官方文档:https://developer.harmonyos.com/cn/docs/design/des-guides/service-widget-about-0000001144696239
关于服务卡片的 交互设计,内容设计,视觉风格 等都有详细讲解。

简单实践-原子化服务之服务卡片初尝试

检验真理的唯一标准是实践 ,说了这么多,编码试一下。先做出点东西来,然后我们在慢慢研究原理。嘻嘻。

官方给出一个java开发时钟卡片的例子。

  • 官方教程文档见Java卡片开发指导
  • 代码见https://gitee.com/openharmony/codelabs/tree/master/ClockFACardDemo

这里,官方的Demo很简单,文档很完整小伙伴一看就明白。感兴趣的可以拉下来跑跑,我们看看具体的实现成果
在这里插入图片描述
主体结构分析

HarmonyOS支持应用以Ability为单位进行部署。Ability可以分为FA(Feature Ability)PA(Particle Ability)两种类型

  • FA:Page Ability:由上次的Holle Word我们可以知道,在HarmonyOS中,提供了Ability和·AbilitySlice·两个基础类, 一个有界面Ability可以由一个或多个AbilitySlice构成,AbilitySlice主要用于承载单个页面的具体逻辑实现界面UI,是应用显示、运行和跳转的最小单元。所以ClockCardSlice为应用的主界面UI.,即这个Demo中只有一个Page AbilitySlice(ClockCardSlice)ClockCardSlice是通过setMainRoute()方法来指定,指定当前Ability的默认页面。
    在这里插入图片描述
  • PA:Service Ability: 由于时钟是需要实时更新的,所以需要Service Ability来实时运行后台任务,即TimerAbility为时钟更新的Service Ability
    在这里插入图片描述
    layout文件夹:页面布局文件夹,由于时钟卡片Codelab涉及两个尺寸:22和24,因此需要新建两个.xml文件用于页面布局。
    config.json:配置文件,用于卡片和Service Ability的声明。

关于 Ability 的更多介绍PageAbility和ServiceAbility的生命周期回调等知识,小伙伴移步HarmonyOS Developer:https://developer.harmonyos.com/cn/docs/documentation/doc-guides/ability-service-lifecycle-0000000000044472

时钟FA卡片应用主要设计到创建更新删除卡片,对象关系映射型数据库使用以及如何启动计时器服务卡片布局等。下面我们就这几部分代码分析,学习。

对象关系映射型数据库的使用

HarmonyOS对象关系映射(Object Relational Mapping,ORM)数据库是一款基于SQLite(一款轻型的数据库,是遵守ACID的关系型数据库管理系统。)的数据库框架,屏蔽了底层SQLite数据库的SQL操作,针对实体关系提供了增删改查等一系列的面向对象接口。应用开发者不必再去编写复杂的SQL语句, 以操作对象的形式来操作数据库,提升效率的同时也能聚焦于业务开发。

个人感觉和使用JPA有相同的地方,如果有使用过JPA的,那这个很容易理解。

对象关系映射数据库的三个主要组件:

  • 数据库:被开发者用@Database注解,且继承了OrmDatabase的类,对应关系型数据库
  • 实体对象:被开发者用@Entity注解,且继承了OrmObject的类,对应关系型数据库中的表
  • 对象数据操作接口:包括数据库操作的入口OrmContext类谓词接口(OrmPredicate)等。
package com.huawei.cookbooks.database;import ohos.data.orm.OrmObject;
import ohos.data.orm.annotation.Entity;
import ohos.data.orm.annotation.PrimaryKey;/*** Card Table  存放对象关系映射数据库相关对象的目录。*/
// TODO 定义一个对象关系映射的数据表
@Entity(tableName = "form")
public class Form extends OrmObject {// TODO 声明主键@PrimaryKey()private Long formId;private String formName;private Integer dimension;public Form(Long formId, String formName, Integer dimension) {this.formId = formId;this.formName = formName;this.dimension = dimension;}public Form() { }public Integer getDimension() {return dimension;}public void setDimension(Integer dimension) {this.dimension = dimension;}public Long getFormId() {return formId;}public void setFormId(Long formId) {this.formId = formId;}public String getFormName() {return formName;}public void setFormName(String formName) {this.formName = formName;}
}
package com.huawei.cookbooks.database;import ohos.data.orm.OrmDatabase;
import ohos.data.orm.annotation.Database;/*** Card Database  卡片数据库对象,用于创建卡片数据库。*/
@Database(entities = {Form.class},version = 1)
public abstract class FormDatabase extends OrmDatabase { }
  • 创建数据库:开发者需要定义一个表示数据库的类,继承OrmDatabase,再通过@Database注解内的entities属性指定哪些数据模型类(表)属于这个数据库。
    • 属性:version:数据库版本号entities:数据库内包含的表
  • 创建数据表。开发者可通过创建一个继承了OrmObject并用@Entity注解的类,获取数据库实体对象,也就是表的对象。
    • tableName:表名primaryKeys:主键名,一个表里只能有一个主键,一个主键可以由多个字段组成foreignKeys:外键列表indices:索引列表

建立数据库连接
在这里插入图片描述
提供对数据库相关操作的方法

package com.huawei.cookbooks.utils;import com.huawei.cookbooks.database.Form;import ohos.data.orm.OrmContext;
import ohos.data.orm.OrmPredicates;import java.util.List;/*** Card Database Operations 提供对数据库相关操作的方法*/
public class DatabaseUtils {/*** delete data** @param formId form id* @param connect data connection*/public static void deleteFormData(long formId, OrmContext connect) {OrmPredicates where = connect.where(Form.class);where.equalTo("formId", formId);List<Form> query = connect.query(where);if (!query.isEmpty()) {connect.delete(query.get(0));connect.flush();}}/*** add card info** @param form card object* @param connect data connection*/public static void insertForm(Form form, OrmContext connect) {connect.insert(form);connect.flush();}
}

对于对象关系映射型数据库的学习,小伙伴移步HarmonyOS Developer学习,这个不在多介绍:https://developer.harmonyos.com/cn/docs/documentation/doc-guides/database-orm-guidelines-0000000000030063

卡片应用初始化:启动卡片定时器服务

在这里插入图片描述

创建、删除卡片

我们想看看官方文档里怎么说。创建一个FormAbility,覆写卡片相关回调函数

  • onCreateForm(Intent intent)
  • onUpdateForm(long formId)
  • onDeleteForm(long formId)
  • onCastTempForm(long formId)
  • onEventNotify(Map<Long, Integer> formEvents)

onCreateForm(Intent intent)中,当卡片使用方请求获取卡片时,卡片提供方会被拉起并调用onCreateForm(Intent intent)回调,intent中会带有卡片ID卡片名称临时卡片标记卡片外观规格信息,分别通过

  • AbilitySlice.PARAM_FORM_IDENTITY_KEY、
  • AbilitySlice.PARAM_FORM_NAME_KEY、
  • AbilitySlice.PARAM_FORM_TEMORARY_KEY和
  • AbilitySlice.PARAM_FORM_DIMENSION_KEY按需获取。

提供方可以通过AbilitySlice.PARAM_FORM_CUSTOMIZE_KEY获取卡片使用方设置的自定义数据。

卡片使用方:显示卡片内容的宿主应用,控制卡片在宿主中展示的位置
卡片提供方:提供卡片显示内容的HarmonyOS Service/HarmonyOS Application,控制卡片的显示内容、控件布局以及控件点击事件

public class FormAbility extends Ability {......@Overridepublic void onStart(Intent intent) {super.onStart(intent);......}@Overrideprotected ProviderFormInfo onCreateForm(Intent intent) {long formId = intent.getLongParam(AbilitySlice.PARAM_FORM_IDENTITY_KEY, 0);String formName = intent.getStringParam(AbilitySlice.PARAM_FORM_NAME_KEY);int specificationId = intent.getIntParam(AbilitySlice.PARAM_FORM_DIMENSION_KEY, 0);boolean tempFlag = intent.getBooleanParam(AbilitySlice.PARAM_FORM_TEMPORARY_KEY, false);// 获取自定义数据IntentParams intentParams = intent.getParam(AbilitySlice.PARAM_FORM_CUSTOMIZE_KEY);HiLog.info(LABEL_LOG, "onCreateForm: " + formId + " " + formName + " " + specificationId);// 开发者需要根据卡片的名称以及外观规格获取对应的xml布局并构造卡片对象,此处ResourceTable.Layout_form_ability_layout_2_2仅为示例ProviderFormInfo formInfo = new ProviderFormInfo(ResourceTable.Layout_form_ability_layout_2_2, this);// 获取卡片信息String formData = getInitFormData(formName, specificationId);ComponentProvider componentProvider = new ComponentProvider();componentProvider.setText(ResourceTable.Id_title, "formData-" + formData);formInfo.mergeActions(componentProvider);......HiLog.info(LABEL_LOG, "onCreateForm finish.......");return formInfo;}@Overrideprotected void onDeleteForm(long formId) {super.onDeleteForm(formId);// 删除卡片实例数据,需要由开发者实现deleteFormInfo(formId);......}@Override// 若卡片支持定时更新/定点更新/卡片使用方主动请求更新功能,则提供方需要覆写该方法以支持数据更新protected void onUpdateForm(long formId) {super.onUpdateForm(formId);// 更新卡片信息,由开发者实现......}@Overrideprotected void onCastTempForm(long formId) {// 使用方将临时卡片转换为常态卡片触发,提供方需要做相应的处理,将数据持久化。super.onCastTempForm (formId);......}@Overrideprotected void onEventNotify(Map<Long, Integer> formEvents) {// 使用方发起可见或者不可见通知触发,提供方需要做相应的处理,比如卡片可见时刷新卡片,仅系统应用能收到该回调。super.onEventNotify(formEvents);......}
}

了解更多见Java卡片开发指导:https://developer.harmonyos.com/cn/docs/documentation/doc-guides/ability-service-widget-provider-java-0000001104082220

我们看看Demo里怎么写的:覆盖了

  • onDeleteForm(当卡片被删除时,需要重写onDeleteForm方法,根据卡片id删除卡片实例数据:)
  • onCreateForm·(当卡片使用方请求获取卡片时,卡片提供方会被拉起并调用onCreateForm回调函数,完成卡片信息的初始化)
package com.huawei.cookbooks;import com.huawei.cookbooks.database.Form;
import com.huawei.cookbooks.database.FormDatabase;
import com.huawei.cookbooks.slice.ClockCardSlice;
import com.huawei.cookbooks.utils.ComponentProviderUtils;
import com.huawei.cookbooks.utils.DatabaseUtils;import ohos.aafwk.ability.Ability;
import ohos.aafwk.ability.AbilitySlice;
import ohos.aafwk.ability.ProviderFormInfo;
import ohos.aafwk.content.Intent;
import ohos.aafwk.content.Operation;
import ohos.agp.components.ComponentProvider;
import ohos.data.DatabaseHelper;
import ohos.data.orm.OrmContext;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;/*** Card Main Ability  主程序入口,由DevEco Studio生成,开发者需要重写创建、删除卡片等方法。*/
public class MainAbility extends Ability {private static final HiLogLabel LABEL_LOG = new HiLogLabel(0, 0, "com.huawei.cookbooks.MainAbility");// TODO 卡片一private static final int DEFAULT_DIMENSION_2X2 = 2;//  TODO 卡片二private static final int DEFAULT_DIMENSION_2X4 = 3;private static final String EMPTY_STRING = "";private static final int INVALID_FORM_ID = -1;private long formId;// TODO 卡片对象private ProviderFormInfo formInfo;// TODO 数据库服务对象private DatabaseHelper helper = new DatabaseHelper(this);// TODO 数据库连接对象private OrmContext connect;/***  TODO 项目启动方法,启动定时服务* @param intent*/@Overridepublic void onStart(Intent intent) {super.onStart(intent);// TODO  建立数据库连接connect = helper.getOrmContext("FormDatabase", "FormDatabase.db", FormDatabase.class);// TODO 启动TimerAbilityIntent intentService = new Intent();Operation operation =new Intent.OperationBuilder().withDeviceId("").withBundleName(getBundleName()).withAbilityName(TimerAbility.class.getName()).build();intentService.setOperation(operation);startAbility(intentService);super.setMainRoute(ClockCardSlice.class.getName());}
//当卡片使用方请求获取卡片时,卡片提供方会被拉起并调用onCreateForm回调函数,完成卡片信息的初始化@Overrideprotected ProviderFormInfo onCreateForm(Intent intent) {if (intent == null) {return new ProviderFormInfo();}// 获取卡片idformId = INVALID_FORM_ID;if (intent.hasParameter(AbilitySlice.PARAM_FORM_IDENTITY_KEY)) {formId = intent.getLongParam(AbilitySlice.PARAM_FORM_IDENTITY_KEY, INVALID_FORM_ID);} else {return new ProviderFormInfo();}// 获取卡片名称String formName = EMPTY_STRING;if (intent.hasParameter(AbilitySlice.PARAM_FORM_NAME_KEY)) {formName = intent.getStringParam(AbilitySlice.PARAM_FORM_NAME_KEY);}// 获取卡片规格int dimension = DEFAULT_DIMENSION_2X2;if (intent.hasParameter(AbilitySlice.PARAM_FORM_DIMENSION_KEY)) {dimension = intent.getIntParam(AbilitySlice.PARAM_FORM_DIMENSION_KEY, DEFAULT_DIMENSION_2X2);}int layoutId = ResourceTable.Layout_form_image_with_info_date_card_2_2;if (dimension == DEFAULT_DIMENSION_2X4) {layoutId = ResourceTable.Layout_form_image_with_info_date_card_2_4;}formInfo = new ProviderFormInfo(layoutId, this);// 存储卡片信息Form form = new Form(formId, formName, dimension);ComponentProvider componentProvider = ComponentProviderUtils.getComponentProvider(form, this);formInfo.mergeActions(componentProvider);if (connect == null) {connect =helper.getOrmContext("FormDatabase", "FormDatabase.db", FormDatabase.class);}try {DatabaseUtils.insertForm(form, connect);} catch (Exception e) {DatabaseUtils.deleteFormData(form.getFormId(), connect);}return formInfo;}// todo 当卡片被删除时,需要重写onDeleteForm方法,根据卡片id删除卡片实例数据:  @Overrideprotected void onDeleteForm(long formId) {super.onDeleteForm(formId);// 删除数据库中的卡片信息DatabaseUtils.deleteFormData(formId, connect);}
}
更新卡片

卡片数据服务:为了方便处理·时钟卡片刷新的定时任务,我们创建了一个Service Ability定时更新卡片信息,在TimerAbility.java

package com.huawei.cookbooks;import com.huawei.cookbooks.database.Form;
import com.huawei.cookbooks.database.FormDatabase;
import com.huawei.cookbooks.utils.ComponentProviderUtils;
import com.huawei.cookbooks.utils.DatabaseUtils;import ohos.aafwk.ability.Ability;
import ohos.aafwk.ability.FormException;
import ohos.aafwk.content.Intent;
import ohos.agp.components.ComponentProvider;
import ohos.data.DatabaseHelper;
import ohos.data.orm.OrmContext;
import ohos.data.orm.OrmPredicates;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;
import ohos.rpc.IRemoteObject;import java.util.List;
import java.util.Timer;
import java.util.TimerTask;/*** Time PA  时钟更新Service Ability。*/
public class TimerAbility extends Ability {private static final HiLogLabel LABEL_LOG = new HiLogLabel(3, 0xD001100, "Demo");private static final long SEND_PERIOD = 1000L;private DatabaseHelper helper = new DatabaseHelper(this);private OrmContext connect;@Overridepublic void onStart(Intent intent) {HiLog.info(LABEL_LOG, "TimerAbility::onStart");connect = helper.getOrmContext("FormDatabase", "FormDatabase.db", FormDatabase.class);startTimer();super.onStart(intent);}// 卡片更新定时器,每秒更新一次private void startTimer() {Timer timer = new Timer();timer.schedule(new TimerTask() {@Overridepublic void run() {updateForms();}},0,SEND_PERIOD);}private void updateForms() {// 从数据库中获取卡片信息OrmPredicates ormPredicates = new OrmPredicates(Form.class);List<Form> formList = connect.query(ormPredicates);// 更新时分秒if (formList.size() <= 0) {return;}for (Form form : formList) {// 遍历卡片列表更新卡片ComponentProvider componentProvider = ComponentProviderUtils.getComponentProvider(form, this);try {Long updateFormId = form.getFormId();updateForm(updateFormId, componentProvider);} catch (FormException e) {// 删除不存在的卡片DatabaseUtils.deleteFormData(form.getFormId(), connect);HiLog.error(LABEL_LOG, "onUpdateForm updateForm error");}}}@Overridepublic void onBackground() {super.onBackground();HiLog.info(LABEL_LOG, "TimerAbility::onBackground");}@Overridepublic void onStop() {super.onStop();HiLog.info(LABEL_LOG, "TimerAbility::onStop");}
}

有关卡片组件的更新,封装了ComponentProviderUtils这个类,在卡片更新时候,通过调用updateForm方法,传入参数formIdcomponentProvider.
在这里插入图片描述

package com.huawei.cookbooks.utils;import com.huawei.cookbooks.ResourceTable;
import com.huawei.cookbooks.database.Form;import ohos.agp.components.ComponentProvider;
import ohos.agp.utils.Color;
import ohos.app.Context;import java.util.Calendar;/*** Component ProviderUtils 提供获取ComponentProvider对象的方法,用于卡片组件的更新。*/
public class ComponentProviderUtils {// 当前星期颜色private static Color nowWeekColor = new Color(Color.rgb(255, 245, 238));// 原色星期private static Color primaryWeekColor = new Color(Color.rgb(192, 192, 192));private static final int WEEK_DAYS = 7;private static final int STRING_LENGTH = 2;private static final int DIM_VERSION = 3;private static final int SUNDAY = 1;private static final int MONDAY = 2;private static final int TUESDAY = 3;private static final int WEDNESDAY = 4;private static final int THURSDAY = 5;private static final int FRIDAY = 6;private static final int SATURDAY = 7;/*** Obtain the day of the week** @return week*/public static int getWeekDayId() {Calendar calendar = Calendar.getInstance();int week = calendar.get(Calendar.DAY_OF_WEEK);int result = getWeekIdResult(week);return result;}/*** get week component id** @param week week* @return component id*/private static int getWeekIdResult(int week) {int result = ResourceTable.Id_mon;switch (week) {case SUNDAY:result = ResourceTable.Id_sun;break;case MONDAY:result = ResourceTable.Id_mon;break;case TUESDAY:result = ResourceTable.Id_tue;break;case WEDNESDAY:result = ResourceTable.Id_wed;break;case THURSDAY:result = ResourceTable.Id_thu;break;case FRIDAY:result = ResourceTable.Id_fri;break;case SATURDAY:result = ResourceTable.Id_sat;break;default:result = ResourceTable.Id_sun;break;}return result;}/*** Obtains the ComponentProvider object** @param form form info* @param context context* @return component provider*/public static ComponentProvider getComponentProvider(Form form, Context context) {int layoutId = ResourceTable.Layout_form_image_with_info_date_card_2_2;if (form.getDimension() == DIM_VERSION) {layoutId = ResourceTable.Layout_form_image_with_info_date_card_2_4;}ComponentProvider componentProvider = new ComponentProvider(layoutId, context);setComponentProviderValue(componentProvider);return componentProvider;}/*** Time converted to string** @param time time* @return time string*/private static String int2String(int time) {String timeString;if (String.valueOf(time).length() < STRING_LENGTH) {timeString = "0" + time;} else {timeString = time + "";}return timeString;}/*** Set the value of componentProvider** @param componentProvider component provider*/private static void setComponentProviderValue(ComponentProvider componentProvider) {Calendar now = Calendar.getInstance();int hour = now.get(Calendar.HOUR_OF_DAY);int min = now.get(Calendar.MINUTE);int second = now.get(Calendar.SECOND);String hourString = int2String(hour);String minString = int2String(min);String secondString = int2String(second);componentProvider.setText(ResourceTable.Id_date, DateUtils.getCurrentDate("yyyy-MM-dd"));componentProvider.setText(ResourceTable.Id_hour, hourString);componentProvider.setText(ResourceTable.Id_min, minString);componentProvider.setText(ResourceTable.Id_sec, secondString);// 获取当前星期int weekDayId = getWeekDayId();componentProvider.setTextColor(weekDayId, nowWeekColor);// 将前一天的星期改回原色int lastWeekId = getLastWeekDayId();componentProvider.setTextColor(lastWeekId, primaryWeekColor);}/*** obtain previous day of the week** @return previous day of the week*/public static int getLastWeekDayId() {Calendar calendar = Calendar.getInstance();int week = calendar.get(Calendar.DAY_OF_WEEK);int lastWeek;if (week == 1) {lastWeek = WEEK_DAYS;} else {lastWeek = week - 1;}return getWeekIdResult(lastWeek);}
}

对于服务卡片我们就先学的这里,学完了Demo,小伙伴是不是蠢蠢欲动呢?自己写一个类似的Demo应该是在可能完成的范围之内的,感兴趣的小伙伴赶快试试吧。之后我准备基于官方的一个Demo(一个音频播放器),搞一个服务卡片。嗯,时间关系,今天就先搞到这里。

【本文正在参与“有奖征文 | HarmonyOS征文大赛”活动】


  1. 活动页:https://marketing.csdn.net/p/ad3879b53f4b8b31db27382b5fc65bbc ↩︎

  2. https://developer.harmonyos.com/cn/docs/design/des-guides/service-overview-0000001139795693 ↩︎

这篇关于HarmonyOS实战一原子化服务初尝试(ClockFACardDemo学习)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/304455

相关文章

精选20个好玩又实用的的Python实战项目(有图文代码)

《精选20个好玩又实用的的Python实战项目(有图文代码)》文章介绍了20个实用Python项目,涵盖游戏开发、工具应用、图像处理、机器学习等,使用Tkinter、PIL、OpenCV、Kivy等库... 目录① 猜字游戏② 闹钟③ 骰子模拟器④ 二维码⑤ 语言检测⑥ 加密和解密⑦ URL缩短⑧ 音乐播放

SQL Server跟踪自动统计信息更新实战指南

《SQLServer跟踪自动统计信息更新实战指南》本文详解SQLServer自动统计信息更新的跟踪方法,推荐使用扩展事件实时捕获更新操作及详细信息,同时结合系统视图快速检查统计信息状态,重点强调修... 目录SQL Server 如何跟踪自动统计信息更新:深入解析与实战指南 核心跟踪方法1️⃣ 利用系统目录

java中pdf模版填充表单踩坑实战记录(itextPdf、openPdf、pdfbox)

《java中pdf模版填充表单踩坑实战记录(itextPdf、openPdf、pdfbox)》:本文主要介绍java中pdf模版填充表单踩坑的相关资料,OpenPDF、iText、PDFBox是三... 目录准备Pdf模版方法1:itextpdf7填充表单(1)加入依赖(2)代码(3)遇到的问题方法2:pd

PyTorch中的词嵌入层(nn.Embedding)详解与实战应用示例

《PyTorch中的词嵌入层(nn.Embedding)详解与实战应用示例》词嵌入解决NLP维度灾难,捕捉语义关系,PyTorch的nn.Embedding模块提供灵活实现,支持参数配置、预训练及变长... 目录一、词嵌入(Word Embedding)简介为什么需要词嵌入?二、PyTorch中的nn.Em

在IntelliJ IDEA中高效运行与调试Spring Boot项目的实战步骤

《在IntelliJIDEA中高效运行与调试SpringBoot项目的实战步骤》本章详解SpringBoot项目导入IntelliJIDEA的流程,教授运行与调试技巧,包括断点设置与变量查看,奠定... 目录引言:为良驹配上好鞍一、为何选择IntelliJ IDEA?二、实战:导入并运行你的第一个项目步骤1

Spring Boot3.0新特性全面解析与应用实战

《SpringBoot3.0新特性全面解析与应用实战》SpringBoot3.0作为Spring生态系统的一个重要里程碑,带来了众多令人兴奋的新特性和改进,本文将深入解析SpringBoot3.0的... 目录核心变化概览Java版本要求提升迁移至Jakarta EE重要新特性详解1. Native Ima

Spring Boot 与微服务入门实战详细总结

《SpringBoot与微服务入门实战详细总结》本文讲解SpringBoot框架的核心特性如快速构建、自动配置、零XML与微服务架构的定义、演进及优缺点,涵盖开发环境准备和HelloWorld实战... 目录一、Spring Boot 核心概述二、微服务架构详解1. 微服务的定义与演进2. 微服务的优缺点三

SpringBoot集成MyBatis实现SQL拦截器的实战指南

《SpringBoot集成MyBatis实现SQL拦截器的实战指南》这篇文章主要为大家详细介绍了SpringBoot集成MyBatis实现SQL拦截器的相关知识,文中的示例代码讲解详细,有需要的小伙伴... 目录一、为什么需要SQL拦截器?二、MyBATis拦截器基础2.1 核心接口:Interceptor

RabbitMQ消息总线方式刷新配置服务全过程

《RabbitMQ消息总线方式刷新配置服务全过程》SpringCloudBus通过消息总线与MQ实现微服务配置统一刷新,结合GitWebhooks自动触发更新,避免手动重启,提升效率与可靠性,适用于配... 目录前言介绍环境准备代码示例测试验证总结前言介绍在微服务架构中,为了更方便的向微服务实例广播消息,

从入门到进阶讲解Python自动化Playwright实战指南

《从入门到进阶讲解Python自动化Playwright实战指南》Playwright是针对Python语言的纯自动化工具,它可以通过单个API自动执行Chromium,Firefox和WebKit... 目录Playwright 简介核心优势安装步骤观点与案例结合Playwright 核心功能从零开始学习