HarmonyOS时钟服务卡片开发指南

开发 OpenHarmony
文章由鸿蒙社区产出,想要了解更多内容请前往:51CTO和华为官方战略合作共建的鸿蒙技术社区https://harmonyos.51cto.com

[[406622]]

想了解更多内容,请访问:

51CTO和华为官方合作共建的鸿蒙技术社区

https://harmonyos.51cto.com

 服务卡片(以下简称“卡片”)是FA(Feature Ability)的一种界面展示形式,将FA(Feature Ability)的重要信息或操作前置到卡片,以达到服务直达,减少体验层级目的。

卡片常用于嵌入到其他应用(当前只支持系统应用)中作为其界面的一部分显示,并支持拉起页面,发送消息等基础的交互功能。

本期Codelab,我们就来为大家介绍如何在HarmonyOS上开发一个时钟类服务卡片应用,该卡片开发使用Java开发语言,包含2×2、2×4两种布局的显示形态,我们先来看看它的显示效果。

[[406623]]

下面我们将集中于卡片应用的创建、更新和删除,卡片数据库(用于存储卡片信息)的创建和使用以及卡片数据服务的使用和更新。

在开始敲代码之前,开发者们需要先下载安装Huawei DevEco Studio,可参照官网的指南进行操作:

● Huawei DevEco Studio安装指南:

https://developer.harmonyos.com/cn/docs/documentation/docguides/software_install0000001053582415

本Codelab主要在entry\src\main\java\目录下完成配置声明、页面布局、功能逻辑代码实现,整个工程的代码结构如下:

【Codelab】时钟服务卡片开发指南-鸿蒙HarmonyOS技术社区

● database文件夹

开发者需自行创建的文件夹,包含两个java文件——Form和FormDatabase。其中Form为卡片对象,用于存储卡片id,卡片名称以及卡片规格。FormDatabase为卡片数据库对象,用于创建卡片数据库。

● slice文件夹

仅包含ClockCardSlice文件,为应用主页面。

● utils文件夹

开发者需自行创建的文件夹,用于存放各类封装好的工具,在本Codelab中,包含ComponentProviderUtils、DatabaseUtils、DateUtils和LogUtils。其中,ComponentProviderUtils提供获取ComponentProvider对象的方法,用于更新卡片;DatabaseUtils提供对数据库相关操作的方法;DateUtils提供日期相关操作的方法;LogUtils封装日志工具类。

● MainAbility

主程序入口,由DevEco Studio生成,用于重写创建、删除卡片等方法。

● MyApplication

DevEco Studio生成,无需做任何变更。

● TimerAbility

一种Service Ability,需要开发者需自行创建,用于时钟更新。

● layout文件夹

页面布局文件夹,由于时钟卡片Codelab涉及两个尺寸:2×2和2×4,因此需要新建两个.xml文件用于页面布局。

● config.json

配置文件,用于卡片和Service Ability的声明。

了解完工程代码结构,下面让我们来对重点步骤一一讲解。

一、配置文件

在卡片应用开始开发前,我们需要在其配置文件config.json中进行以下几项声明,使系统能够识别该应用为一款卡片应用,并使之与系统进行绑定。

