评论

收藏

[HarmonyOS] HarmonyOS 日程提醒卡片应用

移动开发 移动开发 发布于:2021-08-09 16:56 | 阅读数:325 | 评论:0

效果图展示:
::: hljs-center
DSC0000.png

:::
一、项目介绍
    项目为一个简单的在日历上添加日程的应用,我们可以将当日的日程生成卡片到桌面,便于直观查看当日要做事项。   
    此项目旨在帮助开发者快速掌握如下技术点:  
    1)  Js卡片的布局、初始化、更新、事件处理;  
    2)  Java卡片的布局、初始化、更新、事件处理。
二、代码结构解读
本教程我们只是对核心代码进行讲解,您可以在最后的参考中下载完整代码,首先来介绍下整个工程的代码结构:
::: hljs-center
DSC0001.png

:::
    1、java-Util:封装处理卡片数据的工具类  
    2、java-widget:controller—卡片统一管理类;javacard2x4—java2x2与java2x4的卡片初始化、更新等处理类;jscard2x4—js2x2与js2x4的卡片初始化、更新等处理类;widget—js轮播卡片初始化、更新等处理类。  
    3、java-CardServiceAbility:日程变动,更新卡片的service服务类。  
    4、java-MainAbility:应用的入口,也是卡片初始化、更新等操作的入口。  
    5、java-ServiceAbility:js与java的通信服务。  
    6、js-default-page:index—应用首页,日历日程展示页面代码;schedule—日程添加、修改、删除页面代码。  
    7、js-jscard2x4:js2x2与js2x4卡片页面代码;js-widget:js轮播卡片页面代码。  
    8、resources-base-layout:form_grid_pattern_javacard2x4_2_2.xml—java2x2卡片页面代码;form_grid_pattern_javacard2x4_2_4.xml—java2x4卡片页面代码。  
    9、config.json:java、js、卡片等配置文件。
三、Js轮播卡片
1.新建js卡片
1)右击—new—service-widget   
::: hljs-center
DSC0002.png

:::
2)选择模板—下一步  
::: hljs-center
DSC0003.png

:::
3)输入卡片名称、选择卡片类型(单选)、卡片大小(多选),点击finish即可。  
::: hljs-center
DSC0004.png

:::
2.默认卡片页面
1)js-widget-pages-index-index.json默认卡片页面的数据  
    data:默认数据。  
    actions:routerEvent为router事件,用于应用跳转。  
2)js-widget-pages-index-index.css 卡片的样式  
    同js页面样式开发。  
3)js-widget-pages-index-index.html 卡片的页面  
    同js页面开发,不过要使用卡片支持组件;你可以点击 JS卡片组件 去学习。  
    click事件关联json文件中的actions下的应用跳转事件,此处关联的是点击跳转应用的页面,页面代码:on:click="routerEvent"(不可以传参)。
3.初始化卡片页面
在WidgetImpl.java文件中bindFromData方法中,进行页面展示数据的初始化:
public ProviderFormInfo bindFormData() {
     ZSONObject zsonObject = new ZSONObject();
     ProviderFormInfo providerFormInfo = new ProviderFormInfo();
     String scheduleData = StorageUtil.initPreferences(context).getSchedule();
     if(!"".equals(scheduleData)) {
       ZSONArray array = ZSONArray.stringToZSONArray(scheduleData);
       if(array.size()>0) {
         ZSONArray result = StorageUtil.getSwiperCardSchedule(array);
         CardConstant.SWIPER_CARD_LENGTH = result.size();
         zsonObject.put("schedule", result);
         zsonObject.put("count", array.size());
       }
     }
     zsonObject.put("index", 0);
     providerFormInfo.setJsBindingData(new FormBindingData(zsonObject));
     return providerFormInfo;
   }
4.更新卡片页面
1)config.json配置定时或周期性刷新  
    定时或周期性刷新均会调用WidgetImpl.java文件中updateFormData方法,因本卡片未实现此方法,所以不支持此功能;在下面js2x2与2x4卡片中介绍周期性刷新;在下面java2x2与2x4卡片中介绍定时刷新。
2)添加当日日程导致数据变化 ,对日程进行更新介绍  
    数据变化时,触发数据监听方法,代码在StorageUtil.java工具类,这里开启CardServiceAbility服务,代码如下:
