• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.MainAbility357   - 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   ```