使用hml+css+json开发JS卡片页面,支持的语法详见JS API参考中“服务卡片开发”部分。
{
“name”: “card”,
“pages”: [
“pages/index/index”
],
“window”: {
“designWidth”: 720,
“autoDesignWidth”: true
},
“type”: “form”
}
]
“forms”: [
{
“name”: “Form_Js”,
“description”: “form_description”,
“type”: “JS”,
“jsComponentName”: “card”,
“formConfigAbility”: “ability://com.huawei.demo.SecondFormAbility”,
“colorMode”: “auto”,
“isDefault”: true,
“updateEnabled”: true,
“scheduledUpdateTime”: “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有且只有一个默认卡片。
|
布尔值 |
否 |
type |
– |
表示卡片的类型。取值范围如下:
|
字符串 |
否 |
colorMode |
– |
表示卡片的主题样式,取值范围如下:
|
字符串 |
可缺省,缺省值为“auto”。 |
supportDimensions |
– |
表示卡片支持的外观规格,取值范围:
|
字符串数组 |
否 |
defaultDimension |
– |
表示卡片的默认外观规格,取值必须在该卡片supportDimensions配置的列表中。 |
字符串 |
否 |
landscapeLayouts |
– |
表示卡片外观规格对应的横向布局文件,与supportDimensions中的规格一一对应。 仅当卡片类型为Java卡片时,需要配置该标签。 |
字符串数组 |
否 |
portraitLayouts |
– |
表示卡片外观规格对应的竖向布局文件,与supportDimensions中的规格一一对应。 仅当卡片类型为Java卡片时,需要配置该标签。 |
字符串数组 |
否 |
updateEnabled |
– |
表示卡片是否支持周期性刷新,取值范围:
|
布尔类型 |
否 |
scheduledUpdateTime |
– |
表示卡片的定点刷新的时刻,采用24小时制,精确到分钟。 |
字符串 |
可缺省,缺省值为“0:0”。 |
updateDuration |
– |
表示卡片定时刷新的更新周期,单位为30分钟,取值为自然数。
|
数值 |
可缺省,缺省值为“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<Long, Integer> formEvents)
- onTriggerFormEvent(long formId, String message)
- onAcquireFormState(Intent intent)
当卡片使用方请求获取卡片时,卡片提供方会被拉起并调用onCreateForm(Intent intent)回调,intent中会带有卡片ID、卡片名称和卡片外观规格信息,可按需获取使用。
开发JS卡片时,FormAbility可以继承AceAbility或Ability,继承Ability时,需在onStart()方法中额外设置路由信息。示例分别如下:
FormAbility继承AceAbility的代码示例
public class FormAbility extends AceAbility {
……
public static long formId = –1;
public void onStart(Intent intent) {
super.onStart(intent);
}
protected ProviderFormInfo onCreateForm(Intent intent) {
long formId = intent.getLongParam(AbilitySlice.PARAM_FORM_IDENTITY_KEY, 0);
String formName = intent.getStringParam(AbilitySlice.PARAM_FORM_NAME_KEY);
int specificationId = intent.getIntParam(AbilitySlice.PARAM_FORM_DIMENSION_KEY, 0);
boolean tempFlag = intent.getBooleanParam(AbilitySlice.PARAM_FORM_TEMPORARY_KEY, false);
HiLog.info(LABEL_LOG, “onCreateForm: “ + formId + ” “ + formName + ” “ + specificationId);
FormBindingData formBindingData = new FormBindingData(“{\”temperature\”: \”60°\”}”);
ProviderFormInfo formInfo = new ProviderFormInfo();
formInfo.setJsBindingData(formBindingData);
return formInfo;
}
protected void onDeleteForm(long formId) {
// 删除卡片实例数据
super.onDeleteForm(formId);
……
}
protected void onUpdateForm(long formId) {
// 若卡片支持定时更新/定点更新/卡片使用方主动请求更新功能,则提供方需要覆写该方法以支持数据更新
super.onUpdateForm(formId);
……
}
protected void onTriggerFormEvent(long formId, String message) {
// 若卡片支持触发事件,则需要覆写该方法并实现对事件的触发
super.onTriggerFormEvent(formId, message);
……
}
protected void onCastTempForm(long formId) {
//使用方将临时卡片转换为常态卡片触发,提供方需要做相应的处理
super.onCastTempForm (formId);
……
}
protected void onEventNotify(Map<Long, Integer> formEvents) {
//使用方发起可见或者不可见通知触发,提供方需要做相应的处理
super.onEventNotify(formEvents);
……
}
protected FormState onAcquireFormState(Intent intent) {
ElementName elementName = intent.getElement();
if (elementName == null) {
HiLog.info(LABEL_LOG, “onAcquireFormState bundleName and abilityName are not set in intent”);
return FormState.UNKNOWN;
}
String bundleName = elementName.getBundleName();
String abilityName = elementName.getAbilityName();
String moduleName = intent.getStringParam(AbilitySlice.PARAM_MODULE_NAME_KEY);
String formName = intent.getStringParam(AbilitySlice.PARAM_FORM_NAME_KEY);
int specificationId = intent.getIntParam(AbilitySlice.PARAM_FORM_DIMENSION_KEY, 0);
if (“form_name2”.equals(formName)) {
return FormState.DEFAULT;
}
return FormState.READY;
}
}
FormAbility继承Ability的代码示例
public class FormAbility extends Ability {
……
public static long formId = –1;
public void onStart(Intent intent) {
super.onStart(intent);
super.setMainRoute(FormAbilitySlice.class.getName()); //设置路由
}
protected ProviderFormInfo onCreateForm(Intent intent) {
long formId = intent.getLongParam(AbilitySlice.PARAM_FORM_IDENTITY_KEY, 0);
String formName = intent.getStringParam(AbilitySlice.PARAM_FORM_NAME_KEY);
int specificationId = intent.getIntParam(AbilitySlice.PARAM_FORM_DIMENSION_KEY, 0);
boolean tempFlag = intent.getBooleanParam(AbilitySlice.PARAM_FORM_TEMPORARY_KEY, false);
HiLog.info(LABEL_LOG, “onCreateForm: “ + formId + ” “ + formName + ” “ + specificationId);
FormBindingData formBindingData = new FormBindingData(“{\”temperature\”: \”60°\”}”);
ProviderFormInfo formInfo = new ProviderFormInfo();
formInfo.setJsBindingData(formBindingData);
return formInfo;
}
protected void onDeleteForm(long formId) {
// 删除卡片实例数据
super.onDeleteForm(formId);
……
}
protected void onUpdateForm(long formId) {
// 若卡片支持定时更新/定点更新/卡片使用方主动请求更新功能,则提供方需要覆写该方法以支持数据更新
super.onUpdateForm(formId);
……
}
protected void onTriggerFormEvent(long formId, String message) {
// 若卡片支持触发事件,则需要覆写该方法并实现对事件的触发
super.onTriggerFormEvent(formId, message);
……
}
protected void onCastTempForm(long formId) {
// 使用方将临时卡片转换为常态卡片触发,提供方需要做相应的处理
// 该回调属于预留接口,当前无场景,可以先不实现
super.onCastTempForm (formId);
……
}
protected void onEventNotify(Map<Long, Integer> formEvents) {
// 使用方发起可见或者不可见通知触发,提供方需要做相应的处理
super.onEventNotify(formEvents);
……
}
protected FormState onAcquireFormState(Intent intent) {
ElementName elementName = intent.getElement();
if (elementName == null) {
HiLog.info(LABEL_LOG, “onAcquireFormState bundleName and abilityName are not set in intent”);
return FormState.UNKNOWN;
}
String bundleName = elementName.getBundleName();
String abilityName = elementName.getAbilityName();
String moduleName = intent.getStringParam(AbilitySlice.PARAM_MODULE_NAME_KEY);
String formName = intent.getStringParam(AbilitySlice.PARAM_FORM_NAME_KEY);
int specificationId = intent.getIntParam(AbilitySlice.PARAM_FORM_DIMENSION_KEY, 0);
if (“form_name2”.equals(formName)) {
return FormState.DEFAULT;
}
return FormState.READY;
}
}
卡片信息持久化。
因大部分卡片提供方都不是常驻服务,只有在需要使用时才会被拉起获取卡片信息,且卡片管理服务支持对卡片进行多实例管理,卡片ID对应实例ID,因此若卡片提供方支持对卡片数据进行配置,则需要对卡片的业务数据按照卡片ID进行持久化管理,以便在后续获取、更新以及拉起时能获取到正确的卡片业务数据。且需要适配onDeleteForm(long formId)卡片删除通知接口,在其中实现卡片实例数据的删除。
常态卡片:卡片使用方会持久化的卡片;
临时卡片:卡片使用方不会持久化的卡片;
protected ProviderFormInfo onCreateForm(Intent intent) {
long formId = intent.getIntParam(AbilitySlice.PARAM_FORM_IDENTITY_KEY, –1L);
String formName = intent.getStringParam(AbilitySlice.PARAM_FORM_NAME_KEY);
int specificationId = intent.getIntParam(AbilitySlice.PARAM_FORM_DIMENSION_KEY, 0);
boolean tempFlag = intent.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;
}
protected void onDeleteForm(long formId) {
super.onDeleteForm(formId);
// 由开发人员自行实现,删除卡片实例数据
deleteFormInfo(formId);
……
}
protected void onCastTempForm(long formId) {
// 使用方将临时卡片转换为常态卡片触发,提供方需要做相应的处理
super.onCastTempForm (formId);
……
}
卡片数据交互。
protected 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+json开发。详情请参考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”>{{city}}</text>
<text class=“txt_temperature” onclick=“routerEvent”>{{temperature}}</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;
}
json:
{
“data”: {
“temperature“: “35°”,
“city“: “hangzhou”
},
“actions“: {
“routerEvent“: {
“action“: “router”,
“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属性,其值对应到json文件的actions字段中。
- 若设置router事件,则
- action属性值为”router”;
- abilityName为卡片提供方应用的跳转目标Ability名;
- params中的值按需填写,其值在使用时通过intent.getStringParam(“params”)获取即可;
- 若设置message事件,则action属性值为”message”,params为json格式的值。
示例如下:
hml:
<div>
<text class=“txt_city” onclick=“messageEvent”>{{city}}</text>
<text class=“txt_temperature” onclick=“routerEvent”>{{temperature}}</text>
</div>
json:
{
“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,但是由于ability在后台,跳转其他页面会有一定延迟,而且卡片使用方定义的动效是不生效的。宿主侧定义的动效仅在router事件的跳转中生效。因此不建议通过message事件进行页面跳转。
如果想要保证动效,使用routerEvent。
routerEvent配置跳转链接时,只能配置到卡片提供方自己的ability中。
通过内存图片方式使用image组件
在卡片上如果想要显示网络的图片资源、数据库中查询读取的图片资源等图片资源,可以通过image组件提供的内存图片能力。具体步骤说明如下:
- 获取图片数据:
内存图片使用byte[]格式的图片数据,可以来自多个途径:比如网络的图片资源、数据库中查询读取的图片资源、本地图片打开后获得的图片资源等。
- 调用FormBindingData的addImageData接口传入数据:
- 首先创建一个ZSONObject,将{imageSrc,memory:// + picName}的键值对添加到ZSONObject中:
- ZSONObject zsonObject = new ZSONObject();
- zsonObject.put(“imageSrc”, “memory://logo.png”);
其中,imageSrc是image组件src属性关联的变量(比如我们在js文件中,image组件的写法是<image src=”{{imageSrc}}”></image>),picName是内存图片的图片名,该命名可以自定义,但是要保证图片格式的后缀名正确。
- 使用该ZSONObject去创建一个FormBindingData。
- 调用FormBindingData的addImageData接口添加数据:addImageData(“logo.png”, bytes),其中,”logo.png”为picName,必须和第一步里面添加到ZSONObject中的键值对的picName一致,否则1中的内存图片路径(”memory://logo.png”)将读取不到这里添加进去的图片数据。
- 首先创建一个ZSONObject,将{imageSrc,memory:// + picName}的键值对添加到ZSONObject中:
如果是在卡片的onCreateForm生命周期去更新共享内存图片数据,则只需创建ProviderFormInfo,然后将FormBindingData设置给ProviderFormInfo中,返回ProviderFormInfo即可。
<!– xxx.hml –>
<image src=“{{imageSrc}}”></image>
// xxx.xxx.FormAbility
@Override
protected ProviderFormInfo onCreateForm(Intent intent) {
IntentParams params = intent.getParams();
if (params == null) {
return null;
}
formId = (int) params.getParam(AbilitySlice.PARAM_FORM_ID_KEY);
String formName = (String) params.getParam(AbilitySlice.PARAM_FORM_NAME_KEY);
int specificationId = (int) params.getParam(AbilitySlice.PARAM_FORM_DIMENSION_KEY);
// ********************* memory image usage section *********************
// Step1: Create [ZSONObject] and set [picName] of [imageSrc] to it
// [imageSrc] is the variable we bind to the [src] attribute of image component in js
// such as <image src=”{{imageSrc}}”></image>
ZSONObject zsonObject = new ZSONObject();
zsonObject.put(“imageSrc”, “memory://logo.png”);
// Step2: Construct a [FormbindingData] using the [ZSONObject] we just created
FormBindingData formBindingData = new FormBindingData(zsonObject);
// Step3: Add image data to [FormBindingData] via interface: [addImageData]
formBindingData.addImageData(“logo.png”, bytes);
// ********************* memory image usage section *********************
ProviderFormInfo formInfo = new ProviderFormInfo();
formInfo.setJsBindingData(formBindingData);
return formInfo;
}
内存图片刷新的触发条件是:内存图片名称的刷新。所以更新内存图片数据时,需要更新内存图片名称,以触发新的图片数据的读取与绘制。
也就是说需要确保,对于同一个imageSrc变量,本次更新的内存图片名称与上一次使用的内存图片名称不同。以下将对两种用法进行举例说明:
- 在两个不同的路径之间来回切换:
- 第一次updateForm使用内存图片路径为”memory://logo.png”:zsonObject.put(“imageSrc”, “memory://logo.png”);
- 第二次updateForm使用的内存图片路径可以是”memory://logo1.png”:zsonObject.put(“imageSrc”, “memory://logo1.png”);
- 第三次updateForm使用的内存图片路径可以用回”memory://logo.png”:zsonObject.put(“imageSrc”, “memory://logo.png”);
这样只要将imageSrc变量的赋值在两个不同的路径之前来回切换即可,该用法适用于每次更新的图源数据没有独特标识的场景,手动管理每次更新的imageSrc与上一次更新不同。
- 每次更新时创建一个独一无二的内存图片路径:
假设我们要轮播一个图片集,该图片集的每张图片以创建时间命名,则图源的命名本身天然具备互斥属性。
那么我们在更新内存图片路径时,即可将该图片本身的名称加入内存图片路径,比如说”memory://album20210914212111.png”,其中”20210914212111″代表2021-09-14日21点21分11秒创建的图。