private class PreferencesChangeCounter implements Preferences.PreferencesObserver {
     private Context mContext;
     public PreferencesChangeCounter(Context context){
       this.mContext = context;
     }
     @Override
     public void onChange(Preferences preferences, String key) {
       Intent intent = new Intent();
       Operation operation = new Intent.OperationBuilder()
           .withDeviceId("")
           .withBundleName("com.example.myohoscard")
           .withAbilityName("com.example.myohoscard.CardServiceAbility")
           .build();
       intent.setOperation(operation);
       mContext.startAbility(intent,0);
     }
   }
CardServiceAbility服务的onCommand方法中进行卡片更新,其更新代码如下:
public void swiperData(ZSONArray array){
     ZSONArray result = StorageUtil.getSwiperCardSchedule(array);

     CardConstant.SWIPER_CARD_LENGTH = result.size();

     ZSONObject zsonObject = new ZSONObject();
     zsonObject.put("schedule", result);
     zsonObject.put("count", array.size());

     FormBindingData formBindingData = new FormBindingData(zsonObject);
     FormControllerManager formControllerManager = FormControllerManager.getInstance(this);

     for(long id:formControllerManager.getAllFormIdFromSharePreference(CardConstant.SWIPER_CARD_FORMS_NAME)) {
       try {
         updateForm(id, formBindingData);
       } catch (FormException e) {
         e.printStackTrace();
       }
     }
   }
5.卡片的轮播
CardServiceAbility服务的onCommand方法中调用卡片轮播,其代码如下:
public void swiperLoop(){
     ZSONObject data = new ZSONObject();
     if(CardConstant.SWIPER_CARD_THREAD != null) {
       CardConstant.SWIPER_CARD_THREAD.interrupt();
     }
     CardConstant.SWIPER_CARD_THREAD = new Thread(() -> {
       while (true) {
         try {
           if(Thread.currentThread().isInterrupted()){
             //处理中断逻辑
             break;
           }
           Thread.sleep(2000);

           data.put("index", index % CardConstant.SWIPER_CARD_LENGTH);
           FormBindingData bindingData = new FormBindingData(data);
           FormControllerManager formControllerManager = FormControllerManager.getInstance(this);

           for(long formid:formControllerManager.getAllFormIdFromSharePreference(CardConstant.SWIPER_CARD_FORMS_NAME)) {
             updateForm(formid, bindingData);
           }

           index++;
           if(index == CardConstant.SWIPER_CARD_LENGTH){
             index = 0;
           }
         } catch (Exception e) {
           break;
         }
       }
     });
     CardConstant.SWIPER_CARD_THREAD.start();
   }
缺点:此处退出应用程序时,service不再运行导致轮播线程结束,轮播失效。
四、Js2x2与2x4卡片
1.新建卡片页面
同js轮播卡片的新建卡片页面操作步骤,勾选2x2、2x4卡片。
2.默认卡片页面
1) js-card2x4-pages-index-index.json默认卡片页面的数据  
    data:默认数据。  
    actions:routerEvent为router事件,用于应用跳转;sendMessageEvent1与sendMessageEvent2为message事件,用于卡片开发人员自定义点击事件。  
2) js-card2x4-pages-index-index.css 卡片的样式  
    同js页面样式开发。  
3) js-card2x4-pages-index-index.html 卡片的页面  
    同js页面开发,不过要使用卡片支持组件;  
    2x2或2x4的卡片公用一个html页面,利用dim2X4变量true或者false,来展示2x2或2x4的布局;  
    click事件关联json文件中的actions下的操作事件,此处关联的是点击跳转应用的页面,代码:on:click="routerEvent"或on:click="sendMessageEvent1"、on:click="sendMessageEvent2"(不支持参数传递)。
