1# 桌面卡片进入案例 2 3### 介绍 4 5桌面卡片是比较常见的功能,本案例详细列举了卡片开发的大部分功能,如使用postCardAction接口快速拉起卡片提供方应用的指定UIAbility,通过message事件刷新卡片内容等,为开发者提供了卡片功能的展示。 6 7### 效果预览 8 9| 卡片 | 案例| 10|--------|-----------| 11| <img src="screenshots/devices/Widget.jpg" width="270" /> | <img src="screenshots/devices/Case.jpg" width="270" />| 12 13### 使用说明 14 151. 长按应用,添加卡片到桌面。 162. 卡片内可滑动选择案例,点击可进入案例详情。 17 18### 实现思路 19 201. 新建卡片 212. 配置formconfig 223. 编写卡片UI代码 234. 触发刷新事件 245. 触发点击事件 25 26### 实现步骤 27 28本例涉及的关键特性和实现方案如下: 29 301. 新建卡片。右键点击entry目录,选择新建->Service Widget->Dynamic Widget,其中Dynamic Widget为动态卡片,Static Widget为静态卡片。此时会生成几个文件:配置文件[```form_config.json```](entry/src/main/resources/base/profile/form_config.json);卡片Ability[```EntryFormAbility.ets```](entry/src/main/ets/entryformability/EntryFormAbility.ets);卡片组件[```CasesSwiperCard.ets```](entry/src/main/ets/widget/pages/CasesSwiperCard.ets)。 31 322. 新建卡片后,根据需要(如卡片大小,刷新时间,动态静态卡片设置)配置```form_config.json```。 33 34```json5 35{ 36 "forms": [ 37 { 38 "name": "widget", // 卡片的名称。 39 "displayName": "$string:widget_display_name", // 卡片的显示名称。 40 "description": "$string:widget_desc", // 卡片的描述。 41 "src": "./ets/widget/pages/CasesSwiperCard.ets", // 卡片对应的UI代码的完整路径。 42 "uiSyntax": "arkts", // 卡片的类型 43 "window": { // 用于定义与显示窗口相关的配置。 44 "designWidth": 720, 45 "autoDesignWidth": true 46 }, 47 "colorMode": "auto", // 卡片的主题样式。 48 "isDynamic": true, // 卡片是否为动态卡片。 49 "isDefault": true, // 卡片是否为默认卡片。 50 "updateEnabled": true, // 卡片是否支持周期性刷新。 51 "scheduledUpdateTime": "10:30", // 卡片的定点刷新的时刻。 52 "updateDuration": 1, // 卡片定时刷新的更新周期,单位为30分钟,取值为自然数。 53 "defaultDimension": "6*4", // 卡片的默认外观规格。 54 "supportDimensions": [ // 卡片支持的外观规格,取值范围。 55 "6*4" 56 ] 57 } 58 ] 59} 60``` 61 623. 编写卡片UI代码。在主文件```CasesSwiperCard.ets```中添加UI组件,需要注意的是:ArkTS卡片存在较多约束(如不支持导入共享包),较多逻辑不可在卡片中使用,在使用时需要根据文档进行操作。 63 644. 编写跳转事件:当应用未被拉起时,点击某个卡片时跳转到具体的案例页面。在```EntryAbility.ets```中补充逻辑:onCreate生命周期内获取want.parameters.params判断卡片内容的跳转。 65 66```typescript 67// EntryAbility.ets 68export default class EntryAbility extends UIAbility { 69 onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void { 70 // ... 71 // 桌面卡片判断跳转内容 72 if (want?.parameters?.params) { 73 // want.parameters.params 对应 postCardAction() 中 params 内容 74 let params: Record<string, Object> = JSON.parse(want.parameters.params as string); 75 this.selectPage = params.targetPage as string; 76 } 77 // ... 78 } 79} 80``` 81 825. 编写跳转事件:当应用在后台时,点击某个卡片时跳转到具体的案例页面。可从onNewWant生命周期获取want.parameters.params判断卡片内容的跳转。 83 84```typescript 85// EntryAbility.ets 86export default class EntryAbility extends UIAbility { 87 // 如果UIAbility已在后台运行,在收到Router事件后会触发onNewWant生命周期回调 88 onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void { 89 if (want.parameters!['ability.params.stream'] !== undefined) { 90 AppStorage.setOrCreate('imageUri', want.parameters!["ability.params.stream"].toString()); 91 return; 92 } 93 if (want?.parameters?.params) { 94 // want.parameters.params 对应 postCardAction() 中 params 内容 95 let params: Record<string, Object> = JSON.parse(want.parameters.params as string); 96 this.selectPage = params.targetPage as string; 97 if (this.currentWindowStage !== null) { 98 // 存在窗口时点击卡片后进行页面跳转 99 this.currentWindowStage.loadContent(`pages/${this.selectPage}`, (err) => { 100 if (err.code) { 101 hilog.error(0x0000, 'testTagSelectPage', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? ''); 102 return; 103 } 104 hilog.info(0x0000, 'testTagSelectPage', 'Succeeded in loading the content.'); 105 }); 106 this.selectPage = ''; 107 } 108 } else { 109 this.selectPage = ''; 110 } 111 } 112} 113``` 114 1156. 具体跳转逻辑编写。在onWindowStageCreate生命周期内进行具体的跳转逻辑。 116 117```typescript 118// EntryAbility.ets 119export default class EntryAbility extends UIAbility { 120 onWindowStageCreate(windowStage: window.WindowStage): void { 121 // Main window is created, set main page for this ability 122 hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate'); 123 // 判断是否存在窗口可进行页面跳转 124 if (this.currentWindowStage === null) { 125 this.currentWindowStage = windowStage; 126 } 127 // 点击卡片后进行页面跳转 128 if (this.selectPage) { 129 windowStage.loadContent(`pages/${this.selectPage}`, (err) => { 130 if (err.code) { 131 hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? ''); 132 return; 133 } 134 hilog.info(0x0000, 'testTag', 'Succeeded in loading the content.'); 135 }); 136 } else { 137 windowStage.loadContent('pages/Index', (err) => { 138 if (err.code) { 139 hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? ''); 140 return; 141 } 142 hilog.info(0x0000, 'testTag', 'Succeeded in loading the content.'); 143 }); 144 } 145 } 146} 147``` 148 1497. 编写刷新事件:当定时更新或定点更新触发时,需要更新卡片内容。onUpdateForm生命周期发生在定时更新/定点更新/卡片使用方主动请求更新时,在方法内增加获取案例数据的功能。 150 151```typescript 152// EntryFormAbility.ets 153export default class EntryFormAbility extends FormExtensionAbility { 154 // 获取数据并利用formProvider.updateForm更新到卡片 155 async getData(formId: string) { 156 let detail: CASES[] = []; 157 let message = ''; 158 try { 159 let value = await this.context.resourceManager.getRawFileContent('CasesData.txt'); 160 console.log('decodeToStringdecodeToString') 161 message = util.TextDecoder.create().decodeToString(value); 162 detail = CaseCardUtils.formatData(message); 163 hilog.info(DOMAIN_NUMBER, TAG, '[EntryFormAbility] onFormEvent' + 'result:' + message); 164 hilog.info(DOMAIN_NUMBER, TAG, '[EntryFormAbility] getData' + 'detail:' + JSON.stringify(detail)); 165 class FormDataClass { 166 public detail: CASES[] = detail; 167 } 168 let formData = new FormDataClass(); 169 let formInfo = formBindingData.createFormBindingData(formData); 170 await formProvider.updateForm(formId, formInfo); 171 } catch (error) { 172 hilog.error(DOMAIN_NUMBER, TAG, `FormAbility updateForm failed: ${JSON.stringify(error)}`); 173 } 174 } 175 176 async onUpdateForm(formId: string): Promise<void> { 177 // 若卡片支持定时更新/定点更新/卡片使用方主动请求更新功能,则提供方需要重写该方法以支持数据更新 178 hilog.info(DOMAIN_NUMBER, TAG, '[EntryFormAbility] onUpdateForm'); 179 this.getData(formId); 180 } 181} 182``` 183 1848. 编写刷新事件:参数传到卡片组件内,组件接收参数。处理```CasesSwiperCard.ets```卡片内逻辑。卡片页面中使用LocalStorageProp装饰需要刷新的卡片数据。 185 186```ts 187let casesCardInfo = new LocalStorage(); 188@Entry(casesCardInfo) 189@Component 190struct Widget_DynamicCard { 191 @LocalStorageProp('detail') detail: CASES[] = []; // 卡片对象集合 192 private swiperController: SwiperController = new SwiperController(); 193 194 build() { 195 // ... 196 } 197} 198``` 199 200### 工程结构&模块类型 201 202``` 203CardInteractionCase // har 204|---entryability 205| |---EntryAbility.ets // EntryAbility 206|---entryformability 207| |---EntryFormAbility.ets //EntryFormAbility 208|---pages 209| |---DealStrideSolution.ets // 解决相机预览花屏案例页面 210| |---EncapsulationDialog.ets // 弹窗封装案例页面 211| |---Index.ets // 首页页面 212| |---ShareButton.ets // 分享二维码按钮案例页面 213| |---SmartFill.ets // 智能填充案例页面 214| |---VideoTrimmer.ets // 视频下载保存及剪辑压缩上传案例页面 215|---widget 216| |---pages 217| |---CasesSwiperCard.ets // 卡片页面 218| |---utils 219| |---CaseCardUtils.ets // 卡片公共方法页面 220``` 221 222### 参考资料 223 224[创建一个ArkTS卡片](https://docs.openharmony.cn/pages/v5.0/zh-cn/application-dev/form/arkts-ui-widget-creation.md) 225 226[配置卡片的配置文件](https://docs.openharmony.cn/pages/v5.0/zh-cn/application-dev/form/arkts-ui-widget-configuration.md) 227 228[使用router事件跳转到指定UIAbility](https://docs.openharmony.cn/pages/v5.0/zh-cn/application-dev/form/arkts-ui-widget-event-router.md) 229 230[通过message事件刷新卡片内容](https://docs.openharmony.cn/pages/v5.0/zh-cn/application-dev/form/arkts-ui-widget-event-formextensionability.md) 231 232### 依赖 233 234不涉及。 235 236### 约束与限制 237 2381.本示例仅支持标准系统上运行。 239 2402.本示例已适配API version 12版本SDK。 241 2423.本示例需要使用DevEco Studio 5.0.0 Release及以上版本才可编译运行。 243 244### 下载 245 246如需单独下载本工程,执行如下命令: 247 248``` 249git init 250git config core.sparsecheckout true 251echo code/SuperFeature/Widget/ArkTSCard/CardInteractionCase/ > .git/info/sparse-checkout 252git remote add origin https://gitee.com/openharmony/applications_app_samples.git 253git pull origin master 254``` 255