1# FA卡片开发指导 2 3## 卡片概述 4卡片是一种界面展示形式,可以将应用的重要信息或操作前置到卡片,以达到服务直达,减少体验层级的目的。 5 6卡片常用于嵌入到其他应用(当前只支持系统应用)中作为其界面的一部分显示,并支持拉起页面,发送消息等基础的交互功能。卡片使用方负责显示卡片。 7 8卡片的基本概念: 9- 卡片提供方:提供卡片显示内容原子化服务,控制卡片的显示内容、控件布局以及控件点击事件。 10- 卡片使用方:显示卡片内容的宿主应用,控制卡片在宿主中展示的位置。 11- 卡片管理服务:用于管理系统中所添加卡片的常驻代理服务,包括卡片对象的管理与使用,以及卡片周期性刷新等。 12 13> **说明:** 14> 卡片使用方和提供方不要求常驻运行,在需要添加/删除/请求更新卡片时,卡片管理服务会拉起卡片提供方获取卡片信息。 15 16开发者仅需作为卡片提供方进行卡片内容的开发,卡片使用方和卡片管理服务由系统自动处理。 17 18卡片提供方控制卡片实际显示的内容、控件布局以及点击事件。 19 20## 场景介绍 21 22FA卡片开发,即基于[FA模型](fa-brief.md)的卡片提供方开发,主要涉及如下功能逻辑: 23 24- 开发卡片生命周期回调函数LifecycleForm。 25- 创建卡片数据FormBindingData对象。 26- 通过FormProvider更新卡片。 27- 开发卡片页面。 28 29## 接口说明 30 31卡片生命周期回调函数LifecycleForm的接口如下: 32 33**表1** LifecycleForm API接口功能介绍 34 35| 接口名 | 描述 | 36| :----------------------------------------------------------- | :------------------------------------------- | 37| onCreate(want: Want): formBindingData.FormBindingData | 卡片提供方接收创建卡片的通知接口。 | 38| onCastToNormal(formId: string): void | 卡片提供方接收临时卡片转常态卡片的通知接口。 | 39| onUpdate(formId: string): void | 卡片提供方接收更新卡片的通知接口。 | 40| onVisibilityChange(newStatus: { [key: string]: number }): void | 卡片提供方接收修改可见性的通知接口。 | 41| onEvent(formId: string, message: string): void | 卡片提供方接收处理卡片事件的通知接口。 | 42| onDestroy(formId: string): void | 卡片提供方接收销毁卡片的通知接口。 | 43| onAcquireFormState?(want: Want): formInfo.FormState | 卡片提供方接收查询卡片状态的通知接口。 | 44 45FormProvider类具体的API详见[接口文档](../reference/apis/js-apis-application-formProvider.md)。 46 47**表2** FormProvider API接口功能介绍 48 49| 接口名 | 描述 | 50| :----------------------------------------------------------- | :------------------------------------------------ | 51| setFormNextRefreshTime(formId: string, minute: number, callback: AsyncCallback<void>): void; | 设置指定卡片的下一次更新时间。 | 52| setFormNextRefreshTime(formId: string, minute: number): Promise<void>; | 设置指定卡片的下一次更新时间,以promise方式返回。 | 53| updateForm(formId: string, formBindingData: FormBindingData, callback: AsyncCallback<void>): void; | 更新指定的卡片。 | 54| updateForm(formId: string, formBindingData: FormBindingData): Promise<void>; | 更新指定的卡片,以promise方式返回。 | 55 56## 开发步骤 57 58### 创建LifecycleForm 59 60创建FA模型的卡片,需实现LifecycleForm的生命周期接口。具体示例代码如下: 61 621. 导入相关模块。 63 64 ```javascript 65 import formBindingData from '@ohos.application.formBindingData' 66 import formInfo from '@ohos.application.formInfo' 67 import formProvider from '@ohos.application.formProvider' 68 ``` 69 702. 实现LifecycleForm生命周期接口。 71 72 ```javascript 73 export default { 74 onCreate(want) { 75 console.log('FormAbility onCreate'); 76 // 由开发人员自行实现,将创建的卡片信息持久化,以便在下次获取/更新该卡片实例时进行使用 77 let obj = { 78 "title": "titleOnCreate", 79 "detail": "detailOnCreate" 80 }; 81 let formData = formBindingData.createFormBindingData(obj); 82 return formData; 83 }, 84 onCastToNormal(formId) { 85 // 使用方将临时卡片转换为常态卡片触发,提供方需要做相应的处理 86 console.log('FormAbility onCastToNormal'); 87 }, 88 onUpdate(formId) { 89 // 若卡片支持定时更新/定点更新/卡片使用方主动请求更新功能,则提供方需要重写该方法以支持数据更新 90 console.log('FormAbility onUpdate'); 91 let obj = { 92 "title": "titleOnUpdate", 93 "detail": "detailOnUpdate" 94 }; 95 let formData = formBindingData.createFormBindingData(obj); 96 formProvider.updateForm(formId, formData).catch((error) => { 97 console.log('FormAbility updateForm, error:' + JSON.stringify(error)); 98 }); 99 }, 100 onVisibilityChange(newStatus) { 101 // 使用方发起可见或者不可见通知触发,提供方需要做相应的处理 102 console.log('FormAbility onVisibilityChange'); 103 }, 104 onEvent(formId, message) { 105 // 若卡片支持触发事件,则需要重写该方法并实现对事件的触发 106 console.log('FormAbility onEvent'); 107 }, 108 onDestroy(formId) { 109 // 删除卡片实例数据 110 console.log('FormAbility onDestroy'); 111 }, 112 onAcquireFormState(want) { 113 console.log('FormAbility onAcquireFormState'); 114 return formInfo.FormState.READY; 115 }, 116 } 117 ``` 118 119### 配置卡片配置文件 120 121卡片需要在应用配置文件config.json中进行配置。 122 123- js模块,用于对应卡片的js相关资源,内部字段结构说明: 124 125 | 属性名称 | 含义 | 数据类型 | 是否可缺省 | 126 | -------- | ------------------------------------------------------------ | -------- | ------------------------ | 127 | name | 表示JS Component的名字。该标签不可缺省,默认值为default。 | 字符串 | 否 | 128 | pages | 表示JS Component的页面用于列举JS Component中每个页面的路由信息[页面路径+页面名称]。该标签不可缺省,取值为数组,数组第一个元素代表JS FA首页。 | 数组 | 否 | 129 | window | 用于定义与显示窗口相关的配置。 | 对象 | 可缺省 | 130 | type | 表示JS应用的类型。取值范围如下:<br />normal:标识该JS Component为应用实例。<br />form:标识该JS Component为卡片实例。 | 字符串 | 可缺省,缺省值为“normal” | 131 | mode | 定义JS组件的开发模式。 | 对象 | 可缺省,缺省值为空 | 132 133 配置示例如下: 134 135 ```json 136 "js": [{ 137 "name": "widget", 138 "pages": ["pages/index/index"], 139 "window": { 140 "designWidth": 720, 141 "autoDesignWidth": true 142 }, 143 "type": "form" 144 }] 145 ``` 146 147- abilities模块,用于对应卡片的LifecycleForm,内部字段结构说明: 148 149 | 属性名称 | 含义 | 数据类型 | 是否可缺省 | 150 | ------------------- | ------------------------------------------------------------ | ---------- | ------------------------ | 151 | name | 表示卡片的类名。字符串最大长度为127字节。 | 字符串 | 否 | 152 | description | 表示卡片的描述。取值可以是描述性内容,也可以是对描述性内容的资源索引,以支持多语言。字符串最大长度为255字节。 | 字符串 | 可缺省,缺省为空。 | 153 | isDefault | 表示该卡片是否为默认卡片,每个Ability有且只有一个默认卡片。<br />true:默认卡片。<br />false:非默认卡片。 | 布尔值 | 否 | 154 | type | 表示卡片的类型。取值范围如下:<br />JS:JS卡片。 | 字符串 | 否 | 155 | colorMode | 表示卡片的主题样式,取值范围如下:<br />auto:自适应。<br />dark:深色主题。<br />light:浅色主题。 | 字符串 | 可缺省,缺省值为“auto”。 | 156 | supportDimensions | 表示卡片支持的外观规格,取值范围:<br />1 * 2:表示1行2列的二宫格。<br />2 * 2:表示2行2列的四宫格。<br />2 * 4:表示2行4列的八宫格。<br />4 * 4:表示4行4列的十六宫格。 | 字符串数组 | 否 | 157 | defaultDimension | 表示卡片的默认外观规格,取值必须在该卡片supportDimensions配置的列表中。 | 字符串 | 否 | 158 | updateEnabled | 表示卡片是否支持周期性刷新,取值范围:<br />true:表示支持周期性刷新,可以在定时刷新(updateDuration)和定点刷新(scheduledUpdateTime)两种方式任选其一,优先选择定时刷新。<br />false:表示不支持周期性刷新。 | 布尔类型 | 否 | 159 | scheduledUpdateTime | 表示卡片的定点刷新的时刻,采用24小时制,精确到分钟。<br />updateDuration参数优先级高于scheduledUpdateTime,两者同时配置时,以updateDuration配置的刷新时间为准。 | 字符串 | 可缺省,缺省值为“0:0”。 | 160 | updateDuration | 表示卡片定时刷新的更新周期,单位为30分钟,取值为自然数。<br />当取值为0时,表示该参数不生效。<br />当取值为正整数N时,表示刷新周期为30*N分钟。<br />updateDuration参数优先级高于scheduledUpdateTime,两者同时配置时,以updateDuration配置的刷新时间为准。 | 数值 | 可缺省,缺省值为“0”。 | 161 | formConfigAbility | 表示卡片的配置跳转链接,采用URI格式。 | 字符串 | 可缺省,缺省值为空。 | 162 | formVisibleNotify | 标识是否允许卡片使用卡片可见性通知。 | 字符串 | 可缺省,缺省值为空。 | 163 | jsComponentName | 表示JS卡片的Component名称。字符串最大长度为127字节。 | 字符串 | 否 | 164 | metaData | 表示卡片的自定义信息,包含customizeData数组标签。 | 对象 | 可缺省,缺省值为空。 | 165 | customizeData | 表示自定义的卡片信息。 | 对象数组 | 可缺省,缺省值为空。 | 166 167 配置示例如下: 168 169 ```json 170 "abilities": [{ 171 "name": "FormAbility", 172 "description": "This is a FormAbility", 173 "formsEnabled": true, 174 "icon": "$media:icon", 175 "label": "$string:form_FormAbility_label", 176 "srcPath": "FormAbility", 177 "type": "service", 178 "srcLanguage": "ets", 179 "formsEnabled": true, 180 "forms": [{ 181 "colorMode": "auto", 182 "defaultDimension": "2*2", 183 "description": "This is a service widget.", 184 "formVisibleNotify": true, 185 "isDefault": true, 186 "jsComponentName": "widget", 187 "name": "widget", 188 "scheduledUpdateTime": "10:30", 189 "supportDimensions": ["2*2"], 190 "type": "JS", 191 "updateEnabled": true 192 }] 193 }] 194 ``` 195 196 197### 卡片信息的持久化 198 199因大部分卡片提供方都不是常驻服务,只有在需要使用时才会被拉起获取卡片信息,且卡片管理服务支持对卡片进行多实例管理,卡片ID对应实例ID,因此若卡片提供方支持对卡片数据进行配置,则需要对卡片的业务数据按照卡片ID进行持久化管理,以便在后续获取、更新以及拉起时能获取到正确的卡片业务数据。 200 201```javascript 202 onCreate(want) { 203 console.log('FormAbility onCreate'); 204 205 let formId = want.parameters["ohos.extra.param.key.form_identity"]; 206 let formName = want.parameters["ohos.extra.param.key.form_name"]; 207 let tempFlag = want.parameters["ohos.extra.param.key.form_temporary"]; 208 // 由开发人员自行实现,将创建的卡片信息持久化,以便在下次获取/更新该卡片实例时进行使用 209 // storeFormInfo 接口未在此处实现,具体实现请参考:相关实例 章节中的 FormAbility FA模型卡片 实例 210 storeFormInfo(formId, formName, tempFlag, want); 211 212 let obj = { 213 "title": "titleOnCreate", 214 "detail": "detailOnCreate" 215 }; 216 let formData = formBindingData.createFormBindingData(obj); 217 return formData; 218 } 219``` 220 221同时需要适配onDestroy卡片删除通知接口,在其中实现卡片实例数据的删除。 222 223```javascript 224 onDestroy(formId) { 225 console.log('FormAbility onDestroy'); 226 227 // 由开发人员自行实现,删除之前持久化的卡片实例数据 228 // deleteFormInfo 接口未在此处实现,具体实现请参考:相关实例 章节中的 FormAbility FA模型卡片 实例 229 deleteFormInfo(formId); 230 } 231``` 232 233具体的持久化方法可以参考[通过用户首选项实现数据持久化](../database/data-persistence-by-preferences.md)。 234 235需要注意的是,卡片使用方在请求卡片时传递给提供方应用的Want数据中存在临时标记字段,表示此次请求的卡片是否为临时卡片: 236 237- 常态卡片:卡片使用方会持久化的卡片; 238 239- 临时卡片:卡片使用方不会持久化的卡片; 240 241由于临时卡片的数据具有非持久化的特殊性,某些场景比如卡片服务框架死亡重启,此时临时卡片数据在卡片管理服务中已经删除,且对应的卡片ID不会通知到提供方,所以卡片提供方需要自己负责清理长时间未删除的临时卡片数据。同时对应的卡片使用方可能会将之前请求的临时卡片转换为常态卡片。如果转换成功,卡片提供方也需要对对应的临时卡片ID进行处理,把卡片提供方记录的临时卡片数据转换为常态卡片数据,防止提供方在清理长时间未删除的临时卡片时,把已经转换为常态卡片的临时卡片信息删除,导致卡片信息丢失。 242 243### 卡片数据交互 244 245当卡片应用需要更新数据时(如触发了定时更新或定点更新),卡片应用获取最新数据,并调用updateForm接口更新卡片。示例如下: 246 247```javascript 248onUpdate(formId) { 249 // 若卡片支持定时更新/定点更新/卡片使用方主动请求更新功能,则提供方需要重写该方法以支持数据更新 250 console.log('FormAbility onUpdate'); 251 let obj = { 252 "title": "titleOnUpdate", 253 "detail": "detailOnUpdate" 254 }; 255 let formData = formBindingData.createFormBindingData(obj); 256 // 调用updateForm接口去更新对应的卡片,仅更新入参中携带的数据信息,其他信息保持不变 257 formProvider.updateForm(formId, formData).catch((error) => { 258 console.log('FormAbility updateForm, error:' + JSON.stringify(error)); 259 }); 260} 261``` 262 263### 开发卡片页面 264 265开发者可以使用hml+css+json开发JS卡片页面: 266 267> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:** 268> 当前仅支持JS扩展的类Web开发范式来实现卡片的UI页面。 269 270 - hml: 271 ```html 272 <div class="container"> 273 <stack> 274 <div class="container-img"> 275 <image src="/common/widget.png" class="bg-img"></image> 276 </div> 277 <div class="container-inner"> 278 <text class="title">{{title}}</text> 279 <text class="detail_text" onclick="routerEvent">{{detail}}</text> 280 </div> 281 </stack> 282 </div> 283 ``` 284 285 - css: 286 287 ```css 288.container { 289 flex-direction: column; 290 justify-content: center; 291 align-items: center; 292} 293 294.bg-img { 295 flex-shrink: 0; 296 height: 100%; 297} 298 299.container-inner { 300 flex-direction: column; 301 justify-content: flex-end; 302 align-items: flex-start; 303 height: 100%; 304 width: 100%; 305 padding: 12px; 306} 307 308.title { 309 font-size: 19px; 310 font-weight: bold; 311 color: white; 312 text-overflow: ellipsis; 313 max-lines: 1; 314} 315 316.detail_text { 317 font-size: 16px; 318 color: white; 319 opacity: 0.66; 320 text-overflow: ellipsis; 321 max-lines: 1; 322 margin-top: 6px; 323} 324 ``` 325 326 - json: 327 ```json 328 { 329 "data": { 330 "title": "TitleDefault", 331 "detail": "TextDefault" 332 }, 333 "actions": { 334 "routerEvent": { 335 "action": "router", 336 "abilityName": "com.example.entry.MainAbility", 337 "params": { 338 "message": "add detail" 339 } 340 } 341 } 342 } 343 ``` 344 345最终可以得到如下卡片: 346 347![fa-form-example](figures/fa-form-example.png) 348 349### 开发卡片事件 350 351卡片支持为组件设置action,包括router事件和message事件,其中router事件用于Ability跳转,message事件用于卡片开发人员自定义点击事件。关键步骤说明如下: 352 3531. 在hml中为组件设置onclick属性,其值对应到json文件的actions字段中。 3542. 若设置router事件,则 355 - action属性值为"router"; 356 - abilityName为跳转目标的Ability名,如目前DevEco创建的FA模型的MainAbility默认名为com.example.entry.MainAbility; 357 - params为跳转目标Ability的自定义参数,可以按需填写。其值可以在目标Ability启动时的want中的parameters里获取。如FA模型MainAbility的onCreate生命周期里可以通过featureAbility.getWant()获取到want,然后在其parameters字段下获取到配置的参数; 3583. 若设置message事件,则 359 - action属性值为"message"; 360 - params为message事件的用户自定义参数,可以按需填写。其值可以在卡片生命周期函数onEvent中的message里获取; 361 362示例如下: 363 364 - hml: 365 ```html 366 <div class="container"> 367 <stack> 368 <div class="container-img"> 369 <image src="/common/widget.png" class="bg-img"></image> 370 </div> 371 <div class="container-inner"> 372 <text class="title" onclick="routerEvent">{{title}}</text> 373 <text class="detail_text" onclick="messageEvent">{{detail}}</text> 374 </div> 375 </stack> 376 </div> 377 ``` 378 379 - json: 380 ```json 381 { 382 "data": { 383 "title": "TitleDefault", 384 "detail": "TextDefault" 385 }, 386 "actions": { 387 "routerEvent": { 388 "action": "router", 389 "abilityName": "com.example.entry.MainAbility", 390 "params": { 391 "message": "add detail" 392 } 393 }, 394 "messageEvent": { 395 "action": "message", 396 "params": { 397 "message": "add detail" 398 } 399 } 400 } 401 } 402 ```