在卡片所在的"abilities"中需要配置“formsEnabled": true和"visible": true,使之能被识别为卡片。同时需要配置forms模块的细节,代码如下:

  1. "abilities": [ 
  2.   { 
  3.    .... 
  4.     "formsEnabled"true,//表示该Ability支持服务卡片显示 
  5.     "visible"true
  6.     "forms": [  
  7.       {  
  8.         "landscapeLayouts": [  
  9.         "$layout:form_image_with_info_date_card_2_2",  
  10.         "$layout:form_image_with_info_date_card_2_4"  
  11.         ], //表示卡片外观规格对应的横向布局文件,仅当卡片类型为Java卡片时,需要配置该标签 
  12.         "isDefault"true, //该卡片为默认卡片 
  13.         "scheduledUpdateTime""10:30",  
  14.         "defaultDimension""2*2", //卡片的默认外观规格,这里是2*2 
  15.         "name""DateCard", //卡片的类名 
  16.         "description""This is a service widget", //卡片的描述 
  17.         "colorMode""auto",  
  18.         "type""Java", //表示卡片的类型,这是一个java卡片 
  19.         "supportDimensions": [  
  20.           "2*2",  
  21.           "2*4"  
  22.         ], //表示卡片支持的外观规格,这里是2*2和2*4 
  23.         "portraitLayouts": [  
  24.           "$layout:form_image_with_info_date_card_2_2",  
  25.           "$layout:form_image_with_info_date_card_2_4"  
  26.         ], //表示卡片外观规格对应的竖向布局文件,仅当卡片类型为Java卡片时,需要配置该标签 
  27.         "updateEnabled"true, //表示支持周期性刷新,可以在定时刷新 
  28.         "updateDuration": 1, //表示卡片定时刷新的更新周期,单位为30分钟,这里为30分钟刷新一次 
  29.         "formVisibleNotify"true  
  30.         }  
  31.       ] 

有关forms模块相关属性说明,开发者可自行参见官网资料。

● Java卡片开发指导

https://developer.harmonyos.com/cn/docs/documentation/docguides/abilityservicewidgetproviderjava0000001104082220

二、卡片布局

本篇Codelab为卡片应用设计了2×2和2×4两种布局风格,效果如下图:

【Codelab】时钟服务卡片开发指南-鸿蒙HarmonyOS技术社区
【Codelab】时钟服务卡片开发指南-鸿蒙HarmonyOS技术社区

其中,2×2布局分为四行,展示的内容从上到下分别为日期、时间说明内容、时间具体信息、星期,整体由DependentLayout布局内嵌套四个DirectionalLayout构成,每个DirectionalLayout内均使用Text组件进行展示,部分代码示例如下:

  1. <?xml version="1.0" encoding="utf-8"?> 
  2. <DependentLayout 
  3.     xmlns:ohos="http://schemas.huawei.com/res/ohos" 
  4.     ohos:height="match_parent" 
  5.     ohos:width="match_parent" 
  6.     ohos:background_element="#6A9F99" 
  7.     ohos:remote="true"
  8. <!--DirectionalLayout布局-->  
  9.     <DirectionalLayout 
  10.         ohos:height="match_content" 
  11.         ohos:width="match_parent" 
  12.         ohos:orientation="vertical"
  13. <!--日期Text组件-->          
  14.         <Text...> 
  15.     </DirectionalLayout> 
  16. <!--DirectionalLayout布局-->  
  17.     <DirectionalLayout 
  18.         ohos:id="$+id:title" 
  19.         ohos:height="match_content" 
  20.         ohos:width="match_parent" 
  21.         ohos:alignment="horizontal_center" 
  22.         ohos:orientation="horizontal" 
  23.         ohos:top_margin="35fp"
  24. <!--时间说明内容(HOUR、MIN、SEC)Text组件-->  
  25.         <Text...> 
  26.         <Text...> 
  27.         <Text...> 
  28.     </DirectionalLayout> 
  29. <!--DirectionalLayout布局-->  
  30.     <DirectionalLayout 
  31.         ohos:id="$+id:time" 
  32.         ohos:height="match_content" 
  33.         ohos:width="match_parent" 
  34.         ohos:alignment="horizontal_center" 
  35.         ohos:below="$id:title" 
  36.         ohos:orientation="horizontal" 
  37.         ohos:top_margin="0.5fp"
  38. <!--时间具体信息Text组件-->  
  39.         <Text...> 
  40.         <Text...> 
  41.         <Text...> 
  42.         <Text...> 
  43.         <Text...> 
  44.     </DirectionalLayout> 
  45.  <!--DirectionalLayout布局-->  
  46.     <DirectionalLayout 
  47.         ohos:height="match_content" 
  48.         ohos:width="match_parent" 
  49.         ohos:alignment="center" 
  50.         ohos:below="$id:time" 
  51.         ohos:margin="20fp" 
  52.         ohos:orientation="horizontal"
  53. <!--星期Text组件-->  
  54.         <Text...> 
  55.         <Text...> 
  56.         <Text...> 
  57.         <Text...> 
  58.         <Text...> 
  59.         <Text...> 
  60.         <Text...> 
  61.     </DirectionalLayout> 
  62. </DependentLayout> 

2×4布局分为三行,将日期与星期合为一行,时间说明内容、时间具体信息各单列一行,整体由DependentLayout布局嵌套一个DependentLayout和两个DirectionalLayout构成。

其中DependentLayout布局中由一个Text组件和一个DirectionalLayout组成,DirectionalLayout中又嵌套七个Text组件。其余两个DirectionalLayout内均使用Text组件进行展示,部分代码示例如下:

  1. <?xml version="1.0" encoding="utf-8"?> 
  2. <DependentLayout 
  3.     xmlns:ohos="http://schemas.huawei.com/res/ohos" 
  4.     ohos:height="match_parent" 
  5.     ohos:width="match_parent" 
  6.     ohos:background_element="#6A9F99" 
  7.     ohos:remote="true"
  8. <!--DependentLayout布局--> 
  9.     <DependentLayout 
  10.         ohos:height="match_content" 
  11.         ohos:width="match_parent" 
  12.         ohos:orientation="horizontal"
  13. <!--日期Text组件-->    
  14.         <Text...> 
  15. <!--DirectionalLayout布局-->  
  16.         <DirectionalLayout 
  17.             ohos:height="match_content" 
  18.             ohos:width="match_content" 
  19.             ohos:align_parent_right="true" 
  20.             ohos:orientation="horizontal" 
  21.             ohos:top_margin="10fp"
  22. <!--星期Text组件-->              
  23.             <Text...> 
  24.             <Text...> 
  25.             <Text...> 
  26.             <Text...> 
  27.             <Text...> 
  28.             <Text...> 
  29.             <Text...> 
  30.         </DirectionalLayout> 
  31.     </DependentLayout> 
  32. <!--DirectionalLayout布局-->      
  33.     <DirectionalLayout 
  34.         ohos:id="$+id:title" 
  35.         ohos:height="match_content" 
  36.         ohos:width="match_parent" 
  37.         ohos:alignment="horizontal_center" 
  38.         ohos:orientation="horizontal" 
  39.         ohos:top_margin="35fp"
  40. <!--时间说明内容(HOUR、MIN、SEC)Text组件-->   
  41.         <Text...> 
  42.         <Text...> 
  43.         <Text...> 
  44.     </DirectionalLayout> 
  45. <!--DirectionalLayout布局-->   
  46.     <DirectionalLayout 
  47.         ohos:id="$+id:time" 
  48.         ohos:height="match_content" 
  49.         ohos:width="match_parent" 
  50.         ohos:alignment="horizontal_center" 
  51.         ohos:below="$id:title" 
  52.         ohos:orientation="horizontal"
  53. <!--时间具体信息Text组件-->  
  54.         <Text...> 
  55.         <Text...> 
  56.         <Text...> 
  57.         <Text...> 
  58.         <Text...> 
  59.     </DirectionalLayout> 
  60. </DependentLayout> 

三、卡片数据库及卡片数据服务的创建

卡片数据库的创建

本篇Codelab使用对象关系映射数据库来对卡片ID,卡片名字等信息进行存储。因此,我们需要创建一个数据库(FormDatabase)和一张表(Form)。

其中,对象关系映射 (ORM) 数据库类对应于关系数据库,在本Codelab中即FormDatabase.java,用于存储“Form”表,版本号为 “1”。

在使用 ORM 数据库之前,需要创建一个继承自OrmDatabase的数据库类,并使用 @Database 对其进行注释。示例代码如下:

  1. @Database
  2.         entities = {Form.class}, 
  3.         version = 1) 
  4. public abstract class FormDatabase extends OrmDatabase { } 

关于OrmDatabase相关开发信息,开发者可以自行参考官网资料。

● OrmDatabase

https://developer.harmonyos.com/cn/docs/documentation/docreferences/ormdatabase0000001054838766

此外,我们定义一个对象关系映射 (ORM) 数据库中的实体类Form.java,对应数据库内的表名为“form”,包含了卡片id(formId),卡片名称(formName) 和卡片规格(dimension)三个字段。在ORM数据库中操作实体之前,需要创建一个继承自OrmObject的实体类,并用@Entity注解。示例代码如下:

  1. @Entity(tableName = "form"
  2. public class Form extends OrmObject { 
  3.     @PrimaryKey() 
  4.     private Long formId; 
  5.     private String formName; 
  6.     private Integer dimension; 
  7.  
  8.     public Form(Long formId, String formName, Integer dimension) { 
  9.         this.formId = formId;//卡片id 
  10.         this.formName = formName;//卡片名称 
  11.         this.dimension = dimension;//卡片规格 
  12.     } 
  13.  
  14.     public Form() { } 
  15.  
  16.     public Integer getDimension() { 
  17.         return dimension; 
  18.     } 
  19.  
  20.     public void setDimension(Integer dimension) { 
  21.         this.dimension = dimension; 
  22.     } 
  23.  
  24.     public Long getFormId() { 
  25.         return formId; 
  26.     } 
  27.  
  28.     public void setFormId(Long formId) { 
  29.         this.formId = formId; 
  30.     } 
  31.  
  32.     public String getFormName() { 
  33.         return formName; 
  34.     } 
  35.  
  36.     public void setFormName(String formName) { 
  37.         this.formName = formName; 
  38.     } 

关于OrmObject相关开发信息,开发者可以自行参考官网资料。

● OrmObject

https://developer.harmonyos.com/cn/docs/documentation/docreferences/ormobject0000001054120141

卡片数据服务的创建

由于我们开发的时钟服务卡片需要每隔一秒进行刷新,为了方便处理时钟卡片刷新的定时任务,我们需要在目录下右键new>Ability>Empty Service Ability,创建了一个名为TimerAbility的Service Ability,作为卡片更新定时器,以每秒一次的频率更新:

  1. @Override 
  2.     public void onStart(Intent intent) { 
  3.         HiLog.info(LABEL_LOG, "TimerAbility::onStart"); 
  4.         connect = helper.getOrmContext("FormDatabase""FormDatabase.db", FormDatabase.class); 
  5.         startTimer(); 
  6.         super.onStart(intent); 
  7.     } 
  8.     // 卡片更新定时器,每秒更新一次 
  9.     private void startTimer() { 
  10.         Timer timer = new Timer(); 
  11.         timer.schedule( 
  12.                 new TimerTask() { 
  13.                     @Override 
  14.                     public void run() { 
  15.                         updateForms(); 
  16.                     } 
  17.                 }, 
  18.                 0, 
  19.                 SEND_PERIOD); 
  20.     } 

同时,TimerAbility还承担着卡片更新的功能,我们将在下面详细介绍。

四、时钟卡片应用的创建、更新及删除

在正式进入时钟FA卡片的创建、更新及删除的开发之前,我们先来了解关于卡片开发的一些基本概念。

服务卡片整体框架主要包含三部分:卡片使用方、卡片管理服务和卡片提供方。

卡片使用方: 显示卡片内容的宿主应用,控制卡片在宿主中展示的位置。

卡片管理服务: 用于管理系统中所添加卡片的常驻代理服务,包括卡片对象的管理与使用,以及卡片周期性刷新等。

卡片提供方: 提供卡片显示内容的HarmonyOS服务/HarmonyOS应用,控制卡片的显示内容、控件布局以及控件点击事件。开发者开发卡片即为卡片提供方。

说明

卡片使用方和提供方不要求常驻运行,在需要添加/删除/请求更新卡片时,卡片管理服务会拉起卡片提供方获取卡片信息。

卡片创建

当卡片使用方请求获取卡片时,卡片提供方会被拉起并调用onCreateForm回调函数,intent中会带有卡片ID,卡片名称和卡片外观规格信息,分别通过AbilitySlice.PARAM_FORM_IDENTITY_KEY、AbilitySlice.PARAM_FORM_NAME_KEY和AbilitySlice.PARAM_FORM_DIMENSION_KEY获取,并根据卡片的名称以及外观规格获取对应的xml布局并构造卡片对象,完成卡片信息的创建。

在MainAbility中有如下示例代码:

  1. @Override  
  2. protected ProviderFormInfo onCreateForm(Intent intent) {  
  3.     if (intent == null) {  
  4.         return new ProviderFormInfo();  
  5.     }  
  6.      // 获取卡片ID  
  7.     formId = INVALID_FORM_ID;  
  8.     if (intent.hasParameter(AbilitySlice.PARAM_FORM_IDENTITY_KEY)) {  
  9.         formId = intent.getLongParam(AbilitySlice.PARAM_FORM_IDENTITY_KEY, INVALID_FORM_ID);  
  10.     } else {  
  11.         return new ProviderFormInfo();  
  12.     }  
  13.     // 获取卡片名称  
  14.     String formName = EMPTY_STRING;  
  15.     if (intent.hasParameter(AbilitySlice.PARAM_FORM_NAME_KEY)) {  
  16.         formName = intent.getStringParam(AbilitySlice.PARAM_FORM_NAME_KEY);  
  17.     }  
  18.     // 获取卡片规格  
  19.     int dimension = DEFAULT_DIMENSION_2X2;  
  20.     if (intent.hasParameter(AbilitySlice.PARAM_FORM_DIMENSION_KEY)) {  
  21.         dimension = intent.getIntParam(AbilitySlice.PARAM_FORM_DIMENSION_KEY, DEFAULT_DIMENSION_2X2);  
  22.     }  
  23.     int layoutId = ResourceTable.Layout_form_image_with_info_date_card_2_2;  
  24.     if (dimension == DEFAULT_DIMENSION_2X4) {  
  25.         layoutId = ResourceTable.Layout_form_image_with_info_date_card_2_4;  
  26.     }  
  27.     formInfo = new ProviderFormInfo(layoutId, this);  
  28.     // 存储卡片信息  
  29.     Form form = new Form(formId, formName, dimension);  
  30.     ComponentProvider componentProvider = ComponentProviderUtils.getComponentProvider(form, this);  
  31.     formInfo.mergeActions(componentProvider);  
  32.     if (connect == null) {  
  33.         connect =  
  34.                 helper.getOrmContext("FormDatabase""FormDatabase.db", FormDatabase.class);  
  35.     }  
  36.     try {  
  37.         DatabaseUtils.insertForm(form, connect);  
  38.     } catch (Exception e) {  
  39.         DatabaseUtils.deleteFormData(form.getFormId(), connect);  
  40.     }  
  41.     return formInfo;  

卡片的更新

服务卡片刷新机制分为两种,第一种是定时/定点更新,即在config.json中配置了定时/定点更新之后,卡片管理服务会定期拉起服务卡片刷新数据。第二种是主动更新,即开发者根据需要主动调用updateForm方法更新卡片。在本Codelab中,我们选择的是第二种方式。

我们在之前创建的TimerAbility.java中调用updateForm方法,从数据表Form中获取卡片信息,更新时分秒:

  1. private void updateForms() {  
  2.     // 从数据库中获取卡片信息  
  3.     OrmPredicates ormPredicates = new OrmPredicates(Form.class);  
  4.     List<Form> formList = connect.query(ormPredicates);  
  5.     // 更新时分秒  
  6.     if (formList.size() > 0) {  
  7.   for (Form form : formList) {  
  8.        // 遍历卡片列表更新卡片  
  9.       ComponentProvider componentProvider = ComponentProviderUtils.getComponentProvider(form, this);  
  10.       try {  
  11.     Long updateFormId = form.getFormId();  
  12.     updateForm(updateFormId, componentProvider);  
  13.       } catch (FormException e) {  
  14.         // 删除不存在的卡片  
  15.         DatabaseUtils.deleteFormData(form.getFormId(), connect);  
  16.         HiLog.error(LABEL_LOG, "onUpdateForm updateForm error");  
  17.       }  
  18.    }  
  19.      }  

同时,组件同步展示时间更新。我们封装ComponentProviderUtils这个类,在卡片更新时候,通过调用updateForm方法,传入参数formId和componentProvider,以达到日期、时间和星期实时更新的效果,部分代码和效果如下:

【Codelab】时钟服务卡片开发指南-鸿蒙HarmonyOS技术社区
  1. public static ComponentProvider getComponentProvider(Form form, Context context) {  
  2.     int layoutId = ResourceTable.Layout_form_image_with_info_date_card_2_2;  
  3.     if (form.getDimension() == DIM_VERSION) {  
  4.   layoutId = ResourceTable.Layout_form_image_with_info_date_card_2_4;  
  5.     }  
  6.     ComponentProvider componentProvider = new ComponentProvider(layoutId, context);  
  7.     setComponentProviderValue(componentProvider);  
  8.     return componentProvider;  
  9. }  
  10. // 为时钟各个组件赋值  
  11. private static void setComponentProviderValue(ComponentProvider componentProvider) {  
  12.     Calendar now = Calendar.getInstance();  
  13.     int hour = now.get(Calendar.HOUR_OF_DAY);  
  14.     int min = now.get(Calendar.MINUTE);  
  15.     int second = now.get(Calendar.SECOND);  
  16.     String hourString = int2String(hour);  
  17.     String minString = int2String(min);  
  18.     String secondString = int2String(second);  
  19.     componentProvider.setText(ResourceTable.Id_date, DateUtils.getCurrentDate("yyyy-MM-dd"));  
  20.     componentProvider.setText(ResourceTable.Id_hour, hourString);  
  21.     componentProvider.setText(ResourceTable.Id_min, minString);  
  22.     componentProvider.setText(ResourceTable.Id_sec, secondString);  
  23.  
  24.     // 获取当前星期  
  25.     int weekDayId = getWeekDayId();  
  26.     componentProvider.setTextColor(weekDayId, nowWeekColor);  
  27.     // 将前一天的星期改回原色  
  28.     int lastWeekId = getLastWeekDayId();  
  29.     componentProvider.setTextColor(lastWeekId, primaryWeekColor);  

卡片删除

当卡片被删除时,需要重写onDeleteForm方法,我们将在MainAbility中根据卡片id删除卡片实例数据:

  1. @Override  
  2. protected void onDeleteForm(long formId) {  
  3.     super.onDeleteForm(formId);  
  4.     // 删除数据库中的卡片信息  
  5.     DatabaseUtils.deleteFormData(formId, connect); 

至此,我们完成了时钟卡片应用的开发,构建了一个2×2和2×4的卡片样式。

想了解更多内容,请访问:

51CTO和华为官方合作共建的鸿蒙技术社区

https://harmonyos.51cto.com

 

责任编辑:jianghua 来源: 鸿蒙社区
相关推荐

2021-06-24 14:41:16

鸿蒙HarmonyOS应用

2021-08-23 10:12:41

鸿蒙HarmonyOS应用

2021-08-18 10:06:33

鸿蒙HarmonyOS应用

2021-09-15 10:19:15

鸿蒙HarmonyOS应用

2023-08-04 17:24:43

2021-03-01 09:48:24

鸿蒙HarmonyOS应用开发

2021-02-24 15:22:47

鸿蒙HarmonyOS应用开发

2021-07-22 08:45:47

鸿蒙HarmonyOS应用

2022-04-24 15:26:38

服务卡鸿蒙

2021-09-18 09:57:20

鸿蒙HarmonyOS应用

2021-01-13 09:40:31

鸿蒙HarmonyOS开发

2021-10-11 08:37:14

鸿蒙HarmonyOS应用

2021-02-04 13:06:38

鸿蒙HarmonyOS应用开发

2021-04-16 09:28:18

鸿蒙HarmonyOS应用

2011-07-25 16:21:22

Sencha touc

2021-05-12 15:17:39

鸿蒙HarmonyOS应用

2021-02-04 09:45:19

鸿蒙HarmonyOS应用开发

2021-03-31 09:50:25

鸿蒙HarmonyOS应用开发

2021-01-18 09:52:20

鸿蒙HarmonyOS开发

2021-06-28 14:48:03

鸿蒙HarmonyOS应用
点赞
收藏

51CTO技术栈公众号