HarmonyOS基于Java开发的服务卡片
想了解更多内容,开卡片请访问:
和华为官方合作共建的发的服务鸿蒙技术社区
https://harmonyos.51cto.com
服务卡片介绍:
服务卡片是将重要信息或操作前置的一种界面展示,目的开卡片是减少体验层级,服务直达,发的服务增强用户交互体验。开卡片
卡片基本信息说明:
卡片使用方显示卡片内容的发的服务宿主应用,控制卡片在宿主中展示的开卡片位置。
卡片管理服务用于管理系统中所添加卡片的发的服务常驻代理服务,包括卡片对象的开卡片管理与使用,以及卡片周期性刷新等。发的服务
卡片提供方提供卡片显示内容的开卡片HarmonyOS应用或原子化服务,控制卡片的发的服务显示内容、控件布局以及控件点击事件。开卡片
以下是发的服务器托管服务官方提供的运作机制:
项目介绍:
本篇主要对系统时间日期来展示服务卡片,该Demo是开卡片Java为基础,无JS相关代码,以下将对项目详细介绍。
项目目录如下:

database中FormBean是所需的bean文件并设置数据库表名,包含卡片的id、名称以及布局id,FormDatabase是bean绑定Orm数据库类。
Slice中MainAbilitySlice与TimeCardSlice是展示页面,MainAbilitySlice是创建项目系统生成,我设置初始化页面为TimeCardSlice。
Utils中是分别是卡片工具类、数据库工具类、日期工具类。
MainAbility以及MyApplication也是创建项目系统生成的。
注意MainAbility中的setMainRoute可以设置指定初始化页面。
TimerAbility是一个服务类,云服务器提供商用来更新卡片时间。
配置文件如下:
{ "app": { "bundleName": "com.fei.yuan.myapplication", "vendor": "fei", "version": { "code": 1000000, "name": "1.0.0" } }, "deviceConfig": { "default": { "keepAlive": true } }, "module": { "package": "com.fei.yuan.myapplication", "name": ".MyApplication", "mainAbility": "com.fei.yuan.myapplication.MainAbility", "deviceType": [ "phone" ], "distro": { "deliveryWithInstall": true, "moduleName": "entry", "moduleType": "entry", "installationFree": false }, "reqPermissions": [ { "name": "ohos.permission.KEEP_BACKGROUND_RUNNING", "reason": "keep service ability backgroud running", "usedScene": { "ability": [ ".TimerAbility" ], "when": "always" } } ], "abilities": [ { "skills": [ { "entities": [ "entity.system.home" ], "actions": [ "action.system.home" ] } ], "orientation": "unspecified", "name": "com.fei.yuan.myapplication.MainAbility", "icon": "$media:icon", "description": "$string:mainability_description", "launchType": "standard", "formsEnabled": true, "label": "$string:entry_MainAbility", "type": "page", "visible": true, "forms": [ { "landscapeLayouts": [ "$layout:form_image_with_info_time_2_2", "$layout:form_image_with_info_time_2_4" ], "isDefault": true, "scheduledUpdateTime": "10:30", "defaultDimension": "2*2", "name": "widget", "description": "This is a service widget", "colorMode": "auto", "type": "Java", "supportDimensions": [ "1*2", "2*2", "2*4" ], "portraitLayouts": [ "$layout:form_image_with_info_time_2_2", "$layout:form_image_with_info_time_2_4", "$layout:form_image_with_info_time_1_2" ], "updateEnabled": true, "updateDuration": 30, "formVisibleNotify": true, "metaData": { "customizeData": [ { "name": "needBlurBackgroundForLauncher", "value": "true" } ] } } ] }, { "icon": "$media:icon", "name": "com.fei.yuan.myapplication.TimerAbility", "description": "$string:timerability_description", "type": "service", "visible": true, "backgroundModes": [ "dataTransfer", "location" ] } ] } }forms内属性说明:
先上效果图:
代码详解:
下面开始项目解析:
首先介绍MainAbility主页,主要方法有onStart,onCreateForm,onDeleteForm
//主页初始化 public void onStart(Intent intent) { HiLog.info(TAG, "onStart"); super.onStart(intent); //添加在数据库中添加FormDatabase表 ormContext = databaseHelper.getOrmContext("FormBean", "FormDatabase.db", FormDatabase.class); // 这里是开启服务,用来即时更新服务卡片时间(后天有说明) Intent intentService = new Intent(); Operation operation = new Intent.OperationBuilder() .withBundleName(getBundleName()) .withAbilityName(TimerAbility.class.getName()) .build(); intentService.setOperation(operation); startAbility(intentService); //加载初始页 super.setMainRoute(TimeCardSlice.class.getName()); } //创建卡片 protected ProviderFormInfo onCreateForm(Intent intent) { HiLog.info(TAG, "onCreateForm"); if (intent == null) { return new ProviderFormInfo(); } //获取卡片id formId = 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 = ""; 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_time_2_2; if (dimension == DEFAULT_DIMENSION_2X4) { layoutId = ResourceTable.Layout_form_image_with_info_time_2_4; }else if (dimension == DEFAULT_DIMENSION_1X2){ layoutId = ResourceTable.Layout_form_image_with_info_time_1_2; } formInfo = new ProviderFormInfo(layoutId, this); //存储卡片信息 FormBean formBean = new FormBean(formId, formName, dimension); ComponentProvider componentProvider = ComponentProviderUtils.getComponentProvider(formBean, this); formInfo.mergeActions(componentProvider); if (ormContext == null) { ormContext = databaseHelper.getOrmContext("FormBean", "FormDatabase.db", FormDatabase.class); } try { //保存数据到数据库 DatabaseUtils.insertForm(formBean, ormContext); } catch (Exception e) { //删除数据库中保存的数据 DatabaseUtils.deleteFormData(formBean.getFormId(), ormContext); } return formInfo; } //删除卡片 protected void onDeleteForm(long formId) { HiLog.info(TAG, "onDeleteForm: formId=" + formId); super.onDeleteForm(formId); DatabaseUtils.deleteFormData(formId, ormContext); }接下来就是TimeCardSlice页,进行时间获取展示,并使用计时器来进行刷新页面。
private void initData() { //获取日期 String currentDate = DateUtils.getCurrentDate("yyyy-MM-dd"); String[] split = currentDate.split("-"); monthText.setText(split[1]); dayText.setText(split[2]); //获取时间 Calendar calendar = Calendar.getInstance(); int hour = calendar.get(Calendar.HOUR_OF_DAY); hourText.setText(intToString(hour)); int min = calendar.get(Calendar.MINUTE); minText.setText(intToString(min)); int second = calendar.get(Calendar.SECOND); secondText.setText(intToString(second)); //获取星期 int week = calendar.get(Calendar.DAY_OF_WEEK); weekText.setText(getWeek(this,week)); } // 计时器刷新数据 private void startTimer() { timer = new Timer(); timer.schedule(new TimerTask() { @Override public void run() { runnable.run(); myEventHandler.sendEvent(1); } }, 0, PERIOD); }最后是TimerAbility页,主要是对服务卡片进行刷新操作:
protected void onStart(Intent intent) { HiLog.info(TAG, "onStart"); ormContext = databaseHelper.getOrmContext("FormBean", "FormDatabase.db", FormDatabase.class); startTimer(); super.onStart(intent); }重点是这个方法,进行更新数据。
private void upDateFormBean() { OrmPredicates ormPredicates = new OrmPredicates(FormBean.class); List<FormBean> beanList = ormContext.query(ormPredicates); if (beanList.size() <= 0) { return; } for (FormBean formBean : beanList) { //更新卡片信息 ComponentProvider componentProvider = ComponentProviderUtils.getComponentProvider(formBean,this); try { updateForm(formBean.getFormId(),componentProvider); } catch (FormException e) { DatabaseUtils.deleteFormData(formBean.getFormId(),ormContext); } } }注意:Java卡片与JS卡片选型指导,官方提倡使用JS来实现卡片,支持的控件和场景都很丰富
Java/JS卡片场景能力差异如下表所示:
综上所述,JS卡片比Java卡片支持的控件和能力都更丰富:
Java卡片:适合作为一个直达入口,没有复杂的页面和事件。 JS卡片:适合有复杂界面的卡片。完整代码如下:
MainAbility.java:
public class MainAbility extends Ability { private static final HiLogLabel TAG = new HiLogLabel(HiLog.LOG_APP, 0x0, "TAG---"); private static final int INVALID_FORM_ID = -1; private static final int DEFAULT_DIMENSION_1X2 = 1; private static final int DEFAULT_DIMENSION_2X2 = 2; private static final int DEFAULT_DIMENSION_2X4 = 3; private static final int DEFAULT_DIMENSION_4X4 = 4; private long formId; private DatabaseHelper databaseHelper = new DatabaseHelper(this); private OrmContext ormContext; private ProviderFormInfo formInfo; @Override public void onStart(Intent intent) { HiLog.info(TAG, "onStart"); super.onStart(intent); ormContext = databaseHelper.getOrmContext("FormBean", "FormDatabase.db", FormDatabase.class); // 开启服务 Intent intentService = new Intent(); Operation operation = new Intent.OperationBuilder() .withBundleName(getBundleName()) .withAbilityName(TimerAbility.class.getName()) .build(); intentService.setOperation(operation); startAbility(intentService); super.setMainRoute(TimeCardSlice.class.getName()); } /** * 创建卡片时回调 * * @param intent * @return ProviderFormInfo */ @Override protected ProviderFormInfo onCreateForm(Intent intent) { HiLog.info(TAG, "onCreateForm"); if (intent == null) { return new ProviderFormInfo(); } //获取卡片id formId = 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 = ""; 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_time_2_2; if (dimension == DEFAULT_DIMENSION_2X4) { layoutId = ResourceTable.Layout_form_image_with_info_time_2_4; }else if (dimension == DEFAULT_DIMENSION_1X2){ layoutId = ResourceTable.Layout_form_image_with_info_time_1_2; } formInfo = new ProviderFormInfo(layoutId, this); //存储卡片信息 FormBean formBean = new FormBean(formId, formName, dimension); ComponentProvider componentProvider = ComponentProviderUtils.getComponentProvider(formBean, this); formInfo.mergeActions(componentProvider); if (ormContext == null) { ormContext = databaseHelper.getOrmContext("FormBean", "FormDatabase.db", FormDatabase.class); } try { DatabaseUtils.insertForm(formBean, ormContext); } catch (Exception e) { DatabaseUtils.deleteFormData(formBean.getFormId(), ormContext); } return formInfo; } /** * 更新卡片时回调 * * @param formId */ @Override protected void onUpdateForm(long formId) { HiLog.info(TAG, "onUpdateForm"); super.onUpdateForm(formId); } /** * 请求删除卡片时回调 * * @param formId */ @Override protected void onDeleteForm(long formId) { HiLog.info(TAG, "onDeleteForm: formId=" + formId); super.onDeleteForm(formId); DatabaseUtils.deleteFormData(formId, ormContext); } @Override protected void onActive() { HiLog.info(TAG, "onActive"); super.onActive(); } @Override protected void onInactive() { HiLog.info(TAG, "onInactive"); super.onInactive(); } @Override protected void onBackground() { HiLog.info(TAG, "onBackground"); super.onBackground(); } @Override protected void onStop() { HiLog.info(TAG, "onStop"); super.onStop(); } @Override protected void onForeground(Intent intent) { HiLog.info(TAG, "onForeground"); super.onForeground(intent); } @Override protected void onOrientationChanged(AbilityInfo.DisplayOrientation displayOrientation) { super.onOrientationChanged(displayOrientation); } }TimeCardSlice.java:
public class TimeCardSlice extends AbilitySlice { private static final HiLogLabel LABEL = new HiLogLabel(HiLog.LOG_APP, 0, TimeCardSlice.class.getName()); private static final Long PERIOD = 1000L; private Text monthText; private Text dayText; private Text hourText; private Text minText; private Text secondText; private Text weekText; private EventRunner eventRunner; private MyEventHandler myEventHandler; private Timer timer; private Runnable runnable = new Runnable() { private void initHandler() { eventRunner = EventRunner.getMainEventRunner(); if (eventRunner == null) { return; } myEventHandler = new MyEventHandler(eventRunner); } @Override public void run() { initHandler(); } }; @Override protected void onStart(Intent intent) { super.onStart(intent); super.setUIContent(ResourceTable.Layout_form_image_with_info_time_2_2); initComponent(); initData(); startTimer(); } // 计时器刷新数据 private void startTimer() { timer = new Timer(); timer.schedule(new TimerTask() { @Override public void run() { runnable.run(); myEventHandler.sendEvent(1); } }, 0, PERIOD); } private void initComponent() { monthText = (Text) findComponentById(ResourceTable.Id_month); dayText = (Text) findComponentById(ResourceTable.Id_day); weekText = (Text) findComponentById(ResourceTable.Id_week); hourText = (Text) findComponentById(ResourceTable.Id_hour); minText = (Text) findComponentById(ResourceTable.Id_min); secondText = (Text) findComponentById(ResourceTable.Id_second); } private void initData() { //获取日期 String currentDate = DateUtils.getCurrentDate("yyyy-MM-dd"); String[] split = currentDate.split("-"); monthText.setText(split[1]); dayText.setText(split[2]); //获取时间 Calendar calendar = Calendar.getInstance(); int hour = calendar.get(Calendar.HOUR_OF_DAY); hourText.setText(intToString(hour)); int min = calendar.get(Calendar.MINUTE); minText.setText(intToString(min)); int second = calendar.get(Calendar.SECOND); secondText.setText(intToString(second)); //获取星期 int week = calendar.get(Calendar.DAY_OF_WEEK); weekText.setText(getWeek(this,week)); } private class MyEventHandler extends EventHandler { public MyEventHandler(EventRunner runner) throws IllegalArgumentException { super(runner); } @Override protected void processEvent(InnerEvent event) { super.processEvent(event); int eventId = event.eventId; if (eventId == 1) { initData(); } } } @Override protected void onActive() { super.onActive(); } @Override protected void onForeground(Intent intent) { super.onForeground(intent); } @Override protected void onStop() { super.onStop(); timer.cancel(); } }FormBean.java:
@Entity(tableName = "formBean") public class FormBean extends OrmObject { @PrimaryKey() private Long formId; private String formName; private Integer dimension; public FormBean(Long formId, String formName, Integer dimension) { this.formId = formId; this.formName = formName; this.dimension = dimension; } public FormBean() { } 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; } public Integer getDimension() { return dimension; } public void setDimension(Integer dimension) { this.dimension = dimension; } }TimerAbility.java:
public class TimerAbility extends Ability { private static final HiLogLabel TAG = new HiLogLabel(HiLog.LOG_APP, 0x0, "TAG---Service"); private static final long PERIOD = 1000L; private static final int NOTIFICATION_ID = 1001; private DatabaseHelper databaseHelper = new DatabaseHelper(this); private OrmContext ormContext; @Override protected void onStart(Intent intent) { HiLog.info(TAG, "onStart"); ormContext = databaseHelper.getOrmContext("FormBean", "FormDatabase.db", FormDatabase.class); startTimer(); super.onStart(intent); } private void startTimer(){ Timer timer = new Timer(); timer.schedule(new TimerTask() { @Override public void run() { upDateFormBean(); } },0,PERIOD); } private void upDateFormBean() { OrmPredicates ormPredicates = new OrmPredicates(FormBean.class); List<FormBean> beanList = ormContext.query(ormPredicates); if (beanList.size() <= 0) { return; } for (FormBean formBean : beanList) { //更新卡片信息 ComponentProvider componentProvider = ComponentProviderUtils.getComponentProvider(formBean,this); try { updateForm(formBean.getFormId(),componentProvider); } catch (FormException e) { DatabaseUtils.deleteFormData(formBean.getFormId(),ormContext); } } } private void notice(){ NotificationRequest request = new NotificationRequest(NOTIFICATION_ID); request.setAlertOneTime(true); NotificationRequest.NotificationNormalContent normalContent = new NotificationRequest.NotificationNormalContent(); normalContent.setText(DateUtils.getCurrentDate("yyyy-MM-dd HH:mm:ss")); NotificationRequest.NotificationContent content = new NotificationRequest.NotificationContent(normalContent); request.setContent(content); keepBackgroundRunning(NOTIFICATION_ID,request); } @Override protected void onBackground() { HiLog.info(TAG, "onBackground"); super.onBackground(); } @Override protected void onStop() { HiLog.info(TAG, "onStop"); super.onStop(); } }FormDatabase.java:
@Database(entities = { FormBean.class}, version = 1) public abstract class FormDatabase extends OrmDatabase { }ComponentProviderUtils.java:
public class ComponentProviderUtils { private static Context mContext; private static final int DIMENSION_1X2 = 1; private static final int DIMENSION_2X2 = 2; private static final int DIMENSION_2X4 = 3; private static final int DIMENSION_4X4 = 4; /** * 获取ComponentProvider * * @param formBean * @param context * @return ComponentProvider */ public static ComponentProvider getComponentProvider(FormBean formBean, Context context){ mContext = context; int layoutId = ResourceTable.Layout_form_image_with_info_time_2_2; if (formBean.getDimension() == DIMENSION_2X4){ layoutId = ResourceTable.Layout_form_image_with_info_time_2_4; }else if (formBean.getDimension() == DIMENSION_1X2){ layoutId = ResourceTable.Layout_form_image_with_info_time_1_2; } ComponentProvider componentProvider =new ComponentProvider(layoutId,context); setComponentProvider(componentProvider); return componentProvider; } /** * 设置信息 * * @param componentProvider */ private static void setComponentProvider(ComponentProvider componentProvider) { Calendar instance = Calendar.getInstance(); // 时分秒 int hour = instance.get(Calendar.HOUR_OF_DAY); int min = instance.get(Calendar.MINUTE); int second = instance.get(Calendar.SECOND); componentProvider.setText(ResourceTable.Id_hour,intToString(hour)); componentProvider.setText(ResourceTable.Id_min,intToString(min)); componentProvider.setText(ResourceTable.Id_second,intToString(second)); // 星期 int week = instance.get(Calendar.DAY_OF_WEEK); String weekString = getWeek(week); componentProvider.setText(ResourceTable.Id_week,weekString); // 日期 String currentDate = DateUtils.getCurrentDate("yyyy-MM-dd"); String[] split = currentDate.split("-"); componentProvider.setText(ResourceTable.Id_month,split[1]); componentProvider.setText(ResourceTable.Id_day,split[2]); } public static String intToString(int time) { if (String.valueOf(time).length() < 2){ return "0"+time; }else { return time+""; } } //获取星期 public static String getWeek(int week) { int stringId; switch (week) { case 1: stringId = ResourceTable.String_SUNDAY; break; case 2: stringId = ResourceTable.String_MONDAY; break; case 3: stringId = ResourceTable.String_TUESDAY; break; case 4: stringId = ResourceTable.String_WEDNESDAY; break; case 5: stringId = ResourceTable.String_THURSDAY; break; case 6: stringId = ResourceTable.String_FRIDAY; break; case 7: stringId = ResourceTable.String_SATURDAY; break; default: stringId = ResourceTable.String_SUNDAY; break; } return mContext.getString(stringId); } }DatabaseUtils.java:
public class DatabaseUtils { /** * 添加卡片信息到数据库 * * @param formBean * @param ormContext */ public static void insertForm(FormBean formBean, OrmContext ormContext){ ormContext.insert(formBean); ormContext.flush(); } /** * 删除卡片信息 * * @param formId * @param ormContext */ public static void deleteFormData(long formId,OrmContext ormContext){ OrmPredicates where = ormContext.where(FormBean.class); where.equalTo("formId",formId); List<FormBean> query = ormContext.query(where); if (!query.isEmpty()){ ormContext.delete(query.get(0)); ormContext.flush(); } } }DateUtils.java:
public class DateUtils { /** * 获取当前日期 * * @param format format * @return 日期 */ public static String getCurrentDate(String format) { DateFormat dateFormat = new SimpleDateFormat(format); Date date = new Date(); String formatDate = dateFormat.format(date); return formatDate; } //获取星期 public static String getWeek(Context context, int week) { int stringId; switch (week) { case 1: stringId = ResourceTable.String_SUNDAY; break; case 2: stringId = ResourceTable.String_MONDAY; break; case 3: stringId = ResourceTable.String_TUESDAY; break; case 4: stringId = ResourceTable.String_WEDNESDAY; break; case 5: stringId = ResourceTable.String_THURSDAY; break; case 6: stringId = ResourceTable.String_FRIDAY; break; case 7: stringId = ResourceTable.String_SATURDAY; break; default: stringId = ResourceTable.String_SUNDAY; break; } return context.getString(stringId); } }git地址:https://gitee.com/fzyme_admin/time-card.git
想了解更多内容,请访问:
和华为官方合作共建的鸿蒙技术社区
https://harmonyos.51cto.com