HarmonyOS UI 设计套件:让你的应用图标更精致
HarmonyOS UI 设计套件:让你的应用图标更精致
什么是 UI 设计套件
你有没有注意到,华为手机上的应用图标看起来特别精致?有些图标还有立体感,有前景和背景的分层效果。这些效果不是开发者自己做的,而是系统提供的 UI 设计套件(UI Design Kit)帮你处理的。
UI 设计套件提供了一套图标处理 API,让你的应用图标能够和 HarmonyOS 的设计风格完美融合。简单说,就是让你的 App 图标看起来更"鸿蒙"、更精致。
你可能会问:我的图标不是已经在 APK 里了吗?为什么还需要处理?因为 HarmonyOS 有一套自己的图标风格规范,比如圆形遮罩、统一的边框、分层效果等等。如果你的图标不经过处理,看起来可能会和系统其他图标格格不入,就像穿了一身西装却配了一双拖鞋。
核心功能
UI 设计套件提供两种图标处理能力:
- 单层图标处理:处理普通的应用图标,加上 HarmonyOS 风格的边框和遮罩。你的图标本来是一张普通的图片,处理之后就变成了 HarmonyOS 风格的圆形图标
- 分层图标处理:处理有前景和背景分层的图标,实现立体效果。这种图标看起来更有层次感,前景是图标主体,背景是底色或图案
两种处理方式都有同步和异步接口,还支持批量处理。如果你有很多图标需要处理,用批量接口效率更高。
环境搭建
硬件要求
- 设备类型:华为手机、平板、PC/2in1、TV
- HarmonyOS 系统:HarmonyOS NEXT Developer Beta2 及以上
软件要求
- DevEco Studio 版本:DevEco Studio NEXT Developer Beta2 及以上
- HarmonyOS SDK 版本:HarmonyOS NEXT Developer Beta2 SDK 及以上
搭建步骤
- 安装 DevEco Studio:去华为开发者官网下载安装
- 配置开发环境:确保网络环境正常
- 设备调试:使用真机进行调试
项目结构
├─ entry/src/main/ets
│ ├─ entryability
│ │ └─ EntryAbility.ets
│ └─ pages
│ ├─ GetHdsIcon.ets // 单层图标处理页面
│ ├─ GetHdsLayeredIcon.ets // 分层图标处理页面
│ └─ Index.ets // 主页界面
├─ entry/src/main/resources // 应用资源目录
项目按功能分成了两个页面:单层图标处理和分层图标处理。你可以根据自己的需求选择用哪种。
图标处理流程
下面是 UI 设计套件的图标处理流程:
批量图标处理流程
下面是批量图标处理的流程:
分层图标处理
分层图标就是有前景和背景两层的图标,看起来更有立体感。比如华为手机上的一些系统图标,前景是图标主体,背景是一层渐变色,这就是分层图标。
第一步:导入模块
import { LayeredDrawableDescriptor } from '@kit.ArkUI';
import { hdsDrawable } from '@kit.UIDesignKit';
import { image } from '@kit.ImageKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { resourceManager } from '@kit.LocalizationKit';
导入的模块有:
LayeredDrawableDescriptor:分层图标描述符,用来描述前景和背景的关系。你可以理解为"分层图标的配置文件"hdsDrawable:UI 设计套件的核心接口,图标处理都靠它。所有的处理方法都在这个模块里image:图片处理,返回的图标是 PixelMap 格式。PixelMap 是鸿蒙系统的图片格式,可以直接显示在 Image 组件里BusinessError:错误处理用的resourceManager:资源管理,用来获取图标资源。你的图标文件放在 resources 目录下,需要用这个来读取
第二步:配置分层图标资源
在 entry/src/main/resources/base/media 目录下创建一个 JSON 文件(比如 drawable.json):
{
"layered-image": {
"background": "$media:background",
"foreground": "$media:foreground"
}
}
这个文件告诉系统:分层图标由背景图和前景图组成。你需要把背景图(background)和前景图(foreground)放到 resources/base/media 目录下。
$media:background 和 $media:foreground 是资源引用语法,意思是引用 media 目录下的 background 和 foreground 文件。这两个文件一般是 PNG 图片。
为什么要用 JSON 配置文件?因为这样系统就知道你的前景和背景分别是什么,处理的时候就能把它们合成分层图标。如果你不配置这个文件,系统不知道哪张是前景、哪张是背景。
第三步:初始化资源
aboutToAppear(): void {
this.resManager = getContext().resourceManager;
if (!this.resManager) {
return;
}
this.layeredDrawableDescriptor = (this.resManager.getDrawableDescriptor(
$r('app.media.drawable').id
)) as LayeredDrawableDescriptor;
}
在页面出现时,初始化资源管理器,获取分层图标的描述符。
getContext().resourceManager 获取当前应用的资源管理器。资源管理器是读取应用资源文件的工具,你的图片、JSON 配置文件等都靠它来读取。
getDrawableDescriptor 会读取我们之前创建的 JSON 配置文件,然后返回一个 LayeredDrawableDescriptor 对象。这个对象包含了前景和背景的信息,后面调用图标处理接口时需要用到。
第四步:调用分层图标接口
同步方式获取单个分层图标:
private getHdsLayeredIconWithBorder(): image.PixelMap | null {
try {
return hdsDrawable.getHdsLayeredIcon(
this.bundleName, // 应用包名
this.layeredDrawableDescriptor, // 分层图标描述符
48, // 图标大小(像素)
true // 是否有边框
);
} catch (err) {
let message = (err as BusinessError).message;
let code = (err as BusinessError).code;
console.error(`getHdsLayeredIcon failed, code: ${code}, message: ${message}`);
return null;
}
}
调用 hdsDrawable.getHdsLayeredIcon 获取处理后的分层图标。参数说明:
bundleName:应用的包名,比如com.example.myapp。系统用这个来标识是哪个应用的图标layeredDrawableDescriptor:分层图标描述符,就是上一步获取的那个对象48:图标大小,单位是像素。48 像素是比较常用的尺寸,适合在列表里显示true:是否添加 HarmonyOS 风格的边框。加了边框的图标看起来更统一,和系统其他图标风格一致
这个接口是同步的,会直接返回处理后的 PixelMap。如果处理失败,会抛出异常,所以要用 try...catch 包起来。
异步方式获取单个分层图标:
private getHdsLayeredIconAsync(): void {
try {
hdsDrawable.getHdsLayeredIconAsync(
this.bundleName,
this.layeredDrawableDescriptor,
48,
true
).then((data: image.PixelMap) => {
this.layeredIconAsyncResult = data;
}).catch((err: BusinessError) => {
console.error(`getHdsLayeredIconAsync error, code: ${err.code}, msg: ${err.message}`);
});
} catch (err) {
let message = (err as BusinessError).message;
let code = (err as BusinessError).code;
console.error(`getHdsLayeredIconAsync failed, code: ${code}, message: ${message}`);
}
}
异步版本的接口返回一个 Promise,适合在需要避免阻塞主线程的场景下使用。
为什么要区分同步和异步?因为图标处理可能需要一些时间,特别是处理大图标或者批量处理的时候。如果用同步接口,界面可能会卡顿。用异步接口的话,处理过程在后台进行,界面不会卡。
批量处理分层图标:
private getHdsLayeredIcons(): void {
if (!this.layeredDrawableDescriptor) {
return;
}
// 配置选项
let options: hdsDrawable.Options = {
size: 48, // 图标大小
hasBorder: true, // 是否有边框
parallelNumber: 4 // 并行处理数量
};
// 准备要处理的图标列表
let layeredIcons: Array<hdsDrawable.LayeredIcon> = [];
for (let i = 0; i < 10; i++) {
layeredIcons.push({
bundleName: `${this.bundleName}-${i}`,
layeredDrawableDescriptor: this.layeredDrawableDescriptor
});
}
// 批量处理
try {
hdsDrawable.getHdsLayeredIcons(layeredIcons, options)
.then((data: Array<hdsDrawable.ProcessedIcon>) => {
this.layeredIconsResult = data;
})
.catch((err: BusinessError) => {
console.error(`getHdsLayeredIcons error, code: ${err.code}, msg: ${err.message}`);
});
} catch (err) {
console.error(`getHdsLayeredIcons failed`);
}
}
批量处理接口 getHdsLayeredIcons 可以一次处理多个图标。这在应用商店、文件管理器等需要显示大量图标的场景下特别有用。
options 里的配置:
size:图标大小,和单个处理一样hasBorder:是否加边框parallelNumber:并行处理数量。设为 4 表示同时处理 4 个图标,能提高效率。但不要设太大,否则会占用太多系统资源
第五步:显示图标
Image(this.getHdsLayeredIconWithBorder())
.width(48)
.height(48)
把处理后的图标显示在 Image 组件里就行了。注意宽高要和你处理时设置的大小一致,否则图标可能会被拉伸或压缩。
单层图标处理
单层图标处理和分层图标处理类似,但不需要配置分层资源。你的图标就是一张普通的图片,处理之后加上 HarmonyOS 风格的遮罩和边框。
第一步:导入模块
import { LayeredDrawableDescriptor, DrawableDescriptor } from '@kit.ArkUI';
import { hdsDrawable } from '@kit.UIDesignKit';
import { image } from '@kit.ImageKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { resourceManager } from '@kit.LocalizationKit';
多导入了一个 DrawableDescriptor,这是单层图标用的描述符。和分层图标不同,单层图标只有一个图层,所以用 DrawableDescriptor 而不是 LayeredDrawableDescriptor。
第二步:初始化资源
aboutToAppear(): void {
this.resManager = getContext().resourceManager;
if (!this.resManager) {
return;
}
// 获取分层图标描述符(用于遮罩)
this.layeredDrawableDescriptor = (this.resManager.getDrawableDescriptor(
$r('app.media.drawable').id
)) as LayeredDrawableDescriptor;
// 获取单层图标描述符
this.drawableDescriptor = (this.resManager?.getDrawableDescriptor(
$r('app.media.normal_icon')
)) as DrawableDescriptor;
}
单层图标处理需要两个描述符:
layeredDrawableDescriptor:提供遮罩(mask),用于 HarmonyOS 风格的圆形遮罩。你可能会问:单层图标为什么需要分层图标描述符?因为遮罩是从分层图标配置里提取的,系统用这个遮罩来把你的方形图标裁剪成圆形drawableDescriptor:原始的应用图标,就是你放在 resources 目录下的那张图片
第三步:调用单层图标接口
同步方式:
private getHdsIconWithBorder(): image.PixelMap | null {
try {
return hdsDrawable.getHdsIcon(
this.bundleName, // 应用包名
this.drawableDescriptor?.getPixelMap(), // 原始图标
48, // 图标大小
this.layeredDrawableDescriptor?.getMask().getPixelMap(), // 遮罩
true // 是否有边框
);
} catch (err) {
let message = (err as BusinessError).message;
let code = (err as BusinessError).code;
console.error(`getHdsIcon failed, code: ${code}, message: ${message}`);
return null;
}
}
调用 hdsDrawable.getHdsIcon 处理单层图标。参数比处理分层图标多了一个遮罩参数:
this.drawableDescriptor?.getPixelMap():原始图标的 PixelMap。getPixelMap()是把描述符转成 PixelMap 格式this.layeredDrawableDescriptor?.getMask().getPixelMap():遮罩的 PixelMap。getMask()获取遮罩,getPixelMap()转成 PixelMap 格式
遮罩的作用是把你的方形图标裁剪成 HarmonyOS 的标准形状(一般是圆形)。就像用一个圆形的模具去切一块方形的饼干,切完就变成圆形的了。
异步方式:
private getHdsIconAsync(): void {
try {
hdsDrawable.getHdsIconAsync(
this.bundleName,
this.drawableDescriptor?.getPixelMap(),
48,
this.layeredDrawableDescriptor?.getMask().getPixelMap(),
true
).then((data: image.PixelMap) => {
this.iconAsyncResult = data;
}).catch((err: BusinessError) => {
console.error(`getHdsIconAsync error, code: ${err.code}, msg: ${err.message}`);
});
} catch (err) {
console.error(`getHdsIconAsync failed`);
}
}
异步版本和同步版本参数一样,只是返回 Promise 而不是直接返回结果。
批量处理:
getHdsIcons(): void {
if (!this.drawableDescriptor || !this.layeredDrawableDescriptor) {
return;
}
let options: hdsDrawable.Options = {
size: 48,
hasBorder: true,
parallelNumber: 4
};
let icons: Array<hdsDrawable.Icon> = [];
for (let i = 0; i < 10; i++) {
icons.push({
bundleName: `${this.bundleName}-${i}`,
pixelMap: this.drawableDescriptor.getPixelMap()
});
}
try {
hdsDrawable.getHdsIcons(
icons,
this.layeredDrawableDescriptor.getMask().getPixelMap(),
options
).then((data: Array<hdsDrawable.ProcessedIcon>) => {
this.iconsResult = data;
}).catch((err: BusinessError) => {
console.error(`getHdsIcons error, code: ${err.code}, msg: ${err.message}`);
});
} catch (err) {
console.error(`getHdsIcons failed`);
}
}
批量处理接口 getHdsIcons 和分层图标的批量接口类似,但传入的是单层图标的数组。遮罩参数单独传入,因为所有图标共用同一个遮罩。
实际应用场景
UI 设计套件在实际开发中有很多用途:
应用商店
// 应用商店里显示应用图标
async function displayAppIcon(bundleName: string, iconPixelMap: image.PixelMap) {
let mask = layeredDrawableDescriptor.getMask().getPixelMap();
let processedIcon = hdsDrawable.getHdsIcon(bundleName, iconPixelMap, 48, mask, true);
// 显示处理后的图标
}
在应用商店里,每个应用的图标都需要和 HarmonyOS 的设计风格一致。用 UI 设计套件处理一下,图标就会自动加上 HarmonyOS 风格的边框和遮罩,看起来和系统应用的图标一样精致。
文件管理器
// 文件管理器里显示应用图标
async function showAppIcons(appList: Array<{name: string, icon: image.PixelMap}>) {
let icons: Array<hdsDrawable.Icon> = appList.map(app => ({
bundleName: app.name,
pixelMap: app.icon
}));
let options: hdsDrawable.Options = { size: 48, hasBorder: true, parallelNumber: 4 };
let mask = layeredDrawableDescriptor.getMask().getPixelMap();
let processedIcons = await hdsDrawable.getHdsIcons(icons, mask, options);
// 显示处理后的图标列表
}
在文件管理器里,如果要显示已安装应用的图标列表,可以用批量处理接口一次性处理所有图标,效率更高。想象一下,如果你有 100 个应用图标要处理,一个一个处理太慢了,批量处理能快很多。
桌面小部件
// 桌面小部件里显示应用图标
function getWidgetIcon(bundleName: string): image.PixelMap | null {
return hdsDrawable.getHdsLayeredIcon(bundleName, layeredDrawableDescriptor, 96, false);
}
桌面小部件里的图标可能需要更大的尺寸(比如 96 像素),而且可能不需要边框。可以根据需求调整参数,让图标在不同场景下都有最好的显示效果。
适用场景
UI 设计套件适合以下场景:
- 应用商店:展示应用图标
- 文件管理器:显示应用图标
- 桌面小部件:显示应用图标
- 设置页面:显示应用图标
- 任何需要显示应用图标的地方
注意事项
- 图标资源:分层图标需要配置前景和背景资源,确保资源文件存在且路径正确
- 图标大小:根据实际需求设置合适的图标大小,太大会影响性能和内存占用
- 批量处理:如果需要处理大量图标,建议使用批量接口并设置合适的并行数。并行数太大会占用太多 CPU,太小又处理得慢
- 错误处理:图标处理可能会失败(比如资源文件不存在),要做好错误处理
- 内存管理:处理后的 PixelMap 会占用内存,不需要时要及时释放。特别是在批量处理的场景下,要注意内存使用
总结
UI 设计套件让你的应用图标更精致,核心流程:
- 导入 UI 设计套件模块
- 配置图标资源(分层图标需要前景和背景)
- 初始化资源管理器,获取图标描述符
- 调用图标处理接口,获取处理后的图标
- 在 Image 组件里显示图标
掌握了这些,你就能让你的应用图标和 HarmonyOS 的设计风格完美融合,提升应用的整体品质。
更多推荐


所有评论(0)