【本文正在参与“有奖征文| HarmonyOS征文大赛”活动】
服务卡片是HarmonyOS原子化服务的载体,是FA(Feature Ability)的一种界面展示形式,将FA(Feature Ability)重要信息或操作前置到卡片,以此达到服务直达、减少层级体验的目的。在服务中心可以轻松获取!随时分享!无需下载!无需安装!一步到位获取各种服务。
本文将讲解服务卡片的使用,至于创建服务卡片这些官方文档已写了无人能超越的服务卡片入门文档,不再多说,不了解可查看文档:如何开发服务卡片
服务卡片的种类有16种,若服务卡片不足16种,便是创建项目时Compatible API Version
版本选小了,改大点好啦。
接下来我们看看这16种服务卡片分别是什么吧!
模板名称 | 模板描述 | 开发语言 |
---|---|---|
Grid Pattern(宫格卡片模板) | 宫格卡片模板在大尺寸的卡片上特征较为明显,能够有规律进行布局排列。例如展示多排应用图标,每个热区独立可点击,或展示影视海报等信息,以凸显图片为主,描述文本为辅。 | Java、JS |
Image With Infomation(图文卡片模板) | 图文卡片模板主要在于展现图片和一定数量文本的搭配,在这种布局下,图片和文本属于同等重要的信息。在不同尺寸下,图片大小和文本数量会发生一定变化,用于凸显关键信息。 | Java、JS |
Immersive Pattern(沉浸布局卡片模板) | 图片内容是更能够吸引用户的展现形式,因此,沉浸式的布局能够拥有更好的代入感和展现形式。相比较图文和宫格类,这种布局在造型上的制约会更小,设计形式上的发挥空间更大,但在不同设备下的适配需要注意展示效果。 | Java、JS |
List Pattern(列表卡片模板) | 列表卡片模板是展示信息时的常用界面组件,通常会在列表的左侧或右侧带有图片或点缀元素。这类布局的优势在于可以集中的展示较多信息量,并遵循有序的排列。常用于新闻类、搜索类应用,方便用户获取关键的文本信息。 | Java、JS |
Circular Data(环形数据模板) | 环形数据卡片模板主要用于展示自定义内容数据,卡片主体由环形数据图和文本描述组成,用于凸显关键数据的所占比例。 | JS |
Immersive Data(沉浸式数据模板) | 此类型卡片是在沉浸式图片上呈现数据信息,可以使用不同的图标搭配信息进行呈现,强调使用场景与数据之间的关系,开发者可以发挥图文搭配的优势,创造出独特风格的卡片样式。 | JS |
Immersive Information(沉浸式图文模板) | 沉浸式卡片的装饰性较强,能够较好的提升卡片品质感并起到装饰桌面的作用,合理的去布局信息与背景图片之间的空间比例,可以提升用户的个性化使用体验。 | JS |
Multiple Contacts(多个联系人模板) | 多个联系人信息融合在一张卡片中,用户能够快捷的查找到最近通话的联系人。也可以通过赋予卡片编辑能力,为用户提供动态可自定义的联系人卡片。 | JS |
Multiple Functions(多功能模板) | 开发者可以定义此卡片不同热区位置的点击事件,可以执行某一指令或者不同功能界面的跳转。权衡多个功能之间重要程度,将较大的空间位置留给主要的信息,搭配图片使用,使卡片内容看起来更加丰富。 | JS |
Music Player(音乐播放器卡片模板) | 音乐播放器卡片模板主要用于在桌面展示一个音乐播放的控制界面,通过点击卡片上的对应功能按钮,能够实现对音乐播放的控制。 | JS |
Schedule(行程卡片模板) | 行程卡片模板布局主要用于在卡片上展示行程关键信息,并带有功能图标,可通过点击功能图标查看详细行程信息。 | JS |
Shortcuts(捷径卡片模板) | 捷径卡片模板布局主要用于在桌面展示多个快捷功能图标,在这种布局下,每个热区独立可点击,可快速进入相关功能。但请提供对用户有价值、有服务场景的功能,不要滥用卡片的入口位置。 | JS |
Social Call(通话卡片模板) | 通话卡片模板主要用于在桌面显示自定义的联系人图片和通话按钮,在这种场景下,可以直接点击卡片上的通话按钮进行快速呼叫。 | JS |
Standard Image(标准图文模板) | 标准图片类型的卡片使用场景较广,图片和文字信息类型展示基本都可以使用此卡片,但仍然是呈现图片为主,例如展示二维码信息、乘车路线图、卡片信息预览等。 | JS |
Standard List(标准列表模板) | 此卡片的优势在于强调标题信息,且有序排列,可以明确的呈现主副信息内容。列表类型的卡片需要克制的使用,避免整张卡片全是文字信息。 | JS |
Timer Progress(标准时间进度模板) | 此卡片主要突出时间数据,配合标题及正文对数据信息进行解释。可以使用不同的色彩来强调信息的重要性,突出核心的内容。 | JS |
为了让大家避免我所遇到的一些开发到一半发现功能无法使用Java完成的囧境,贴上官方的卡片选型指导,供大家选择正确的开发方式。
场景 | Java卡片 | JS卡片 |
---|---|---|
实时刷新 | Java使用ComponentProvider做实时刷新代价比较大 | JS可以做到端侧刷新,但是需要定制化组件 |
开发方式 | Java UI在卡片提供方需要同时对数据和组件进行处理,生成ComponentProvider远端渲染 | JS卡片在使用方加载渲染,提供方只要处理数据、组件和逻辑分离 |
组件支持 | Text、Image、DirectionalLayout、PositionLayout、DependentLayout | div、list、list-item、swiper、stack、image、text、span、progress、button(定制:chart 、clock、calendar) |
卡片动效 | 不支持 | 暂不开放 |
阴影模糊 | 不支持 | 支持 |
动态适应布局 | 不支持 | 支持 |
自定义卡片跳转页面 | 不支持 | 支持 |
选好类型了?项目建起来了?好,下一步,是时候创建卡片了!
新建项目后,选中src文件夹→点击鼠标右键→New→Service Widget
List Pattern是16种服务卡片中最基本的服务卡片之一,这里使用List Pattern进行讲解
这里 ↓ 有个BUG,Description输入框不能输入中文,在哪改成中文的Description后面会讲到
紧接着便会生成这些文件
点击运行,长按运行后安装的App,选择服务卡片。看,它是长这样的
上图中, entry_MainAbility
是指服务卡片的App名称,This is a service widget
是服务卡片的 Description,这里两个参数可在 config.json
文件夹进行修改。
打开config.json
,找到创建服务卡片时起的名字,description
值变了,对服务卡片的描述也就变了
entry_MainAbility
改哪里呢?entry_MainAbility
改的是包含了entities
与actions
key对象的 label
。
按住ctrl+鼠标左键点击label
会出现三个文件,手机系统会根据手机系统当前在使用的语言选择使用哪一个文件里面的值,我用的系统是中文版,且不考虑开发英文版,因此只改最下面那个即可。
当你创建完一个服务卡片后,在layout文件夹下会根据你创建的布局尺寸生成对应尺寸的xml文件,我们就在这里开始 改-布-局-数-据
修改布局数据需要在创建服务卡片时生成的WidgetAbility
类上修改,在这个类上面的操作便是对服务卡片数据的操作,在官方文档 卡片开发说明 、Java卡片开发指导 有提到相关接口、方法
类名 | 接口名 | 描述 |
Ability | ||
ProviderFormInfo onCreateForm(Intent intent) | 卡片提供方接收创建卡片通知接口。 | |
void onUpdateForm(long formId) | 卡片提供方接收更新卡片通知接口。 | |
void onDeleteForm(long formId) | 卡片提供方接收删除卡片通知接口。 | |
void onTriggerFormEvent(long formId, String message) | 卡片提供方处理卡片事件接口(JS卡片使用)。 | |
boolean updateForm(long formId, ComponentProvider component) | 卡片提供方主动更新卡片(Java卡片使用)。 | |
boolean updateForm(long formId, FormBindingData formBindingData) | 卡片提供方主动更新卡片(JS卡片使用),仅更新formBindingData中携带的信息,卡片中其余信息保持不变。 | |
void onCastTempForm(long formId) | 卡片提供方接收临时卡片转常态卡片通知。 | |
void onEventNotify(Map |
卡片提供方接收到事件通知,其中Ability.FORM_VISIBLE表示卡片可见通知,Ability.FORM_INVISIBLE表示卡片不可见通知。 | |
ProviderFormInfo | ProviderFormInfo(int resId, Context context) | Java卡片返回对象构造函数。 |
ProviderFormInfo() | JS卡片返回对象构造函数。 | |
void mergeActions(ComponentProvider componentProviderActions) | 在提供方侧调用该接口,将开发者在ComponentProvider中设置的actions配置数据合并到当前对象中。 | |
void setJsBindingData(FormBindingData data) | 设置JS卡片的内容信息(JS卡片使用)。 |
在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按需获取。
看了官方文档的提示,接下来便是我大显身手的时候了
打开WidgetAbility
类(创建服务卡片时选择Select Ability / New Ability
自动生成),在其onCreateForm
方法编写以下代码,即可实现修改服务卡片界面数据
@Override
protected ProviderFormInfo onCreateForm(Intent intent) {
// 获得卡片ID
long formId = intent.getLongParam(AbilitySlice.PARAM_FORM_IDENTITY_KEY, INVALID_FORM_ID);
// 获取卡片名称
String formName = intent.getStringParam(AbilitySlice.PARAM_FORM_NAME_KEY);
// 获取卡片外观规格
int dimension = intent.getIntParam(AbilitySlice.PARAM_FORM_DIMENSION_KEY, DEFAULT_DIMENSION_2X2);
// 根据卡片的名称以及外观规格获取对应的xml布局并构造卡片对象
ProviderFormInfo formInfo = new ProviderFormInfo(ResourceTable.Layout_form_list_pattern_widget_2_2, this);
// 设置卡片信息
ComponentProvider componentProvider = new ComponentProvider();
componentProvider.setText(ResourceTable.Id_title, "这是标题啊~");
componentProvider.setText(ResourceTable.Id_title1, "这是item标题1~");
componentProvider.setText(ResourceTable.Id_title2, "这是item标题2~");
componentProvider.setText(ResourceTable.Id_content1, "这是content1~");
componentProvider.setText(ResourceTable.Id_content2, "这是content2~");
formInfo.mergeActions(componentProvider);
return formInfo;
}
看到这里,还没完!写到这里万字都不够,岂能乱起标题欺骗读者?这事我可干不来。
那么,接下来的功能是:点击服务卡片跳转到App一个界面上。
在包名下面创建继承自AbilitySlice
的WidgetSlice
类,重写onStart
方法添加super.setUIContent(ResourceTable.Layout_form_list_pattern_widget_2_2);
以绑定服务卡片布局,接着,打开WidgetAbility
类,在onStart
加上super.setMainRoute(WidgetSlice.class.getName());
,运行~
不过没关系,不符合我们的需求就改嘛
打开config.json界面,这次修改的是服务卡片的label
喔,可不能搞错了
找到服务卡片的label
,没错,就是改这个key里面的内容就可以了!
其中,"forms"
模块中的name
为卡片名,即在onCreateForm
中根据AbilitySlice.PARAM_FORM_NAME_KEY
可取到的值。
forms
对象的内部结构说明:
属性名称 | 子属性名称 | 含义 | 数据类型 | 是否可缺省 |
---|---|---|---|---|
name | - | 表示卡片的类名。字符串最大长度为127字节。 | 字符串 | 否 |
description | - | 表示卡片的描述。取值可以是描述性内容,也可以是对描述性内容的资源索引,以支持多语言。字符串最大长度为255字节。 | 字符串 | 可缺省,缺省为空。 |
isDefault | - | 表示该卡片是否为默认卡片,每个Ability有且只有一个默认卡片。true:默认卡片。false:非默认卡片。 | 布尔值 | 否 |
type | - | 表示卡片的类型。取值范围如下:Java:Java卡片。JS:JS卡片。 | 字符串 | 否 |
colorMode | - | 表示卡片的主题样式,取值范围如下:auto:自适应。dark:深色主题。light:浅色主题。 | 字符串 | 可缺省,缺省值为“auto”。 |
supportDimensions | - | 表示卡片支持的外观规格,取值范围:1*2:表示1行2列的二宫格。2*2:表示2行2列的四宫格。2*4:表示2行4列的八宫格。4*4:表示4行4列的十六宫格。 | 字符串数组 | 否 |
defaultDimension | - | 表示卡片的默认外观规格,取值必须在该卡片supportDimensions配置的列表中。 | 字符串 | 否 |
landscapeLayouts | - | 表示卡片外观规格对应的横向布局文件,与supportDimensions中的规格一一对应。仅当卡片类型为Java卡片时,需要配置该标签。 | 字符串数组 | 否 |
portraitLayouts | - | 表示卡片外观规格对应的竖向布局文件,与supportDimensions中的规格一一对应。仅当卡片类型为Java卡片时,需要配置该标签。 | 字符串数组 | 否 |
updateEnabled | - | 表示卡片是否支持周期性刷新,取值范围:true:表示支持周期性刷新,可以在定时刷新(updateDuration)和定点刷新(scheduledUpdateTime)两种方式任选其一,优先选择定时刷新。false:表示不支持周期性刷新。 | 布尔类型 | 否 |
scheduledUpdateTime | - | 表示卡片的定点刷新的时刻,采用24小时制,精确到分钟。 | 字符串 | 可缺省,缺省值为“0:0”。 |
updateDuration | - | 表示卡片定时刷新的更新周期,单位为30分钟,取值为自然数。当取值为0时,表示该参数不生效。当取值为正整数N时,表示刷新周期为30*N分钟。 | 数值 | 可缺省,缺省值为“0”。 |
formConfigAbility | - | 表示卡片的配置跳转链接,采用URI格式。 | 字符串 | 可缺省,缺省值为空。 |
jsComponentName | - | 表示JS卡片的Component名称。字符串最大长度为127字节。仅当卡片类型为JS卡片时,需要配置该标签。 | 字符串 | 否 |
metaData | - | 表示卡片的自定义信息,包含customizeData数组标签。 | 对象 | 可缺省,缺省值为空。 |
customizeData | - | 表示自定义的卡片信息。 | 对象数组 | 可缺省,缺省值为空。 |
customizeData | name | 表示数据项的键名称。字符串最大长度为255字节。 | 字符串 | 可缺省,缺省值为空。 |
customizeData | value | 表示数据项的值。字符串最大长度为255字节。 | 字符串 | 可缺省,缺省值为空。 |
界面的Text为什么会下沉对最底下?而不是展示在 标题在这~
稍微下面一点的位置呢?来,我们一起看它的xml布局是如何写的。
通过xml布局文件可知,布局的内容被包裹在DependentLayout
里面,DirectionalLayout
布局又使用了ohos:align_parent_bottom="true"
将自己牢牢的吸附在父布局的底部。
想要做出改变,那就得在这里修改,删除ohos:align_parent_bottom="true"
并在DirectionalLayout
添加以下代码即可:
ohos:align_top="$id:title"
ohos:top_margin="30vp"
点击运行,就是这样子咯
看了效果图,心细的你,也许就会发现一件事,文本的值与点击进来前的服务卡片值不对应
点击服务卡片,进入到的界面是绑定了当前xml布局、且继承了AbilitySlice
的类(即上面有提到的WidgetSlice
类),目前我们还没有在这里进行赋值,展示出来的界面又岂会有数据?
打开WidgetSlice
类,在onStart
加上以下代码:
((Text)findComponentById(ResourceTable.Id_title)).setText("服务卡片页面标题");
((Text)findComponentById(ResourceTable.Id_title1)).setText("服务卡片页面小标题1");
((Text)findComponentById(ResourceTable.Id_title2)).setText("服务卡片页面小标题2");
((Text)findComponentById(ResourceTable.Id_content1)).setText("服务卡片页面内容1");
((Text)findComponentById(ResourceTable.Id_content2)).setText("服务卡片页面内容2");
添加代码后,就变成这样啦
根据官方文档的提示去进行更新卡片数据时,出了些小问题,不知道这个onUpdate
方法它是何时会去执行,打开父类查看源码却发现还没下载,debug该方法更是无响应。
查看了官方代码示例的ClockFACardDemo
项目后得知是在onStart
时就已经启动了一个定时器,每一秒钟执行一次
既然不知道onUpdateForm
方法何时调用,那为何不像代码示例那样做一个定时器,然后更新数据呢?
onCreateForm
方法加上定时更新代码:
// 卡片更新定时器,每秒更新一次
Timer timer = new Timer();
final int[] i = {0};
timer.schedule(new TimerTask() {
@Override
public void run() {
onUpdate(formId, i[0]++);
}
},0, 1000L);
/**
* @param formId 设备id
* @param frequency 执行次数
*/
private void onUpdate(long formId,int frequency) {
ComponentProvider componentProvider = new ComponentProvider(ResourceTable.Layout_form_list_pattern_widget_2_2, this);
// 设置卡片的更新信息
componentProvider.setText(ResourceTable.Id_title, "数据变了"+frequency+"次");
componentProvider.setText(ResourceTable.Id_title1, "数据发生了变化~");
componentProvider.setText(ResourceTable.Id_title2, "数据发生了变化~");
componentProvider.setText(ResourceTable.Id_content1, "content1数据发生了变化~");
componentProvider.setText(ResourceTable.Id_content2, "content2数据发生了变化~");
try {
updateForm(formId, componentProvider);
} catch (FormException e) {
e.printStackTrace();
}
}
这样就大功告成啦!
config.json
配置自定义跳转URI的格式如下:ability://单个ability名字。本文代码已上传至:HarmonyOS服务卡片开发
【本文正在参与“有奖征文| HarmonyOS征文大赛”活动】