1、服务卡片的下半场——“去边界化”
随着操作系统的不断演进,桌面 UI 的设计语言正在经历从“信息堆叠”到“环境融合”的深刻变革。在 HarmonyOS 6.1 及以上系统中,华为针对桌面服务卡片(Service Widgets)引入了诸多革命性的视觉与底层 API 增强。其中最受开发者与 UI/UX 设计师关注的,莫过于基于 Form Kit 全新开放的“背板透明”(Transparent Backplate)能力。 过去的桌面卡片大多被局限在一个带有实色或毛玻璃背景的圆角矩形中,这种设计虽然能保证卡片在任何壁纸下都具备绝对的可读性,但却打破了用户精心挑选的壁纸画面的完整性,甚至在某些复杂的视觉场景下显得有些突兀。
HarmonyOS 6.1 提出了一种全新的“容器”概念——透明卡片。它不仅是简单的“隐藏背景”,而是通过极为严苛的排版、色彩自适应以及阴影处理,将核心内容自然地“悬浮”并融入系统的壁纸环境中。这种极致沉浸的视觉体验,极大降低了用户的视觉负担,也为应用在桌面上打造出了一种更加通透、灵动的交互触点。
本文将作为一篇深度的技术指南,结合 HarmonyOS 6.1+ 的新特性及官方API,从设计规范、核心参数到代码实战,手把手教你开发一款拥有极致沉浸感的高质量透明卡片。
2. 关键参数与规范速览
在进入硬核的代码阶段前,需要对透明卡片的设计规范和关键参数有全局的认知。为了让卡片在“无背景”的极端条件下依然保持高可用性,HarmonyOS 给出了极其详细的约束。以下是梳理的速览表格:
| 属性类别 | 属性项 | 参数与技术细节 |
| 基本设计参数 | 尺寸规格 | 默认首选 2X2,可选 1x1, 1x2, 2x4, 4x4 等,根据 supportDimensions 布局配置。 |
| 显示规则 | 设计需具备清晰的信息层次、简洁易识别,卡片高度信息提倡极简风格。 | |
| 视觉与样式规范 | 背景透明 | 卡片提供方在配置文件中设置 "transparencyEnabled": true。 |
| 文本字体 | 主标题 16sp,副标题 12sp,根据深浅色模式自适应文本颜色。 | |
| 颜色设计 | 避免明度过高的浅色,全面支持深色模式,构建层次分明的色彩系统。 | |
| 按钮与图标 | 内置图标大小 40x40vp 或 12x24vp;交互按钮 56x24vp,根据模式颜色自适应。 | |
| 卡片动效 | 支持元素形变与渐隐渐现动效,帮助用户无缝理解内容刷新与状态切换。 | |
| 生命周期与规则 | 核心生命周期 | onAddForm, onUpdateForm, onDeleteForm 等,卡片需自行进行数据缓存管理。 |
| 权限与资源申请 | 背板透明需申请专有能力,且卡片非透明的“有效信息占比”不得小于 10%。 | |
| 特别场景适配 | 锁屏/待机屏保 | 按 2*2 尺寸设计为主,系统可能将其放大填充。支持长按/双指捏合编辑,避免卡片内复杂手势。 |
3. 环境准备与全局架构配置
要实现背板完全透明的卡片,我们首先需要在项目的架构层(主要是配置文件层)向系统声明我们所需的特性。这主要涉及到应用配置文件(module.json5)和卡片专门的配置文件(form_config.json)。
3.1 开启透明配置与多尺寸支持
在 Form Kit 的设计中,卡片的维度定义直接决定了用户可以在桌面上如何排布它。默认且最常被使用的经典尺寸是 2*2。为了适配多态桌面,我们可以通过 supportDimensions 字段声明支持的组合。
更关键的是新增的 API 属性 "transparencyEnabled": true。这个开关是开启“沉浸感”大门的钥匙。声明该属性后,系统在渲染渲染该卡片层时,将移除底层的默认系统毛玻璃背景板。
在项目的 src/main/resources/base/profile/form_config.json 中配置如下:
{
"forms": [
{
"name": "TransparentWidget",
"description": "This is a highly immersive transparent widget.",
"src": "./ets/widget/pages/TransparentWidgetCard.ets",
"window": {
"designWidth": 720,
"autoDesignWidth": true
},
"colorMode": "auto",
"isDefault": true,
"updateEnabled": true,
"scheduledUpdateTime": "10:30",
"updateDuration": 1,
"defaultDimension": "2*2",
"supportDimensions": [
"1*1",
"1*2",
"2*2",
"2*4",
"4*4"
],
"transparencyEnabled": true,
"formConfigAbility": "ability://com.example.immersiveapp.EntryAbility"
}
]
}
配置解析:
- transparencyEnabled:核心 API。一旦设为 true,卡片 UI 根节点如果不加背景色,将直接透出用户的壁纸。
- window 节点:针对折叠屏设备的杀手锏。designWidth: 720 与 autoDesignWidth: true 可以让卡片在折叠屏展开态和折叠态之间动态计算布局比例,而不会出现元素被生硬拉伸或截断的问题。
3.2 权限与系统规则约束
需要特别注意的是,“背板透明需申请专有能力,卡片占比不小于 10%”。HarmonyOS 对透明卡片有严格的 UI 审查策略:
- 可见度底线:如果你把卡片做成全透明的,且上面的文本或图标太小(占比不足整体卡片面积的 10%),系统和审核规范是不允许的,因为这会造成“隐形幽灵卡片”的体验灾难,用户将无法找到或者误触卡片。
- 手势控制:系统在锁屏及桌面上,会接管卡片的长按(编辑卡片)和双指捏合(进入桌面编辑态)等系统级手势。因此,在透明卡片的内部,我们必须避免开发复杂的滑动(Swipe)、拖拽(Drag)手势,仅保留最基础的点击(Click)即可。
4. 视觉与样式的底层重构
透明卡片绝非一句简单的 backgroundColor(Color.Transparent) 就能解决的。失去了背景板的保护,文字和图标将直接暴露在千变万化的用户壁纸之上。用户可能使用的是一张纯白色的风景图,也可能是极度黑暗的星空图。因此,完美的深浅色模式(Dark/Light Mode)自适应是透明卡片的灵魂。
4.1 动态色彩体系规范
为了在保障可读性的同时兼顾沉浸感,需要摒弃高饱和度、高明度的危险配色,建立一套基于透明度(Opacity / Alpha)计算的系统级自适应颜色矩阵。根据系统建议,我们将颜色固化为以下方案:
| 样式 / 前景元素 | 浅色模式 (Light Mode) | 深色模式 (Dark Mode) |
| 主标题 (Main Title) | #000000,90% 不透明度 | #FFFFFF,86% 不透明度 |
| 副标题 (Subtitle) | #000000,60% 不透明度 | #FFFFFF,60% 不透明度 |
| 卡片亮点 ( 特殊底板区域 ) | #000000,3% 不透明度 | #FFFFFF,3% 不透明度 |
| 按钮文字 (Button Text) | #0A59F7 | #5291FF |
为了在 ArkTS 中优雅地实现这一套方案,我们应该在 resources/base/element/color.json 中配置基础的透明颜色资源,或利用 HarmonyOS 强大的状态变量动态响应系统主题。
4.2 布局、字体与控件定义
透明卡片的寸土寸金要求设计师做“减法”。
- 字体排版:主标题强制收敛为 16sp,副标题压缩为 12sp。这种克制的字体组合能确保在 2*2 小空间内留下足够的“呼吸感”。
- 图标与按钮:内置图标的视觉重量需保持统一,主图标为 40x40vp,辅助或内联小图标为 12x24vp;而具有强交互属性的按钮,应使用 56x24vp 的标准胶囊状或圆角矩形尺寸,并确保点击区域的准确触达。
5. ArkTS UI 代码实战:构建透明卡片视图
下面进入具体的 UI 编写环节,创建一个天气与日程的综合透明卡片。在此过程中,我们会大量使用 ArkUI 的声明式语法,并注入上文提到的视觉参数。
// TransparentWidgetCard.ets
@Entry
@Component
struct TransparentWidgetCard {
// 从 FormBindingData 获取的状态数据
@LocalStorageProp('title') title: string = '今日日程';
@LocalStorageProp('subTitle') subTitle: string = '10:30 AM · 设计评审';
@LocalStorageProp('temperature') temperature: string = '24°';
// 监听系统深浅色模式(假设系统环境已通过Context传入或全局管理)
@State isDarkMode: boolean = true;
build() {
// 根容器,千万不要设置任何阻挡性的背景色
Stack({ alignContent: Alignment.TopStart }) {
// 核心内容区
Column() {
// 头部标题区域
Row() {
Text(this.title)
.fontSize('16sp')
.fontWeight(FontWeight.Medium)
.fontColor(this.isDarkMode ? 'rgba(255, 255, 255, 0.86)' : 'rgba(0, 0, 0, 0.90)')
Blank()
// 天气小图标 (40x40vp)
Image($r('app.media.ic_weather_sunny'))
.width('40vp')
.height('40vp')
.objectFit(ImageFit.Contain)
}
.width('100%')
.padding({ top: 12, left: 12, right: 12 })
// 副标题与内容描述区域
Row() {
Text(this.subTitle)
.fontSize('12sp')
.fontColor(this.isDarkMode ? 'rgba(255, 255, 255, 0.60)' : 'rgba(0, 0, 0, 0.60)')
.maxLines(2)
.textOverflow({ overflow: TextOverflow.Ellipsis })
}
.width('100%')
.padding({ top: 4, left: 12, right: 12 })
Blank()
// 底部操作与亮点区域
Row() {
// 卡片亮点/微小底板区 (使用 3% 的透明度烘托氛围,但不遮挡壁纸)
Row() {
Text('查看详情')
.fontSize('12sp')
.fontColor(this.isDarkMode ? '#5291FF' : '#0A59F7') // 按钮文字自适应
}
.width('56vp')
.height('24vp') // 交互按钮规范 56x24vp
.justifyContent(FlexAlign.Center)
.alignItems(VerticalAlign.Center)
.backgroundColor(this.isDarkMode ? 'rgba(255, 255, 255, 0.03)' : 'rgba(0, 0, 0, 0.03)')
.borderRadius(12)
.onClick(() => {
// 卡片点击事件,拉起宿主应用
postCardAction(this, {
action: 'router',
abilityName: 'EntryAbility',
params: { targetPage: 'details' }
});
})
}
.width('100%')
.padding({ bottom: 12, left: 12, right: 12 })
.justifyContent(FlexAlign.End)
}
.width('100%')
.height('100%')
}
.width('100%')
.height('100%')
.backgroundColor(Color.Transparent) // 关键:确保底层彻底透明
}
}
代码深度剖析
- 彻底的透明化:根节点 Stack 和内部容器都没有调用不透明的 backgroundColor。这使得壁纸毫无保留地渗透进卡片的各个缝隙。
- 极简色彩矩阵映射:利用 rgba 色值动态匹配规范要求的透明度。例如主标题在深色模式下使用 #FFFFFF 且 Alpha 通道为 86%,即 rgba(255, 255, 255, 0.86)。
- 空间秩序 (Spatial Order):由于取消了背景卡片天然带来的边框感,我们在容器内部严格使用 padding (左右 12vp) 以及 Blank() 自适应留白组件。这种“用排版建立边界”的设计思想,是透明卡片能够成立的核心。
6. 生命周期管理与数据渲染机制
在桌面上展示的透明卡片,其实际的运行时并不在主 App 进程中,而是寄宿在桌面的 Form 进程里。这就要求我们需要通过 FormExtensionAbility 来进行严格的生命周期管理。
卡片的四大核心生命周期:
- onAddForm:卡片被用户添加到桌面时触发。此时需要完成初始化的数据抓取与绑定。
- onUpdateForm:根据 form_config.json 中配置的定时刷新时间,或者由于网络推送等触发的数据更新时调用。
- onCastToNormalForm:如果是临时卡片转为常态卡片时触发。
- onDeleteForm:卡片被用户移除时触发。需要在这里清理数据库缓存、解绑服务以防止内存泄漏。
下面是 FormExtensionAbility 的具体实现示例:
import formBindingData from '@ohos.app.form.formBindingData';
import FormExtensionAbility from '@ohos.app.form.FormExtensionAbility';
import formInfo from '@ohos.app.form.formInfo';
import formProvider from '@ohos.app.form.formProvider';
export default class ImmersiveFormAbility extends FormExtensionAbility {
onAddForm(want: any) {
console.info('[ImmersiveForm] onAddForm triggered');
// 初始化卡片数据
let initialData = {
title: '今日行程',
subTitle: '您有一场 10:30 的设计评审',
temperature: '24°'
};
// 将数据封装为 FormBindingData,以便底层的 ArkUI 能够响应
let formData = formBindingData.createFormBindingData(initialData);
// 返回数据对象供系统渲染
return formData;
}
onUpdateForm(formId: string) {
console.info(`[ImmersiveForm] onUpdateForm triggered for ${formId}`);
// 模拟从后台或者本地数据库拉取新数据
let updatedData = {
title: '下午茶提醒',
subTitle: '15:00 咖啡与甜点已备好',
temperature: '25°'
};
let formData = formBindingData.createFormBindingData(updatedData);
// 主动推送更新到卡片
formProvider.updateForm(formId, formData).catch((err: any) => {
console.error(`[ImmersiveForm] Failed to update form: ${JSON.stringify(err)}`);
});
}
onDeleteForm(formId: string) {
console.info(`[ImmersiveForm] onDeleteForm triggered for ${formId}`);
// 业务逻辑:卡片缓存清理
// 卡片需自行缓存管理,在此处清除针对该 formId 存储的临时文件或数据字典
}
}
卡片必须自行缓存管理,在透明卡片的开发中,“状态无缝衔接”至关重要。系统在桌面滑动或息屏重绘时,可能会频繁重新拉起卡片渲染层。因此,卡片的数据应该遵循“本地缓存为主,网络拉取为辅”的策略。在 onUpdateForm 时,获取到的网络数据必须立刻序列化存储到本地沙箱。下次 onAddForm 被触发(例如设备重启后桌面重新加载卡片)时,优先从沙箱读取旧数据瞬间渲染,避免透明卡片出现漫长的“白屏或透明闪烁”现象,之后再在后台拉取新数据。
7. 动效体验提升:构建呼吸般的刷新感
如果一个背景透明的悬浮信息在数据变化时,表现为生硬的闪烁和突变,那它苦心营造的“沉浸感”将瞬间破灭。HarmonyOS 6.1+ 支持针对卡片元素添加平滑动效。 在透明卡片上,“渐隐渐现”(Fade In / Fade Out)与“缩放过渡”(Scale Transform)是理解内容刷新状态的绝佳帮助。可以在 ArkTS 的组件库中,引入 animation 属性或 transition 特性来优化体验。 // 示例:在数据刷新时的组件变化动效
Text(this.subTitle)
.fontSize('12sp')
.fontColor(this.isDarkMode ? 'rgba(255, 255, 255, 0.60)' : 'rgba(0, 0, 0, 0.60)')
.transition({ type: TransitionType.All, opacity: 0, scale: { x: 0.9, y: 0.9 } }) // 渐隐与微小缩放
.animation({
duration: 400, // 400毫秒
curve: Curve.EaseInOut, // 平滑曲线
delay: 50 // 略微延迟避免卡顿
})
通过这几十行代码,当 subTitle 的数据由后台驱动更新时,旧文字会像雾气一样消散,新文字则伴随着轻微的弹性放大浮现出来。这在无边界的透明画布上,不仅显得极为高级,还能在不干扰用户核心注意力的情况下,巧妙地传达“信息已刷新”的视觉暗号。
8. 特别场景下的重度适配策略
8.1 锁屏与待机屏保的“超大视野”
透明卡片不仅属于桌面,它也常常被用在系统的锁屏与待机屏保环境中。在这种极其特殊的系统场景下,HarmonyOS 官方的设计规范明确指出:针对锁屏,优先按 2*2 尺寸设计,系统可能自动将其双倍放大以填充锁屏核心信息区。
这意味着:
- 矢量资产至上:卡片内部的图片资源(例如 40x40vp 的内置图标),如果切图使用了位图(PNG/JPG),在放大后极易出现像素锯齿,摧毁透明质感。必须使用 SVG 矢量图。
- 极简防误触交互:锁屏界面往往承载着繁重的系统事件(例如滑动解锁、调用手电筒、左滑进入负一屏)。在设计透明卡片时,不仅在视觉上不能用复杂的底板去跟系统界面“抢地盘”,在交互上也要严格限制,避免卡片内含有长按或复杂手势,仅暴露极其明确的点击按钮(例如前文提到的 56x24vp 的外链按钮)给用户,因为系统的长按和双指捏合手势具有更高的优先级。
8.2 折叠屏空间的自适应重组
针对华为 Mate X 等折叠屏设备,桌面的宽度会随着设备的开合发生剧烈变动。一个在折叠态(外屏)显得排版饱满的卡片,展开到内屏后可能会因为被强行拉宽而变得松散难看。上文在 form_config.json 中配置的:
"window": {
"designWidth": 720,
"autoDesignWidth": true
}
发挥了核心作用。autoDesignWidth 意味着不需要为展开态和折叠态开发两套代码。底层引擎会根据当前外屏/内屏的绝对物理宽度与 720 的比例,动态重算组件内部的伸缩属性(Flex/Weight)。在代码层面,需要配合 Blank() 组件或 Flex 布局中的 flexGrow 属性,让内部空白区域自动吸收掉折叠屏展开时多出来的横向多余空间,保持主标题和右侧图标始终锚定在卡片的左、右绝对边缘。
9. 避坑指南:隐形底线与合规考量
开发透明卡片是一场与“可见度”的博弈。需要时刻紧绷以下两根弦:
-
不可预知的壁纸对比度:开发者极易犯的错误是:硬编码颜色。如果不加判定地全量使用黑色字体,一旦用户换上纯黑壁纸,你的卡片直接“人间蒸发”。
解决方案:永远利用系统环境变量获取当前设备处于浅色/深色模式,使用透明度代替硬色差。此外,通过上文提到的“卡片亮点区域(3%背景)”,在局部文字或关键操作按钮下铺设微弱的对比层,可以在不破坏大面积透明的前提下,挽救极端低对比度环境下的可读性。
-
10% 有效信息占比的铁律:系统强制要求卡片的“非透明占比”不能低于 10%。如果你的卡片只有中间孤零零的一个 12sp 单词,它会被审核系统判定为“恶意隐藏卡片”或者极度不合规的设计。
解决方案:确保在尺寸允许的范围内,使用合适的 40x40vp 主题图标、合理长度的 16sp 主标题文本以及 56x24vp 的按钮,从而充实信息密度,达到合规且美观的界限。
10. 总结与展望
在 HarmonyOS 6.1 及最新的 Form Kit 能力赋能下,透明卡片彻底打破了传统 App 组件与桌面操作系统壁纸之间的生硬边界。配置 "transparencyEnabled": true 仅仅是一个基础声明,想要交付一个充满“高级感”与“极致沉浸感”的视觉容器,需要开发者极尽打磨每一个维度的细节:
从严格遵从 16sp / 12sp 的字号秩序,到运用深色模式下 #FFFFFF 86% / 60% 的精密不透明度计算;从在 FormExtensionAbility 中建立健壮的数据自缓存模型,到利用平滑动效优雅地处理数据刷新;再到从容应对锁屏 2*2 放大场景以及折叠屏的弹性伸缩。
开发一张极致体验的透明卡片,就像是在打造一款悬浮在系统环境之上的数字艺术品。拥抱无界设计,去打造真正“懂壁纸、懂用户、懂沉浸”的超级卡片吧!