卡片应用是一款特殊的Ability服务,其配置文件config.json中声明以下几项,系统能够识别该应用为一款卡片应用,并与系统进行绑定。
config.json文件”abilities”配置forms模块细节如下,各属性详情可见表1。
“forms”: [
{
“name”: “Form_Java”,
“description”: “form_description”,
“type”: “Java”,
“colorMode”: “auto”,
“isDefault”: true,
“updateEnabled”: true,
“scheduledUpdateTime”: “10:30”,
“updateDuration”: 1,
“defaultDimension”: “2*2”,
“formVisibleNotify”: true,
“supportDimensions”: [
“1*2”,
“2*2”,
“2*4”,
“4*4”
],
“landscapeLayouts“: [
“$layout:form_ability_layout_1_2“,
“$layout:form_ability_layout_2_2“,
“$layout:form_ability_layout_2_4″,
“$layout:form_ability_layout_4_4“
],
“portraitLayouts“: [
“$layout:form_ability_layout_1_2“,
“$layout:form_ability_layout_2_2″,
“$layout:form_ability_layout_2_4″,
“$layout:form_ability_layout_4_4“
],
“formConfigAbility”: “ability://SecondFormAbility”,
“metaData”: {
“customizeData”: [
{
“name”: “originWidgetName”,
“value”: “com.huawei.weather.testWidget”
}
]
}
}
]
“forms”模块中的name为卡片名,即在onCreateForm中根据AbilitySlice.PARAM_FORM_NAME_KEY可取到的值。
在卡片所在的”abilities”中还需要配置”visible”: true和”formsEnabled”: true。
属性名称 |
子属性名称 |
含义 |
数据类型 |
是否可缺省 |
---|---|---|---|---|
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)
- onAcquireFormState(Intent intent)
在onCreateForm(Intent intent)中,当卡片使用方请求获取卡片时,卡片提供方会被拉起并调用onCreateForm(Intent intent)回调,intent中会带有卡片ID,卡片名称,临时卡片标记和卡片外观规格信息,分别通过AbilitySlice.PARAM_FORM_IDENTITY_KEY、AbilitySlice.PARAM_FORM_NAME_KEY、AbilitySlice.PARAM_FORM_TEMORARY_KEY和AbilitySlice.PARAM_FORM_DIMENSION_KEY按需获取。
public class FormAbility extends Ability {
……
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);
// 获取自定义数据
IntentParams intentParams = intent.getParam(AbilitySlice.PARAM_FORM_CUSTOMIZE_KEY);
HiLog.info(LABEL_LOG, “onCreateForm: “ + formId + ” “ + formName + ” “ + specificationId);
// 开发者需要根据卡片的名称以及外观规格获取对应的xml布局并构造卡片对象,此处ResourceTable.Layout_form_ability_layout_2_2仅为示例
ProviderFormInfo formInfo = new ProviderFormInfo(ResourceTable.Layout_form_ability_layout_2_2, this);
// 获取卡片信息
String formData = getInitFormData(formName, specificationId);
ComponentProvider componentProvider = formInfo.getComponentProvider();
componentProvider.setText(ResourceTable.Id_title, “formData-“ + formData);
formInfo.mergeActions(componentProvider);
……
HiLog.info(LABEL_LOG, “onCreateForm finish…….”);
return formInfo;
}
protected void onDeleteForm(long formId) {
super.onDeleteForm(formId);
// 删除卡片实例数据,需要由开发者实现
deleteFormInfo(formId);
……
}
// 若卡片支持定时更新/定点更新/卡片使用方主动请求更新功能,则提供方需要覆写该方法以支持数据更新
protected void onUpdateForm(long formId) {
super.onUpdateForm(formId);
// 更新卡片信息,由开发者实现
……
}
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进行持久化管理,以便在后续获取、更新以及拉起时能获取到正确的卡片业务数据。
protected ProviderFormInfo onCreateForm(Intent intent) {
long formId = intent.getLongParam(AbilitySlice.PARAM_FORM_ID_KEY, –1L);
String formName = intent.getStringParam(AbilitySlice.PARAM_FORM_NAME_KEY);
int specificationId = intent.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;
}
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);
ComponentProvider componentProvider = new ComponentProvider(ResourceTable.Layout_form_ability_layout_2_2, this);
// 获取卡片实例需要更新的卡片数据,需要由开发者实现
String formData = getUpdateFormData(formId);
componentProvider.setText(ResourceTable.Id_title, “update formData-“ + formData);
updateForm(formId, componentProvider);
……
}
卡片使用方点击拉起卡片页面,会在onStart(Intent intent)中携带formId(通过AbilitySlice.PARAM_FORM_IDENTITY_KEY获取),若需要在AbilitySlice中更新,也可以使用updateForm接口进行更新,示例如下:
public class FormAbilitySlice extends AbilitySlice {
……
public void onStart(Intent intent) {
super.onStart(intent);
……
Button button = new Button(this);
button.setText(“Update form data”);
button.setClickedListener(component -> {
……
if (intent.hasParameter(AbilitySlice.PARAM_FORM_IDENTITY_KEY)) {
int formId = intent.getIntParam(AbilitySlice.PARAM_FORM_ID_KEY, –1);
ComponentProvider componentProvider = new ComponentProvider(ResourceTable.Layout_form_ability_layout_2_2, context);
String formData = getUpdateFormData(formId);
componentProvider.setText(ResourceTable.Id_modifylayout, “update formData-“ + formData);
getAbility().updateForm(formId, componentProvider);
}
});
……
}
}
Java卡片控制事件。
Java卡片当前通过IntentAgent能力支持对卡片控制设置事件,例如可以使用START_ABILITY、START_SERVICE这两类能力,在点击整张卡片时,跳转到提供卡片的ability。(注:Intent中支持自定义参数的传递,支持的类型有int/long/String/List)
示例如下:
protected ProviderFormInfo onCreateForm(Intent intent) {
……
ProviderFormInfo formInfo = new ProviderFormInfo(ResourceTable.Layout_form_ability_layout_2_2, this);
ComponentProvider componentProvider = new ComponentProvider();
// 针对title控件设置事件
componentProvider.setIntentAgent(ResourceTable.Id_title, startAbilityIntentAgent());
formInfo.mergeActions(componentProvider);
……
return formInfo;
}
// 设置触发的事件为系统预置的HarmonyOS betaApp应用
private IntentAgent startAbilityIntentAgent() {
Intent intent = new Intent();
Operation operation = new Intent.OperationBuilder()
.withDeviceId(“”)
.withBundleName(“com.huawei.ohos.betaapp.link”)
.withAbilityName(“com.huawei.ohos.betaapp.link.MainAbility”)
.build();
intent.setOperation(operation);
List<Intent> intentList = new ArrayList<>();
intentList.add(intent);
List<Flags> flags = new ArrayList<>();
flags.add(Flags.UPDATE_PRESENT_FLAG);
IntentAgentInfo paramsInfo = new IntentAgentInfo(200, IntentAgentConstant.OperationType.START_ABILITY, flags, intentList, null);
IntentAgent intentAgent = IntentAgentHelper.getIntentAgent(this, paramsInfo);
return intentAgent;
}
开发Java卡片布局。
在使用DevEco Studio创建模块时会生成对应的Java UI xml布局文件,具体规则请参考《XML创建布局》,需要注意设置ohos:remote=”true”。
<DependentLayout xmlns:ohos=“http://schemas.huawei.com/res/ohos”
ohos:width=“match_parent”
ohos:height=“match_parent”
ohos:id=“$+id:background”
ohos:orientation=“vertical”
ohos:background_element=“$media:weather”
ohos:remote=“true”>
<Text
ohos:id=“$+id:title”
ohos:text=“天气1”
ohos:text_size=“39px”
ohos:text_color=“#b0c4de”
ohos:top_margin=“42px”
ohos:left_margin=“20px”
ohos:width=“match_content”
ohos:height=“match_content”/>
<Text
ohos:id=“$+id:temperature”
ohos:text=“35°”
ohos:text_size=“100px”
ohos:text_color=“#b0c4de”
ohos:top_margin=“25px”
ohos:left_margin=“20px”
ohos:below=“$id:title”
ohos:width=“match_content”
ohos:height=“match_content”/>
<Text
ohos:id=“$+id:location”
ohos:text=“上海”
ohos:text_size=“39px”
ohos:text_color=“#b0c4de”
ohos:top_margin=“24px”
ohos:left_margin=“20px”
ohos:below=“$id:temperature”
ohos:width=“match_content”
ohos:height=“match_content”/>
<Text
ohos:id=“$+id:textView4”
ohos:text=“9月4号 星期五”
ohos:text_size=“39px”
ohos:text_color=“#b0c4de”
ohos:top_margin=“10px”
ohos:left_margin=“20px”
ohos:below=“$id:location”
ohos:width=“match_content”
ohos:height=“match_content”/>
<Text
ohos:id=“$+id:textView5”
ohos:text=“多云”
ohos:text_size=“39px”
ohos:text_color=“#b0c4de”
ohos:top_margin=“10px”
ohos:left_margin=“150px”
ohos:below=“$id:location”
ohos:end_of=“$id:textView4”
ohos:align_parent_end=“true”
ohos:width=“match_content”
ohos:height=“match_content”/>
<Image
ohos:id=“$+id:imageView”
ohos:width=“160px”
ohos:height=“150px”
ohos:top_margin=“20px”
ohos:left_margin=“150px”
ohos:below=“$id:title”
ohos:end_of=“$id:temperature”
ohos:image_src=“$media:clouds”/>
</DependentLayout>