3.初始化卡片页面
在Jscard2x4Impl.java文件中bindFromData方法中,进行页面展示数据的初始化:
public ProviderFormInfo bindFormData() {
     HiLog.info(TAG, "bind form data");
     ZSONObject zsonObject = new ZSONObject();
     ProviderFormInfo providerFormInfo = new ProviderFormInfo();
     if (dimension == DIMENSION_1X2) {
       zsonObject.put("mini", true);
     }
     if (dimension == DIMENSION_2X4) {
       zsonObject.put("dim2X4", true);
     }
     String scheduleData = StorageUtil.initPreferences(context).getSchedule();
     if(!"".equals(scheduleData)) {
       ZSONArray array = ZSONArray.stringToZSONArray(scheduleData);
       if(array.size()>0) {
         zsonObject = getObject(array,zsonObject);
       }
     }
     providerFormInfo.setJsBindingData(new FormBindingData(zsonObject));
     return providerFormInfo;
   }

   /**
  * 当天日程存在时,卡片数据展示
  * @param array
  * @param zsonObject
  * @return
  */
   public static ZSONObject getObject(ZSONArray array,ZSONObject zsonObject){
     ZSONArray result = StorageUtil.getCurrentCardSchedule(array);
     ZSONObject schedule_current = ZSONObject.stringToZSON(ZSONObject.toZSONString(result.get(0)));
     if(schedule_current != null){
       zsonObject.put("schedule_current", schedule_current);
     }
     ZSONObject schedule_1 = ZSONObject.stringToZSON(ZSONObject.toZSONString(result.get(1)));
     ZSONObject schedule_2 = ZSONObject.stringToZSON(ZSONObject.toZSONString(result.get(2)));
     if(schedule_1 != null){
       zsonObject.put("noSchedule", false);
       zsonObject.put("hasSchedule1", true);
       zsonObject.put("schedule_1", schedule_1);
     }
     if(schedule_2 != null){
       zsonObject.put("hasSchedule2", true);
       zsonObject.put("schedule_2", schedule_2);
     }
     zsonObject.put("count", array.size());
     return zsonObject;
   }
4.更新卡片页面
1)config.json配置周期性刷新  
    config.json中jscard2X4卡片配置"updateEnabled": true与"updateDuration": 1,其中updateDuration配置参数为30分钟倍数,此处1即30分钟主动刷新一次卡片。  
周期性刷新调用Jscard2x4Impl.java文件中updateFormData方法,代码如下:
public void updateFormData(long formId, Object... vars) {
     HiLog.info(TAG, "update form data timing, default 30 minutes");
     String scheduleData = StorageUtil.initPreferences(context).getSchedule();
     if(!"".equals(scheduleData)) {
       ZSONArray array = ZSONArray.stringToZSONArray(scheduleData);
       if(array.size()>0) {
         FormBindingData formBindingData = new FormBindingData(getObject(array,new ZSONObject()));//上一步中有getObject()方法代码
         try {
           ((MainAbility)context).updateForm(formId, formBindingData);
         } catch (FormException e) {
           e.printStackTrace();
         }
       }
     }

   }
2)添加当日日程导致数据变化 ,对日程进行更新介绍  
    数据变化时,触发数据监听方法,代码在StorageUtil.java工具类中,这里开启CardServiceAbility服务。CardServiceAbility服务的onCommand方法中调用cardData进行卡片更新,其更新代码如下:
FormBindingData formBindingData = new FormBindingData(Jscard2X4Impl.getObject(array,new ZSONObject()));//上一步中有getObject()方法代码
     FormControllerManager formControllerManager = FormControllerManager.getInstance(this);

     try {
       for(long id:formControllerManager.getAllFormIdFromSharePreference(CardConstant.JS_CARD_FORMS_NAME)) {
         updateForm(id, formBindingData);
       }
     } catch (FormException e) {
       e.printStackTrace();
     }
5.router事件与message事件处理
1)router事件  
    在跳转后的页面,接收参数并进行处理,这里请看MainAbility.java下的onStart方法,代码如下:
String strParams = intent.getStringParam("params");
     if(strParams != null) {
       ZSONObject data = ZSONObject.stringToZSON(strParams);
       String message = data.getString("message");
       //此处可以根据message判断点击了某个按钮,从而做相应的处理
       new ToastDialog(this).setText(message)
           .setAlignment(LayoutAlignment.CENTER).show();
     }
2)message事件  
    在Jscard2x4Impl.java文件中onTriggerFormEvent方法中进行处理,代码如下:
public void onTriggerFormEvent(long formId, String message) {
     HiLog.info(TAG, "handle card click event.");
     ZSONObject data = ZSONObject.stringToZSON(message);
     String msg = data.getString("msg");

     Intent intent = new Intent();
     intent.setParam("params","{"message":""+msg+""}");
     Operation operation = new Intent.OperationBuilder()
         .withDeviceId("")
         .withBundleName("com.example.myohoscard")
         .withAbilityName("com.example.myohoscard.MainAbility")
         .build();
     intent.setOperation(operation);
     ((MainAbility)context).startAbility(intent);
   }
