开天辟地 HarmonyOS(鸿蒙) - 卡片: 静态卡片
开天辟地 HarmonyOS(鸿蒙) - 卡片: 静态卡片
示例如下:
pages\widget\StaticWidgetDemo.ets
/*
* 静态卡片
* 长按 app 的图标,则可以添加卡片
* 如需在应用内添加卡片,请参见 AddFormMenuItemDemo.ets 中的相关说明
*
* 静态卡片仅支持 UI 组件布局
* 动态卡片除了支持 UI 组件布局外,还支持组件的通用事件方法以及自定义动画效果,可以用于需要交互的场景
*
*
* 1、需要在 src/main/module.json5 中添加卡片类型的 extension ability,类似如下
* {
* "module": {
* "extensionAbilities": [
* {
* "name": "com.webabcd.harmonydemo.EntryFormAbility", // 自定义标识
* "srcEntry": "./ets/entryformability/EntryFormAbility.ets", // 卡片对应的 extension ability 的代码地址
* "label": "$string:EntryAbility_label",
* "description": "$string:EntryAbility_desc",
* "type": "form", // 当前的 ExtensionAbility 的类型为卡片
* "metadata": [
* {
* "name": "ohos.extension.form", // 卡片的 metadata 的 name 必须是 ohos.extension.form
* "resource": "$profile:form_config" // 卡片的相关配置,详见 src/main/resources/profile/form_config.json 中的配置
* }
* ]
* }
* ],
* }
* }
*
* 2、卡片的相关配置,在 src/main/resources/profile/form_config.json 文件中,说明如下
* "forms": [
* {
* "name": "static widget", // 卡片的自定义标识
* "displayName": "$string:staticWidget_display_name", // 添加卡片时,在卡片的预览中显示的名称
* "description": "$string:staticWidget_desc", // 添加卡片时,在卡片的预览中显示的描述
* "src": "./ets/widget/pages/StaticWidgetCard.ets", // 卡片的具体实现的代码地址
* "uiSyntax": "arkts", // 通过 ArkTS 实现卡片
* "window": {
* "designWidth": 720, // 设计的基准宽度,与 lpx 相关,缺省值为 720
* "autoDesignWidth": true // 是否根据屏幕的像素密度自动计算 designWidth 的值,此值配置为 true 时则会忽略 designWidth 配置的值,缺省值为 false
* },
* "colorMode": "auto", // 卡片的深色浅色模式(auto, dark, light)
* "isDynamic": false, // 是否是动态卡片
* "isDefault": true, // 是否是默认卡片,每一个卡片类型的 extension ability 只能有一个默认卡片
* "updateEnabled": true, // 是否允许刷新
* "scheduledUpdateTime": "10:30", // 定点刷新(仅 updateDuration 配置为 0 时 scheduledUpdateTime 才会生效)
* "updateDuration": 1, // 周期刷新(单位为 30 分钟,比如配置为 2 则为每 60 分钟刷新一次,配置为 0 则禁用 updateDuration 刷新)
* "supportDimensions": [ // 卡片支持的外观规格(1*2, 2*2, 2*4, 4*4, 6*4)
* "2*2",
* "4*4"
* ],
* "defaultDimension": "2*2" // 卡片默认的外观规格
* }
* ]
*
* 3、卡片对应的 extension ability 的代码详见 /entryformability/EntryFormAbility.ets 中的说明
*
* 4、卡片的具体实现详见 /widget/pages/StaticWidgetCard.ets 中的说明
*
* 注:以上是开发时手动创建卡片的方式,如果要通过 DevEco Studio 自动创建卡片,则先选中指定的 hap 模块,然后在 File -> New -> Service Widget 中创建即可
*/
import { TitleBar } from '../TitleBar';
@Entry
@Component
struct StaticWidgetDemo {
@State message: string = "静态卡片,具体实现请参见:\n" +
"src/main/module.json5\n" +
"src/main/resources/profile/form_config.json\n" +
"/entryformability/EntryFormAbility.ets\n" +
"/widget/pages/StaticWidgetCard.ets"
build() {
Column({space:10}) {
TitleBar()
Text(this.message)
}
}
}
\entry\src\main\module.json5
{
"module": {
"name": "entry", // 当前 module 的名称
"type": "entry", // 当前 module 的类型
"srcEntry": "./ets/MyAbilityStage.ets", // 当前 module 的对应的 AbilityStage 的代码的地址
"appStartup": "$profile:startup_config", // 启动任务的配置文件
"description": "$string:module_desc", // 当前 module 的描述
"mainElement": "com.webabcd.harmonydemo.EntryAbility", // 当前 module 的入口 ability 的名称(ability 必须是 exported 为 true 的)
"deviceTypes": [
"phone",
"tablet",
"2in1"
],
"deliveryWithInstall": true,
"installationFree": false,
"pages": "$profile:main_pages", // 用于描述页面的信息(参见 /entry/src/main/resources/base/profile/main_pages.json)
"querySchemes": [ // 当前应用可以通过 canOpenLink() 判断当前设备中是否存在支持指定协议(这个协议必须在 querySchemes 中配置,最多 50 个)的应用
"webabcd"
],
"abilities": [
{
"name": "com.webabcd.harmonydemo.EntryAbility", // 当前 ability 的名称(自定义标识)
"srcEntry": "./ets/entryability/EntryAbility.ets", // 当前 ability 的代码的地址
"description": "$string:EntryAbility_desc", // 描述
"icon": "$media:layered_image", // app 的图标(需要配置 entity.system.home, action.system.home),如果不指定此字段的话则 app 的图标会使用 AppScope/app.json5 中的 icon
"label": "$string:EntryAbility_label", // app 的标题(需要配置 entity.system.home, action.system.home),如果不指定此字段的话则 app 的标题会使用 AppScope/app.json5 中的 label
"startWindowIcon": "$media:startIcon", // 启动屏上显示的图标
"startWindowBackground": "$color:start_window_background", // 启动屏的背景
"exported": true, // 用于标识当前 ability 是否可以被其他应用调用
"orientation": "portrait", // 屏幕方向
"preferMultiWindowOrientation": "landscape_auto", // 悬浮窗方向
"skills": [ { "entities": [ "entity.system.home" ], "actions": [ "action.system.home" ] } ], // 这个设置说明当前 ability 是入口 ability(主:有了这个配置则 module 的 mainElement 标签将失效)
"backgroundModes": [ // 长时任务的类型
"dataTransfer", // 数据上传下载
// "audioPlayback", // 音频、视频播放
// "audioRecording", // 录音、录屏
// "location", // 定位
// "bluetoothInteraction", // 蓝牙传输
],
"removeMissionAfterTerminate": true // 当调用 terminateSelf() 杀死当前 UIAbility 时,是否需要将其从在最近任务列表中删除(默认值为 false)
},
{
"name": "com.webabcd.harmonydemo.EntryAbility2",
"srcEntry": "./ets/entryability/EntryAbility2.ets",
"icon": "$media:layered_image",
"label": "$string:EntryAbility_label",
"startWindowIcon": "$media:startIcon",
"startWindowBackground": "$color:start_window_background"
},
{
"name": "com.webabcd.harmonydemo.EntryAbility_singleton",
"srcEntry": "./ets/entryability/EntryAbility_singleton.ets",
"icon": "$media:layered_image",
"label": "$string:EntryAbility_label",
"startWindowIcon": "$media:startIcon",
"startWindowBackground": "$color:start_window_background",
"launchType": "singleton" // 指定当前 ability 的启动方式为 singleton 方式(详见 /basic/LaunchTypeDemo.ets 中的相关说明)
},
{
"name": "com.webabcd.harmonydemo.EntryAbility_multiton",
"srcEntry": "./ets/entryability/EntryAbility_multiton.ets",
"icon": "$media:layered_image",
"label": "$string:EntryAbility_label",
"startWindowIcon": "$media:startIcon",
"startWindowBackground": "$color:start_window_background",
"launchType": "multiton" // 指定当前 ability 的启动方式为 multiton 方式(详见 /basic/LaunchTypeDemo.ets 中的相关说明)
},
{
"name": "com.webabcd.harmonydemo.EntryAbility_specified",
"srcEntry": "./ets/entryability/EntryAbility_specified.ets",
"icon": "$media:layered_image",
"label": "$string:EntryAbility_label",
"startWindowIcon": "$media:startIcon",
"startWindowBackground": "$color:start_window_background",
"launchType": "specified" // 指定当前 ability 的启动方式为 specified 方式(详见 /basic/LaunchTypeDemo.ets 中的相关说明)
}
],
"extensionAbilities": [
{
"name": "EntryBackupAbility",
"srcEntry": "./ets/entrybackupability/EntryBackupAbility.ets",
"type": "backup",
"exported": false,
"metadata": [
{
"name": "ohos.extension.backup",
"resource": "$profile:backup_config"
}
]
},
{
"name": "com.webabcd.harmonydemo.MyWorkSchedulerExtensionAbility", // 自定义标识
"srcEntry": "./ets/pages/background/MyWorkSchedulerExtensionAbility.ets", // 延迟任务对应的代码的地址
"type": "workScheduler" // 当前的 ExtensionAbility 的类型为延迟任务
},
{
"name": "com.webabcd.harmonydemo.EntryFormAbility", // 自定义标识
"srcEntry": "./ets/entryformability/EntryFormAbility.ets", // 卡片对应的 extension ability 的代码地址
"label": "$string:EntryAbility_label",
"description": "$string:EntryAbility_desc",
"type": "form", // 当前的 ExtensionAbility 的类型为卡片
"metadata": [
{
"name": "ohos.extension.form", // 卡片的 metadata 的 name 必须是 ohos.extension.form
"resource": "$profile:form_config" // 卡片的相关配置,详见 src/main/resources/profile/form_config.json 中的配置
}
]
}
],
"routerMap": "$profile:route_map", // 指定路由表,详见 src/main/resources/profile/route_map.json 中的配置
"requestPermissions":[
{
"name": "ohos.permission.INTERNET", // 请求 Internet 网络的权限
"reason": "$string:hello_webabcd", // 申请此权限的原因
"usedScene": {
"abilities": [ ], // 需要使用此权限的 ability 的名称,配置为空则所有 ability 均可以使用此权限
"when":"always" // inuse(使用时允许使用此权限),always(始终允许使用此权限)
}
},
{
"name": "ohos.permission.KEEP_BACKGROUND_RUNNING", // 请求长时任务的权限
"reason": "$string:hello_webabcd", // 申请此权限的原因
"usedScene": {
"abilities": [ ], // 需要使用此权限的 ability 的名称,配置为空则所有 ability 均可以使用此权限
"when": "always" // inuse(使用时允许使用此权限),always(始终允许使用此权限)
}
},
{
"name": "ohos.permission.PUBLISH_AGENT_REMINDER", // 请求提醒任务的权限
"reason": "$string:hello_webabcd", // 申请此权限的原因
"usedScene": {
"abilities": [ ], // 需要使用此权限的 ability 的名称,配置为空则所有 ability 均可以使用此权限
"when": "always" // inuse(使用时允许使用此权限),always(始终允许使用此权限)
}
}
]
}
}
\entry\src\main\resources\base\profile\form_config.json
{
"forms": [
{
"name": "static widget",
"displayName": "$string:staticWidget_display_name",
"description": "$string:staticWidget_desc",
"src": "./ets/widget/pages/StaticWidgetCard.ets",
"uiSyntax": "arkts",
"window": {
"designWidth": 720,
"autoDesignWidth": true
},
"colorMode": "auto",
"isDynamic": false,
"isDefault": true,
"updateEnabled": true,
"scheduledUpdateTime": "09:30",
"updateDuration": 1,
"supportDimensions": [
"1*2",
"2*2",
"2*4",
"4*4",
"6*4"
],
"defaultDimension": "2*2"
},
{
"name": "dynamic widget",
"displayName": "$string:dynamicWidget_display_name",
"description": "$string:dynamicWidget_desc",
"src": "./ets/widget/pages/DynamicWidgetCard.ets",
"uiSyntax": "arkts",
"window": {
"designWidth": 720,
"autoDesignWidth": true
},
"colorMode": "auto",
"isDynamic": true,
"isDefault": false,
"updateEnabled": true,
"scheduledUpdateTime": "09:30",
"updateDuration": 1,
"supportDimensions": [
"4*4"
],
"defaultDimension": "4*4"
},
{
"name": "FormLink",
"displayName": "$string:formLink_display_name",
"description": "$string:formLink_desc",
"src": "./ets/widget/pages/FormLinkCard.ets",
"uiSyntax": "arkts",
"window": {
"designWidth": 720,
"autoDesignWidth": true
},
"colorMode": "auto",
"isDynamic": false,
"isDefault": false,
"updateEnabled": true,
"scheduledUpdateTime": "09:30",
"updateDuration": 1,
"supportDimensions": [
"4*4"
],
"defaultDimension": "4*4"
}
]
}
\entry\src\main\ets\entryformability\EntryFormAbility.ets
/*
* 卡片对应的 extension ability
* 用于管理卡片的生命周期,以及和卡片做数据交互
*/
import { formBindingData, FormExtensionAbility, formProvider } from '@kit.FormKit';
import { Want } from '@kit.AbilityKit';
import { Helper } from '../utils/Helper';
import { MyLog } from '../utils/MyLog';
import { BusinessError } from '@kit.BasicServicesKit';
import { fileIo as fs } from '@kit.CoreFileKit';
export default class EntryFormAbility extends FormExtensionAbility {
// 将指定的资源文件复制到指定的沙箱目录
copyFile(src: Resource, dst: string) {
let resourceManager = this.context.getApplicationContext().resourceManager
let buffer = resourceManager.getMediaContentSync(src.id).buffer
let outputStream = fs.createStreamSync(dst, 'w+');
let writeLength = outputStream.writeSync(buffer, {
offset: 0,
length: buffer.byteLength
})
outputStream.closeSync();
}
// 卡片创建时的回调
onAddForm(want: Want) {
// 通过 want.parameters 可以获取卡片的规格,以及宽和高等
MyLog.d(`onAddForm ${JSON.stringify(want.parameters)}`)
// FormExtensionContext - FormExtensionAbility 的上下文(在 FormExtensionAbility 内,可以通过 this.context 获取 FormExtensionContext 对象)
let context = this.context
let abilityName = context.extensionAbilityInfo.name
MyLog.d(`extension ability name: ${abilityName}`)
// 将指定的资源文件复制到指定的沙箱目录
// 如果需要卡片显示一个网络图片,则可以先将图片下载到沙箱目录(注:卡片每次活过来后,最多在后台存在 5 秒),然后再参照本例后续的方法
let imagePath = this.context.getApplicationContext().filesDir + '/icon.png'
this.copyFile($r('app.media.app_icon'), imagePath)
// 创建一个保存多张图片的字典表,用于在卡片中显示图片
// key 代表图片的标识
// value 代表图片的文件描述符,打开文件到内存后,把文件描述符传给卡片,然后卡片再根据文件描述符显示内存中的图片
let formImages: Record<string, number> = {};
let file = fs.openSync(imagePath);
formImages['myImage_0'] = file.fd; // 注意:如果图片更新了,则这里需要指定一个和之前不同的 key 以便卡片可以显示更新后的图片
/*
* formBindingData.createFormBindingData() - 创建一个 FormBindingData 对象
* formBindingData.FormBindingData - 需要传递给卡片的数据
* 此对象中的字段的值,可以在卡片中通过 @LocalStorageProp 引用
*/
let formData: Record<string, string | Record<string, number>> = {
'formId': `${want.parameters!['ohos.extra.param.key.form_identity']}`, // 卡片 id
'dimension': `${want.parameters!['ohos.extra.param.key.form_dimension']}`, // 卡片的规格(1代表1*2, 2代表2*2, 3代表2*4, 4代表4*4, 7代表6*4)
'width': `${want.parameters!['ohos.extra.param.key.form_width']}`, // 卡片的宽
'height': `${want.parameters!['ohos.extra.param.key.form_height']}`, // 卡片的高
'content': Helper.getTimestampString(), // 卡片中可以通过 @LocalStorageProp('content') 引用此值
'myImage': 'myImage_0', // 指定在 formImages 中的指定 key 的图片,然后在卡片中可以通过 @LocalStorageProp('myImage') 引用此图片
'formImages': formImages // 保存多张图片的字典表
};
return formBindingData.createFormBindingData(formData);
}
// 刷新时会执行 onUpdateForm() 回调
// 当时间符合 scheduledUpdateTime 或 updateDuration 的条件时会触发刷新
// 当系统语言或深色浅色模式发生变化时也会触发刷新
// 卡片每次活过来后,最多在后台存在 5 秒
onUpdateForm(formId: string) {
MyLog.d(`onUpdateForm: ${formId}`)
/*
* formBindingData.createFormBindingData() - 创建一个 FormBindingData 对象
* formBindingData.FormBindingData - 需要传递给卡片的数据
* 此对象中的字段的值,可以在卡片中通过 @LocalStorageProp 引用
* formProvider.updateForm() - 更新指定的卡片
* formId - 卡片 id
* formBindingData - 为卡片绑定的 FormBindingData 对象
* formProvider.setFormNextRefreshTime() - 指定下一次刷新的时间(注:卡片每天最多刷新 50 次)
* formId - 卡片 id
* minute - 此时间后刷新(单位:分钟),最短为 5 分钟
*/
let formData: Record<string, string> = {
'content': Helper.getTimestampString() // 卡片中可以通过 @LocalStorageProp('content') 引用此值
};
let formInfo: formBindingData.FormBindingData = formBindingData.createFormBindingData(formData)
formProvider.updateForm(formId, formInfo)
try {
// 设置过 5 分钟后刷新卡片
formProvider.setFormNextRefreshTime(formId, 5, (err: BusinessError) => {
})
} catch (err) {
}
}
// 卡片通过 message 的方式传递数据时,会触发此回调
onFormEvent(formId: string, message: string) {
MyLog.d(`onFormEvent: ${formId}, ${message}`) // 此处的 message 参数就是 message 方式传递过来的数据
// 更新指定的卡片
let formData: Record<string, string> = {
'content': Helper.getTimestampString() // 卡片中可以通过 @LocalStorageProp('content') 引用此值
};
let formInfo: formBindingData.FormBindingData = formBindingData.createFormBindingData(formData)
formProvider.updateForm(formId, formInfo)
}
// 卡片销毁时的回调
onRemoveForm(formId: string) {
MyLog.d(`onRemoveForm: ${formId}`)
}
};
\entry\src\main\ets\widget\pages\StaticWidgetCard.ets
/*
* 卡片的具体实现(本例演示的是静态卡片)
*
* 静态卡片仅支持 UI 组件布局
* 动态卡片除了支持 UI 组件布局外,还支持组件的通用事件方法以及自定义动画效果,可以用于需要交互的场景
*
* 注:静态卡片不支持组件的通用事件方法以及自定义动画效果
*/
@Entry
@Component
struct StaticWidgetCard {
@LocalStorageProp('content') content: string = ''
@LocalStorageProp('myImage') myImage: string = ''
@LocalStorageProp('formId') cardId: string = ''
@LocalStorageProp('width') cardWidth: string = ''
@LocalStorageProp('height') cardHeight: string = ''
@LocalStorageProp('dimension') cardDimension: string = ''
private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(new RenderingContextSettings(true))
build() {
// 关于 FormLink 的详细说明请参见 FormLinkCard.ets
FormLink({
action: 'router', // 点击后跳转到指定的 ability
abilityName: 'com.webabcd.harmonydemo.EntryAbility', // 需要打开的 ability 的名称
}) {
Row() {
Column() {
// 可以直接显示本地资源中的图片
Image($r('app.media.son')).width(20).height(20)
// 如果是沙箱文件,则需要使用此方式显示
// memory:// 代表显示的是内存中的图片
// this.myImage 代表显示的图片的文件描述符(需要在 FormExtensionAbility 打开图片,然后把文件描述符传过来,请参见 /entryformability/EntryFormAbility.ets 中的说明)
// 注:如果需要显示一个网络图片,则先把图片下载后保存到沙箱中,然后再显示即可
Image('memory://' + this.myImage).width(20).height(20)
// 在卡片上更新一个文本(需要在 FormExtensionAbility 把更新后的文本传过来,请参见 /entryformability/EntryFormAbility.ets 中的说明)
Text(this.content)
// 卡片中也支持 Canvas 组件
Canvas(this.context).width(20).height(20).onReady(() => {
this.context.fillStyle = '#ff0000';
this.context.fillRect(0, 0, 20, 20);
})
// 卡片 id
Text(`formId: ${this.cardId}`)
// 卡片的规格(1代表1*2, 2代表2*2, 3代表2*4, 4代表4*4, 7代表6*4)
Text(`cardDimension: ${this.cardDimension}`)
// 卡片的宽
Text(`cardWidth: ${this.cardWidth}`)
// 卡片的高
Text(`cardHeight: ${this.cardHeight}`)
}
.width('100%')
}
.height('100%')
}
}
}