JS卡片开发步骤
使用hml+css+js开发JS卡片页面,支持的语法详见JS API参考中“服务卡片开发”部分。
使用DevEco Studio创建卡片工程。
创建成功后,在config.json的module中会生成js模块,用于对应卡片的js相关资源,配置示例如下:
"js": [ { "name": "card", "pages": [ "pages/index/index" ], "window": { "designWidth": 720, "autoDesignWidth": true }, "type": "form" }]
config.json文件“abilities”配置forms模块细节如下,各属性详情可见表1。
"forms": [ { "name": "Form_Js", "description": "form_description", "type": "JS", "jsComponentName": "card", "formConfigAbility": "ability://com.huawei.demo.SecondFormAbility", "colorMode": "auto", "isDefault": true, "updateEnabled": true, "scheduledUpateTime": "10:30", "updateDuration": 1, "defaultDimension": "2*2", "supportDimensions": [ "2*2", "2*4", "4*4" ], "metaData": { "customizeData": [ { "name": "originWidgetName", "value": "com.huawei.weather.testWidget" } ] } }]
说明
配置文件中,应注意如下配置:
- “js”模块中的name字段要与“forms”模块中的jsComponentName字段的值一致,为js资源的实例名。
- “forms”模块中的name为卡片名,即在onCreateForm中根据AbilitySlice.PARAM_FORM_NAME_KEY可取到的值。
- 除此之外,卡片的Ability中还需要配置"visible": true和"formsEnabled": true。
- 定时刷新和定点刷新都配置的情况下,定时刷新优先。
- defaultDimension是默认规格,必须设置。
属性名称 | 子属性名称 | 含义 | 数据类型 | 是否可缺省 |
---|---|---|---|---|
name | - | 表示卡片的类名。字符串最大长度为127字节。 | 字符串 | 否 |
description | - | 表示卡片的描述。取值可以是描述性内容,也可以是对描述性内容的资源索引,以支持多语言。字符串最大长度为255字节。 | 字符串 | 可缺省,缺省为空。 |
isDefault | - | 表示该卡片是否为默认卡片,每个Ability有且只有一个默认卡片。true:默认卡片。false:非默认卡片。 | 布尔值 | 否 |
type | - | 表示卡片的类型。取值范围如下:Java:Java卡片。JS:JS卡片。 | 字符串 | 否 |
colorMode | - | 表示卡片的主题样式,取值范围如下:auto:自适应。dark:深色主题。light:浅色主题。 | 字符串 | 可缺省,缺省值为“auto”。 |
supportDimensions | - | 表示卡片支持的外观规格,取值范围:12:表示1行2列的二宫格。22:表示2行2列的四宫格。24:表示2行4列的八宫格。44:表示4行4列的十六宫格。 | 字符串数组 | 否 |
defaultDimension | - | 表示卡片的默认外观规格,取值必须在该卡片supportDimensions配置的列表中。 | 字符串 | 否 |
landscapeLayouts | - | 表示卡片外观规格对应的横向布局文件,与supportDimensions中的规格一一对应。仅当卡片类型为Java卡片时,需要配置该标签。 | 字符串数组 | 否 |
portraitLayouts | - | 表示卡片外观规格对应的竖向布局文件,与supportDimensions中的规格一一对应。仅当卡片类型为Java卡片时,需要配置该标签。 | 字符串数组 | 否 |
updateEnabled | - | 表示卡片是否支持周期性刷新,取值范围:true:表示支持周期性刷新,可以在定时刷新(updateDuration)和定点刷新(scheduledUpdateTime)两种方式任选其一,优先选择定时刷新。false:表示不支持周期性刷新。 | 布尔类型 | 否 |
scheduledUpdateTime | - | 表示卡片的定点刷新的时刻,采用24小时制,精确到分钟。 | 字符串 | 可缺省,缺省值为“0:0”。 |
updateDuration | - | 表示卡片定时刷新的更新周期,单位为30分钟,取值为自然数。当取值为0时,表示该参数不生效。当取值为正整数N时,表示刷新周期为30*N分钟。 | 数值 | 可缺省,缺省值为“0”。 |
formConfigAbility | - | 表示卡片的配置跳转链接,采用URI格式。 | 字符串 | 可缺省,缺省值为空。 |
jsComponentName | - | 表示JS卡片的Component名称。字符串最大长度为127字节。仅当卡片类型为JS卡片时,需要配置该标签。 | 字符串 | 否 |
metaData | - | 表示卡片的自定义信息,包含customizeData数组标签。 | 对象 | 可缺省,缺省值为空。 |
customizeData | - | 表示自定义的卡片信息。 | 对象数组 | 可缺省,缺省值为空。 |
name | 表示数据项的键名称。字符串最大长度为255字节。 | 字符串 | 可缺省,缺省值为空。 | |
value | 表示数据项的值。字符串最大长度为255字节。 | 字符串 | 可缺省,缺省值为空。 |
创建一个FormAbility,覆写卡片相关回调函数。
- onCreateForm(Intent intent)
- onUpdateForm(long formId)
- onDeleteForm(long formId)
- onCastTempForm(long formId)
- onEventNotify(Map
formEvents) - onTriggerFormEvent(long formId, String message)
当卡片使用方请求获取卡片时,卡片提供方会被拉起并调用onCreateForm(Intent intent)回调,intent中会带有卡片ID,卡片名称和卡片外观规格信息,可按需获取使用。
开发Js卡片时,FormAbility可以继承AceAbility或Ability,继承Ability时,需在onStart()方法中额外设置路由信息。示例分别如下:
FormAbility继承AceAbility的代码示例
public class FormAbility extends AceAbility { ...... public static long formId = -1; @Override public void onStart(Intent intent) { super.onStart(intent); } @Override protected ProviderFormInfo onCreateForm(Intent intent) { IntentParams params = intent.getParams(); if (params == null) { return null; } // 卡片ID formId = (long) params.getParam(AbilitySlice.PARAM_FORM_IDENTITY_KEY); // 卡片名称 String formName = (String) params.getParam(AbilitySlice.PARAM_FORM_NAME_KEY); // 卡片规格信息 int specificationId = (int) params.getParam(AbilitySlice.PARAM_FORM_DIMENSION_KEY); HiLog.info(LABEL_LOG, "onCreateForm: " + formId + " " + formName + " " + specificationId); FormBindingData formBindingData = new FormBindingData("{\"temperature\": \"60°\"}"); ProviderFormInfo formInfo = new ProviderFormInfo(); formInfo.setJsBindingData(formBindingData); return formInfo; } @Override protected void onDeleteForm(long formId) { // 删除卡片实例数据 super.onDeleteForm(formId); ...... } @Override protected void onUpdateForm(long formId) { // 若卡片支持定时更新/定点更新/卡片使用方主动请求更新功能,则提供方需要覆写该方法以支持数据更新 super.onUpdateForm(formId); ...... } @Override protected void onTriggerFormEvent(long formId, String message) { // 若卡片支持触发事件,则需要覆写该方法并实现对事件的触发 super.onTriggerFormEvent(formId, message); ...... } @Override protected void onCastTempForm(long formId) { //使用方将临时卡片转换为常态卡片触发,提供方需要做相应的处理 super.onCastTempForm (formId); ...... } @Override protected void onEventNotify(Map<Long, Integer> formEvents) { //使用方发起可见或者不可见通知触发,提供方需要做相应的处理 super.onEventNotify(formEvents); ...... }}
FormAbility继承Ability的代码示例
public class FormAbility extends Ability { ...... public static long formId = -1; @Override public void onStart(Intent intent) { super.onStart(intent); super.setMainRoute(FormAbilitySlice.class.getName()); //设置路由 } @Override protected ProviderFormInfo onCreateForm(Intent intent) { IntentParams params = intent.getParams(); if (params == null) { return null; } // 卡片ID formId = (long) params.getParam(AbilitySlice.PARAM_FORM_IDENTITY_KEY); // 卡片名称 String formName = (String) params.getParam(AbilitySlice.PARAM_FORM_NAME_KEY); // 卡片规格信息 int specificationId = (int) params.getParam(AbilitySlice.PARAM_FORM_DIMENSION_KEY); HiLog.info(LABEL_LOG, "onCreateForm: " + formId + " " + formName + " " + specificationId); FormBindingData formBindingData = new FormBindingData("{\"temperature\": \"60°\"}"); ProviderFormInfo formInfo = new ProviderFormInfo(); formInfo.setJsBindingData(formBindingData); return formInfo; } @Override protected void onDeleteForm(long formId) { // 删除卡片实例数据 super.onDeleteForm(formId); ...... } @Override protected void onUpdateForm(long formId) { // 若卡片支持定时更新/定点更新/卡片使用方主动请求更新功能,则提供方需要覆写该方法以支持数据更新 super.onUpdateForm(formId); ...... } @Override protected void onTriggerFormEvent(long formId, String message) { // 若卡片支持触发事件,则需要覆写该方法并实现对事件的触发 super.onTriggerFormEvent(formId, message); ...... } @Override protected void onCastTempForm(long formId) { //使用方将临时卡片转换为常态卡片触发,提供方需要做相应的处理 super.onCastTempForm (formId); ...... } @Override protected void onEventNotify(Map<Long, Integer> formEvents) { //使用方发起可见或者不可见通知触发,提供方需要做相应的处理 super.onEventNotify(formEvents); ...... }}
卡片信息持久化。
因大部分卡片提供方都不是常驻服务,只有在需要使用时才会被拉起获取卡片信息,且卡片框架支持对卡片进行多实例管理,卡片ID对应实例ID,因此若卡片提供方支持对卡片数据进行配置,则需要对卡片的业务数据按照卡片ID进行持久化管理,以便在后续获取、更新以及拉起时能获取到正确的卡片业务数据。且需要适配onDeleteForm(long formId)卡片删除通知接口,在其中实现卡片实例数据的删除。
常态卡片:卡片使用方会持久化的卡片;
暂态卡片:卡片使用方不会持久化的卡片;
需要注意的是,卡片使用方在请求卡片时传递给提供方应用的Intent数据中存在临时标记字段,表示此次请求的卡片是否为临时卡片,由于临时卡片的数据不会持久化的特殊性,某些场景比如卡片服务框架死亡重启,此时临时卡片数据在卡片服务框架中已经删除,且对应的卡片id不会通知到提供方,所以卡片提供方需要自己负责清理长时间未删除的临时卡片数据。同时对应的卡片使用方可能会将之前请求的临时卡片转换为常态卡片。如果转换成功,卡片提供方也需要对对应的临时卡片id进行处理,把卡片提供方记录的临时卡片数据转换为常态卡片数据,防止提供方在清理长时间未删除的临时卡片时,把已经转换为常态卡片的临时卡片信息删除,导致卡片信息丢失。
@Overrideprotected ProviderFormInfo onCreateForm(Intent intent) {long formId = intent.getIntParam(AbilitySlice.PARAM_FORM_IDENTITY_KEY, -1L); String formName = params.getStringParam(AbilitySlice.PARAM_FORM_NAME_KEY); int specificationId = params.getIntParam(AbilitySlice.PARAM_FORM_DIMENSION_KEY, 0); boolean tempFlag = params.getBooleanParam(AbilitySlice.PARAM_FORM_TEMPORARY_KEY, false); HiLog.info(LABEL_LOG, "onCreateForm: " + formId + " " + formName + " " + specificationId); ....... // 由开发人员自行实现,将创建的卡片信息持久化,以便在下次获取/更新该卡片实例时进行使用 storeFormInfo(formId, formName, specificationId, formData); ...... HiLog.info(LABEL_LOG, "onCreateForm finish......."); return formInfo;}@Overrideprotected void onDeleteForm(long formId) { super.onDeleteForm(formId); // 由开发人员自行实现,删除卡片实例数据 deleteFormInfo(formId); ......}@Overrideprotected void onCastTempForm(long formId) { // 使用方将临时卡片转换为常态卡片触发,提供方需要做相应的处理 super.onCastTempForm (formId); ......}
卡片数据交互。
当卡片应用需要更新数据时(如触发了定时更新或定点更新),卡片应用获取最新数据,并调用updateForm接口更新卡片。示例如下:
@Overrideprotected void onUpdateForm(long formId) { super.onUpdateForm(formId); ZSONObject zsonObject = new ZSONObject(); zsonObject.put("temperature", "90°"); FormBindingData formBindingData = new FormBindingData(zsonObject); // 调用updateForm接口去更新对应的卡片,仅更新入参中携带的数据信息,其他信息保持不变 if (!updateForm(formId, formBindingData)) { // err process }}
开发JS卡片页面。
JS卡片页面与普通FA类似通过hml+css+js开发,但不支持复杂js语法。详情请参考JS API参考。
示例如下:
hml:<div class="container"> <stack class="stack_container"> <image class = "img" src="common/clouds.png"></image> <div style="flex-direction: column;"> <text class="txt_city" onclick="messageEvent"></text> <text class="txt_temperature" onclick="routerEvent"></text> </div> </stack></div> css:.container { flex-direction: column; justify-content: center; align-items: center;} .stack_container { width: 100%; height: 100%; background-image: url("/common/weather-background-day.png"); background-size: cover;}... js:export default { data: { temperature: "35°", city: "hangzhou" }, actions: { routerEvent: { action: "router", bundleName: "com.example.myapplication", abilityName: "com.example.myapplication.FormAbility", params: { message: "weather" } }, messageEvent: { action: "message", params: { message: "weather update" } } }}
开发JS卡片事件和Action。
JS卡片支持为组件设置action,包括router事件和message事件,其中router事件用于应用跳转,message事件用于卡片开发人员自定义点击事件。关键步骤说明如下:
- 在hml中为组件设置onclick属性,其值对应到js文件的actions属性中。
- 若设置router事件,则
- action属性值为"router";
- abilityName为卡片提供方应用的跳转目标Ability名;
- params中的值按需填写,其值在使用时通过intent.getStringParam("params")获取即可;
- 若设置message事件,则action属性值为"message",params为json格式的值。
示例如下:
hml:<text class="txt_city" onclick="messageEvent"></text><text class="txt_temperature" onclick="routerEvent"></text>
js:export default { actions: { routerEvent: { action: "router", abilityName: "com.example.myapplication.FormAbility", params: { message: "weather" } }, messageEvent: { action: "message", params: { message: "test date", } } }}
当点击组件触发message事件时,卡片应用的onTriggerFormEvent方法被触发,params属性的值将作为参数被传入,解析使用即可。
说明
message事件由于是自定义,也可以在message事件中实现跳转到其他Ability的能力。但是,在这种情况下,宿主侧定义的动效是不生效的。宿主侧定义的动效仅在router事件的跳转中生效。
如果想要保证动效,使用routerEvent。
routerEvent配置跳转链接时,只能配置到卡片提供方自己的ability中。