五、Java2x2与2x4卡片
1.新建卡片页面
    同js轮播卡片的新建卡片页面操作步骤,勾选type—java、Dimensions—2x2与2x4卡片。
2.默认卡片页面
    resources-base-layout下:java2x2卡片布局页面—form_grid_pattern_javacard2x4_2_2.xml,java2x2卡片布局页面—form_grid_pattern_javacard2x4_2_4.xml;其开发模式同应用java模式页面布局开发。
3.初始化卡片页面
在Javacard2x4Impl.java文件中bindFromData方法中,进行页面展示数据的初始化:
public ProviderFormInfo bindFormData() {
     HiLog.info(TAG, "bind form data when create form");
     ProviderFormInfo providerFormInfo = new ProviderFormInfo(RESOURCE_ID_MAP.get(dimension), context);
     String scheduleData = StorageUtil.initPreferences(context).getSchedule();
     if(!"".equals(scheduleData)) {
       ZSONArray array = ZSONArray.stringToZSONArray(scheduleData);
       if(array.size()>0) {
         // ComponentProvider表示Java卡片
         ComponentProvider componentProvider = new ComponentProvider();
         providerFormInfo.mergeActions(getFormDataCreate(array,context,dimension,componentProvider));

       }
     }

     return providerFormInfo;
   }
public static ComponentProvider getFormDataCreate(ZSONArray array,Context context,int dimension,ComponentProvider componentProvider){
     componentProvider.setVisibility(ResourceTable.Id_count, Text.VISIBLE);
     componentProvider.setText(ResourceTable.Id_count, array.size()+"");

     ZSONArray result = StorageUtil.getCurrentCardSchedule(array);
     ZSONObject schedule_current = ZSONObject.stringToZSON(ZSONObject.toZSONString(result.get(0)));
     if(schedule_current != null){
       componentProvider.setText(ResourceTable.Id_time, schedule_current.getString("startCardShow")+"-"+schedule_current.getString("endCardShow"));
       componentProvider.setText(ResourceTable.Id_schedule, schedule_current.getString("title"));
     }
     if (dimension == DEFAULT_DIMENSION_2X2) {
       componentProvider.setIntentAgent(ResourceTable.Id_layout, getStartAbilityIntentAgent(context));
     }

     if (dimension == DIMENSION_2X4) {
       componentProvider.setVisibility(ResourceTable.Id_noschedule, Text.HIDE);
       ZSONObject schedule_1 = ZSONObject.stringToZSON(ZSONObject.toZSONString(result.get(1)));
       if(schedule_1 != null){
//        componentProvider.setText(ResourceTable.Id_time1, schedule_1.getString("startCardShow")+"-"+schedule_1.getString("endCardShow"));
         componentProvider.setText(ResourceTable.Id_schedule1, schedule_1.getString("title"));
       }

       ZSONObject schedule_2 = null;
       if(result.get(2) != null){
         schedule_2 = ZSONObject.stringToZSON(ZSONObject.toZSONString(result.get(2)));
       }
       if(schedule_2 != null){
         componentProvider.setVisibility(ResourceTable.Id_line, Text.VISIBLE);
//        componentProvider.setText(ResourceTable.Id_time2, schedule_2.getString("startCardShow")+"-"+schedule_2.getString("endCardShow"));
         componentProvider.setText(ResourceTable.Id_schedule2, schedule_2.getString("title"));
       }
     }

     if (dimension == DIMENSION_2X4) {
       componentProvider.setIntentAgent(ResourceTable.Id_more, getStartAbilityIntentAgent(context));
     }
     return componentProvider;
   }
4.更新卡片页面
1)config.json配置定时刷新  
config.json中jscard2X4卡片配置"scheduledUpdateTime": "10:42"与"updateDuration": 0,其中scheduledUpdateTime配置参数为24小时制,可以在当天指定时间对卡片进行刷新。  
定时刷新调用Javacard2x4Impl.java文件中updateFormData方法,代码如下:
public void updateFormData(long formId, Object... vars) {
     HiLog.info(TAG, "update form data timing, default 30 minutes");

     String scheduleData = StorageUtil.initPreferences(context).getSchedule();
     if(!"".equals(scheduleData)) {
       ZSONArray array = ZSONArray.stringToZSONArray(scheduleData);
       if(array.size()>0) {
         ComponentProvider componentProvider =
             new ComponentProvider(RESOURCE_ID_MAP.get(dimension), context);
         try {
           ((MainAbility)context).updateForm(formId, getFormDataUpdate(array,context,dimension,componentProvider));
         } catch (FormException e) {
           HiLog.error(TAG, e.toString());
         }

       }
     }

   }
