概述
服务卡片,简称“卡片”,是HarmonyOS系统一种呈现信息和交互操作的载体,在应用或元服务中提取关键信息和核心操作,以卡片的形式展示在桌面上,用户通过与服务卡片交互即可实现服务直达,减少交互层级,提升用户体验。
典型的服务卡片使用场景有音乐服务卡片。音乐服务卡片将音乐APP的重要信息与核心功能操作前置到卡片上,以卡片的形式呈现给用户,如音乐播控、歌单推荐、心动歌词等。用户可通过音乐服务卡片快速访问音乐APP的核心功能,无需打开完整的应用界面。
图1 音乐服务卡片场景效果图
本文将以音乐服务卡片场景为例,分别介绍音乐播控、歌单推荐、心动歌词三种服务卡片的实现,包括卡片设计和功能开发,以及开发中常见的一些问题。通过本案例,开发者可以更加深入的了解服务卡片与应用的交互和卡片的数据更新机制,快速高效的进行精美的服务卡片开发。
场景介绍
音乐播控
场景定义:
- 体验:用户无需打开应用,直接在卡片上就能控制基本的音乐播放功能操作,节省操作步骤,提升用户体验。
- 功能:用户可以通过服务卡片直接实现音乐播放,包括播放、暂停、上一首、下一首、收藏等操作。
歌单推荐
场景定义:
- 体验:用户在播放音乐时,可以直接在卡片上看到推荐的歌曲列表,点击即可切换和收藏,增强用户对音乐选择的便捷性和个性化体验。
- 功能:服务卡片可以根据用户的音乐偏好和当前播放内容,实时推荐相关歌曲或歌单。点击“我的收藏”跳转到应用收藏列表;点击“热门歌单”,跳转到歌单列表。
心动歌词
场景定义:
- 体验:以用户个性化互动为核心,通过歌词内容增强用户情感共鸣和音乐体验,通过技术创新深度链接用户情感,提供更加富有个性化的音乐体验。
- 功能:根据用户的听歌历史和偏好,推荐与其喜好相符的歌词卡片。
卡片设计
在设计方面,服务卡片需要遵循突出服务内容、明确划分有限的操作空间、展示必要的信息和图片以及轻量交互[体验原则],关于卡片内容设计遵循**[卡片内容设计]**规范,例如卡片沉浸式体验设计,包含图片和用色丰富的沉浸式卡片,背景色吸取内容图中的色彩。
图2 音乐服务卡片沉浸效果图
音乐播控、歌单推荐和心动歌词卡片设计效果如下所示:
实现方案
整体方案
音乐应用作为[卡片提供方],提供音乐服务卡片的内容显示、控件布局和卡片交互处理逻辑。桌面作为[卡片使用方],即卡片的宿主应用,控制卡片在桌面中展示的位置和内容。卡片框架管理卡片生命周期和刷新机制,负责卡片页面的渲染。
图3 音乐服务卡片运行机制
音乐应用包含[UIAbility](主进程)和[FormExtensionAbility](卡片进程)两个进程。其中,主进程包含音乐播控、收藏、热门歌单等功能模块;卡片进程是卡片业务逻辑模块,提供卡片创建、刷新、销毁等生命周期回调。如下图所示:
图4 音乐应用进程结构
开发者可以根据FormExtensionAbility生命周期回调,在对应回调方法中处理卡片数据持久化、卡片数据更新等操作,FormExtensionAbility生命周期回调时机和功能实现说明如下表所示:
生命周期 | 回调时机 |
---|---|
[onAddForm] | 长按APP图标/卡片后点击“服务卡片”拉起卡片视图后 |
[onUpdateForm] | 定时更新、定点更新、卡片使用方主动请求更新时执行回调 |
[onFormEvent] | 用户触发了卡片上的postCardAction或FormLink中的message事件 |
[onRemoveForm] | 长按卡片选择“移除”后 |
关键技术
在服务卡片开发中,我们会使用多种关键技术共同来实现不同类型的卡片,比如卡片规格的选择、卡片的沉浸式效果、卡片的更新以及数据持久化等。具体关键技术介绍如下:
-
卡片的选择
- 动态卡片支持通用事件能力和自定义动效能力,适用于有复杂业务逻辑和交互的场景,功能丰富但内存开销较大。
- 静态卡片支持UI组件和布局能力,不支持通用事件和自定义动效能力,卡片内容以静态图显示,可以通过[FormLink]组件跳转到指定的UIAbility,适用于展示类卡片(UI相对固定),功能简单但可以有效控制内存开销。
音乐播控卡片具有播放/暂停、上一曲/下一曲、收藏等功能,需较强的播控逻辑和交互处理,更适合选择动态卡片;歌单推荐和心动歌词仅需要页面跳转功能,业务逻辑和交互都比较简单,选择静态卡片更合适。
-
沉浸式卡片
沉浸式卡片设计能给用户带来更好的视觉体验,开发者可以使用[@ohos.effectKit]模块的ColorPicker的[getMainColor()]方法获取歌曲封面图像主色,作为卡片的背景色,使卡片和歌曲封面融为一体达到卡片沉浸效果。此外,在一些场景下,还可以给卡片添加背景图片,使用Image组件的blur属性或者@ohos.effectKit模块的[blur()]方法给图片做模糊化处理,来达到卡片沉浸效果。
-
卡片更新与数据交互
针对音乐服务卡片这种多类型多规格的卡片场景,推荐使用关系型数据库[relationalStore]进行卡片数据持久化存储。
应用主进程通过FormProvider的[updateForm()]方法实现卡片的主动刷新,例如更新歌曲信息、歌词信息等;在FormExtensionAbility的onUpdateForm()生命周期回调方法中实现卡片被动刷新逻辑,例如定时和定点刷新。
关于服务卡片的数据交互和更新机制。
卡片实现方案
音乐播控卡片
- 音乐播控卡片具有播放/暂停、上一曲/下一曲、收藏等功能,需较强的播控逻辑和交互处理,选择动态卡片实现。
- 音乐播控卡片和应用之间存在较多的交互,选择使用动态卡片来实现。动态卡片支持通用事件能力和自定义动效能力,适用于有复杂业务逻辑和交互的场景。
- 卡片与应用主进程之间通过call事件进行交互,比如播放、暂停、收藏等。
- 本示例音乐播放功能使用的是AVPlayer。为保证音乐能在后台播放或熄屏播放,需要接入[AVSession(媒体会话)]和[申请长时任务],避免播放被系统强制中断。
歌单推荐卡片
- 歌单推荐卡片没有复杂的业务逻辑和数据交互,选择使用静态卡片来实现,歌单推荐的交互动作主要是点击卡片对应组件之后,拉起应用主进程进行页面跳转。
- 页面跳转通过[FormLink]组件router类型事件来实现。
- 卡片创建的时候会加载网络图片。
心动歌词卡片
- 心动歌词卡片和应用主进程之间交互逻辑比较简单,仅有点击卡片跳转到应用播放页面的交互,所以选择静态卡片实现,通过FormLink组件实现点击卡片跳转。
- 心动歌词卡片内容主要是通过被动刷新的方式进行卡片数据刷新,在FormExtensionAbility的onUpdateForm()回调方法中,对卡片数据进行刷新,在卡片配置文件form_config中配置updateDuration参数实现定时刷新。
场景实现
音乐播控卡片
实现步骤
音乐播控卡片的主要实现步骤如下:
其中“实现音乐播控功能”需要通过卡片、数据库、媒体播放等多个模块之间的数据交互来实现,音乐播控功能实现时序图如下:
图5 卡片音乐播控时序图
音乐播控卡片的详细开发流程如下:
-
开发卡片UI布局
图6 音乐播控卡片效果图
分别创建2x2和2x4两个规格的动态卡片。对于比较复杂的布局,优先考虑使用相对布局 [RelativeContainer]来减少性能开销。由于2x4卡片包含了2x2卡片的功能,下面将以2x4卡片为例介绍音乐播控卡片实现。
在卡片布局文件中定义卡片所需要的变量信息,使用[@LocalStorageProp]装饰器修饰,用于接收应用侧传递过来的数据,其中isPlay与isCollected分别表示歌曲的播放和收藏状态,根据状态的不同展示不同的图标,示例代码如下:
// src/main/ets/widget/pages/PlayControlCard2x4.ets
let storageUpdateCall = new LocalStorage();
@Entry(storageUpdateCall)
@Component
struct PlayControlCard2x4 {
@LocalStorageProp('formId') formId: string = '';
@LocalStorageProp('isPlay') isPlay: boolean = false;
@LocalStorageProp('title') title: string = 'SongName';
@LocalStorageProp('isCollected') isCollected: boolean = false;
@LocalStorageProp('musicCover') musicCover: Resource = $r('app.media.ic_dream');
@LocalStorageProp('singer') singer: string = 'Singer';
@LocalStorageProp('songId') songId: string = '';
@LocalStorageProp('isNeedRequestUpdate') @Watch('requestData') isNeedRequestUpdate: boolean = false;
@LocalStorageProp('imageColor') imageColor: string = 'rgba(76, 72, 68, 1)';
@LocalStorageProp('imageColorHex') imageColorHex: string = '18191d';
requestData() {
ActionUtils.updateControlCardAction(this, this.formId);
}
build() {
RelativeContainer() {
Image(this.musicCover)
// ...
SymbolGlyph(this.isCollected ? $r('sys.symbol.heart_fill') : $r('sys.symbol.heart'))
// ...
Text(this.title)
// ...
Text(this.singer)
// ...
Row() {
SymbolGlyph($r('sys.symbol.backward_end_fill'))
// ...
SymbolGlyph(this.isPlay ? $r('sys.symbol.pause') : $r('sys.symbol.play_fill'))
// ...
SymbolGlyph($r('sys.symbol.forward_end_fill'))
// ...
}
// ...
}
// ...
}
}
-
卡片数据初始化
用户长按桌面应用图标,桌面弹出卡片添加弹窗时,需要对卡片数据进行初始化,能让用户直观的理解到卡片所提供的服务内容、功能和样式。同时,音乐播控卡片的预览数据应该和应用当前播放歌曲应该保持一致。
图7 音乐播控卡片预览效果
在音乐应用中,EntryFormAbility继承了FormExtensionAbility类并实现了其生命周期回调方法。当预览卡片时,会触发EntryFormAbility的onAddForm()回调方法,在此方法中可以获取卡片名称、卡片ID等信息。开发者可以根据卡片名称判断是否为音乐播控卡片,如果是,则调用FormUtils.updateMusicControlCard()方法更新卡片数据。FormUtils是卡片管理工具类,封装了卡片添加、删除、更新等相关功能。示例代码如下:
// src/main/ets/entryformability/EntryFormAbility.ets
export default class EntryFormAbility extends FormExtensionAbility {
onAddForm(want: Want) {
// ...