【中软国际】HarmonyOS 日程提醒卡片应用 精华
效果图展示:
一、项目介绍
项目为一个简单的在日历上添加日程的应用,我们可以将当日的日程生成卡片到桌面,便于直观查看当日要做事项。
此项目旨在帮助开发者快速掌握如下技术点:
1) Js卡片的布局、初始化、更新、事件处理;
2) Java卡片的布局、初始化、更新、事件处理。
二、代码结构解读
本教程我们只是对核心代码进行讲解,您可以在最后的参考中下载完整代码,首先来介绍下整个工程的代码结构:
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
2)选择模板—下一步
3)输入卡片名称、选择卡片类型(单选)、卡片大小(多选),点击finish即可。
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
作者:陈巧银
更多原创内容请关注:中软国际 HarmonyOS 技术学院
入门到精通、技巧到案例,系统化分享HarmonyOS开发技术,共建鸿蒙生态,欢迎投稿和订阅,让我们一起携手前行共建鸿蒙生态。
效果不错,牛逼
学习了!!!
厉害,深入浅出,通俗易懂
深入浅出,蒋总藕像!
不明觉厉
精华吖,感谢分享