public static ComponentProvider getFormDataUpdate(ZSONArray array,Context context,int dimension,ComponentProvider componentProvider){

     componentProvider.setVisibility(ResourceTable.Id_count, Text.VISIBLE);
     componentProvider.setText(ResourceTable.Id_count, array.size()+"");

     ZSONArray result = StorageUtil.getCurrentCardSchedule(array);
     ZSONObject schedule_current = ZSONObject.stringToZSON(ZSONObject.toZSONString(result.get(0)));
     if(schedule_current != null){
       componentProvider.setText(ResourceTable.Id_time, schedule_current.getString("startCardShow")+"-"+schedule_current.getString("endCardShow"));
       componentProvider.setText(ResourceTable.Id_schedule, schedule_current.getString("title"));
     }

     if (dimension == DIMENSION_2X4) {
       componentProvider.setVisibility(ResourceTable.Id_noschedule, Text.HIDE);
       ZSONObject schedule_1 = ZSONObject.stringToZSON(ZSONObject.toZSONString(result.get(1)));
       if(schedule_1 != null){
//        componentProvider.setText(ResourceTable.Id_time1, schedule_1.getString("startCardShow")+"-"+schedule_1.getString("endCardShow"));
         componentProvider.setText(ResourceTable.Id_schedule1, schedule_1.getString("title"));
       }

       ZSONObject schedule_2 = null;
       if(result.get(2) != null){
         schedule_2 = ZSONObject.stringToZSON(ZSONObject.toZSONString(result.get(2)));
       }
       if(schedule_2 != null){
         componentProvider.setVisibility(ResourceTable.Id_line, Text.VISIBLE);
//        componentProvider.setText(ResourceTable.Id_time2, schedule_2.getString("startCardShow")+"-"+schedule_2.getString("endCardShow"));
         componentProvider.setText(ResourceTable.Id_schedule2, schedule_2.getString("title"));
       }
     }

     return componentProvider;
   }
2)添加当日日程导致数据变化 ,对日程进行更新介绍
数据变化时,触发数据监听方法,代码在StorageUtil.java工具类中,这里开启CardServiceAbility服务。CardServiceAbility服务的onCommand方法中调用cardData进行卡片更新,其更新代码如下:
try {
       for(long id:formControllerManager.getAllFormIdFromSharePreference(CardConstant.JAVA_CARD_FORMS_NAME)) {
         int dimension = formControllerManager.getDimension(id+"");

         ComponentProvider componentProvider =
           new ComponentProvider(Javacard2x4Impl.RESOURCE_ID_MAP.get(dimension), this);
         updateForm(id, Javacard2x4Impl.getFormDataUpdate(array,this,dimension,componentProvider));//上一步中有此方法代码
       }
     } catch (FormException e) {
       e.printStackTrace();
     }
5.卡片点击事件添加
给控件点击点击事件,其代码为:componentProvider.setIntentAgent(ResourceTable.Id_more, getStartAbilityIntentAgent(context)),其中getStartAbilityIntentAgent方法代码如下:
private static IntentAgent getStartAbilityIntentAgent(Context context) {
     Intent intent = new Intent();
     Operation operation = new Intent.OperationBuilder()
         .withDeviceId("")
         .withBundleName("com.example.myohoscard")
         .withAbilityName("com.example.myohoscard.MainAbility")
         .build();
     intent.setOperation(operation);
     List<Intent> intentList = new ArrayList<>();
     intentList.add(intent);
     List<IntentAgentConstant.Flags> flags = new ArrayList<>();
     flags.add(IntentAgentConstant.Flags.UPDATE_PRESENT_FLAG);
     IntentAgentInfo paramsInfo =
         new IntentAgentInfo(200, IntentAgentConstant.OperationType.START_ABILITY, flags, intentList, null);
     return IntentAgentHelper.getIntentAgent(context, paramsInfo);
   }
六、代码参考
https://gitee.com/chinasoft6_ohos/my-ohos-card
作者:陈巧银
想了解更多关于鸿蒙的内容,请访问:
51CTO和华为官方战略合作共建的鸿蒙技术社区
https://harmonyos.51cto.com/#bkwz


关注下面的标签,发现更多相似文章