解决方案
-
系统图标使用
@Component
struct SystemIcons {
build() {
Row({ space: 16 }) {
// ✅ 使用系统图标
Image( r ( ′ s y s . m e d i a . o h o s i c p u b l i c a d d ′ ) ) . w i d t h ( 24 ) . h e i g h t ( 24 ) . f i l l C o l o r ( C o l o r . B l a c k ) ; I m a g e ( r('sys.media.ohos_ic_public_add')) .width(24) .height(24) .fillColor(Color.Black); Image( r(′sys.media.ohosicpublicadd′)) .width(24) .height(24) .fillColor(Color.Black); Image(r('sys.media.ohos_ic_public_delete'))
.width(24)
.height(24)
.fillColor(Color.Red);Image($r('sys.media.ohos_ic_public_search'))
.width(24)
.height(24);
}
}
} -
自定义图标管理
/**
-
图标资源管理类
*/
export class AppIcons {
// ✅ 统一管理图标资源
static readonly ADD = r('app.media.ic_add'); static readonly DELETE = r('app.media.ic_delete');
static readonly EDIT = r('app.media.ic_edit'); static readonly SEARCH = r('app.media.ic_search');
static readonly FILTER = r('app.media.ic_filter'); static readonly SETTINGS = r('app.media.ic_settings');// 功能模块图标
static readonly ITEM = r('app.media.ic_item'); static readonly HEALTH = r('app.media.ic_health');
static readonly BABY = r('app.media.ic_baby'); static readonly ASSET = r('app.media.ic_asset');
static readonly FINANCE = $r('app.media.ic_finance');// 状态图标
static readonly SUCCESS = r('app.media.ic_success'); static readonly WARNING = r('app.media.ic_warning');
static readonly ERROR = r('app.media.ic_error'); static readonly INFO = r('app.media.ic_info');
}// ✅ 使用
@Component
struct IconUsage {
build() {
Row() {
Image(AppIcons.ADD)
.width(24)
.height(24);Image(AppIcons.DELETE)
.width(24)
.height(24)
.fillColor(Color.Red);
}
}
}
- 可复用图标组件
/**
-
图标组件
*/
@Component
export struct AppIcon {
@Prop icon: Resource;
@Prop size: number = 24;
@Prop color?: ResourceColor;build() {
Image(this.icon)
.width(this.size)
.height(this.size)
.fillColor(this.color)
.objectFit(ImageFit.Contain);
}
}// ✅ 使用
@Component
struct IconDemo {
build() {
Row({ space: 16 }) {
AppIcon({
icon: AppIcons.ADD,
size: 24,
color: AppColors.PRIMARY
})AppIcon({
icon: AppIcons.DELETE,
size: 32,
color: Color.Red
})
}
}
}
- 带标签的图标按钮
/**
-
图标按钮组件
*/
@Component
export struct IconButton {
@Prop icon: Resource;
@Prop label: string;
@Prop onClick?: () => void;
@Prop iconSize: number = 24;
@Prop iconColor?: ResourceColor;build() {
Column({ space: 4 }) {
Image(this.icon)
.width(this.iconSize)
.height(this.iconSize)
.fillColor(this.iconColor);Text(this.label)
.fontSize(12)
.fontColor('#666666');
}
.padding(8)
.borderRadius(8)
.onClick(() => {
if (this.onClick) {
this.onClick();
}
})
}
}// ✅ 使用
@Component
struct ActionBar {
build() {
Row({ space: 24 }) {
IconButton({
icon: AppIcons.ADD,
label: '添加',
iconColor: AppColors.PRIMARY,
onClick: () => {
console.info('点击添加');
}
})IconButton({
icon: AppIcons.EDIT,
label: '编辑',
onClick: () => {
console.info('点击编辑');
}
})
}
}
}
- 动态图标状态
/**
-
可切换状态的图标
/
@Component
struct ToggleIcon {
@State isActive: boolean = false;
build() {
Image(this.isActive ? AppIcons.HEART_FILLED : AppIcons.HEART_OUTLINE)
.width(24)
.height(24)
.fillColor(this.isActive ? Color.Red : '#999999')
.onClick(() => {
animateTo({ duration: 200 }, () => {
this.isActive = !this.isActive;
});
})
}
}
/* -
加载状态图标
*/
@Component
struct LoadingIcon {
@State isLoading: boolean = true;build() {
if (this.isLoading) {
LoadingProgress()
.width(24)
.height(24)
.color(AppColors.PRIMARY);
} else {
Image(AppIcons.SUCCESS)
.width(24)
.height(24)
.fillColor(Color.Green);
}
}
}
- 图标大小规范
/**
-
图标尺寸常量
*/
export class IconSizes {
static readonly SMALL = 16; // 小图标
static readonly NORMAL = 24; // 常规图标
static readonly LARGE = 32; // 大图标
static readonly XLARGE = 48; // 超大图标
}// ✅ 使用
Image(AppIcons.ADD)
.width(IconSizes.NORMAL)
.height(IconSizes.NORMAL);
图标格式选择
SVG vs PNG
特性 SVG PNG
缩放 ✅ 无损 ❌ 失真
颜色控制 ✅ fillColor ❌ 不可改
文件大小 小 大
适用场景 单色图标 复杂图片
推荐方案
// ✅ 推荐:单色图标用SVG
resources/base/media/
ic_add.svg // 添加图标
ic_delete.svg // 删除图标
ic_edit.svg // 编辑图标// ✅ 推荐:复杂图片用PNG
resources/base/media/
bg_splash.png // 启动页背景
img_avatar.png // 用户头像
img_banner.png // Banner图片
实战案例
案例1: 底部导航栏
@Component
struct TabBar {
@State currentIndex: number = 0;private tabs: TabInfo[] = [
{ id: 0, icon: AppIcons.HOME, label: '首页' },
{ id: 1, icon: AppIcons.CATEGORY, label: '分类' },
{ id: 2, icon: AppIcons.PROFILE, label: '我的' }
];@Builder
buildTab(tab: TabInfo, index: number) {
Column({ space: 4 }) {
Image(tab.icon)
.width(24)
.height(24)
.fillColor(this.currentIndex === index ? AppColors.PRIMARY : '#999999');Text(tab.label)
.fontSize(12)
.fontColor(this.currentIndex === index ? AppColors.PRIMARY : '#999999');
}
}build() {
Row() {
ForEach(this.tabs, (tab: TabInfo, index: number) => {
this.buildTab(tab, index)
.layoutWeight(1)
.onClick(() => {
this.currentIndex = index;
})
})
}
.width('100%')
.height(56)
.backgroundColor(Color.White)
}
}
案例2: 空状态页面
@Component
struct EmptyState {
@Prop icon: Resource = AppIcons.EMPTY;
@Prop message: string = '暂无数据';
@Prop actionText?: string;
@Prop onAction?: () => void;build() {
Column({ space: 16 }) {
Image(this.icon)
.width(120)
.height(120)
.fillColor('#CCCCCC');Text(this.message)
.fontSize(14)
.fontColor('#999999');if (this.actionText && this.onAction) {
Button(this.actionText)
.onClick(() => {
if (this.onAction) {
this.onAction();
}
});
}
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center);
}
}// 使用
EmptyState({
icon: AppIcons.NO_DATA,
message: '还没有添加物品',
actionText: '立即添加',
onAction: () => {
router.pushUrl({ url: 'pages/AddItemPage' });
}
})
最佳实践
-
图标命名规范
// ✅ 推荐命名
ic_add.svg // ic_功能.svg
ic_delete.svg
ic_home_filled.svg // ic_功能_状态.svg
ic_heart_outline.svg// ❌ 不推荐
add.svg
delete_icon.svg
home.png -
统一管理
// ✅ 推荐:集中管理
export class AppIcons {
static readonly ADD = KaTeX parse error: Expected 'EOF', got '}' at position 24: ...edia.ic_add'); }̲ // ❌ 不推荐:分散使...r('app.media.ic_add')); // 到处硬编码 -
响应式尺寸
// ✅ 推荐:使用vp单位
.width(24) // vp,自动适配屏幕密度// ❌ 不推荐:使用px
.width('24px') // 不同屏幕显示大小不一致
总结
图标管理要点:
✅ AppIcons统一管理资源 ✅ 单色图标用SVG+fillColor ✅ 封装AppIcon组件复用 ✅ 遵循命名规范 ✅ 使用vp单位响应式
规范的图标管理提升开发效率!