HarmonyOS 鸿蒙Next中图标资源怎样统一管理

HarmonyOS 鸿蒙Next中图标资源怎样统一管理 应用开发需要管理大量图标:

  • 系统图标vs自定义图标
  • 图标统一管理
  • 动态切换图标颜色
  • SVG vs PNG选择
3 回复

解决方案

1. 系统图标使用

@Component
struct SystemIcons {
  build() {
    Row({ space: 16 }) {
      // ✅ 使用系统图标
      Image($r('sys.media.ohos_ic_public_add'))
        .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);
    }
  }
}

2. 自定义图标管理

/**
 * 图标资源管理类
 */
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);
    }
  }
}

3. 可复用图标组件

/**
 * 图标组件
 */
@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
        })
    }
  }
}

4. 带标签的图标按钮

/**
 * 图标按钮组件
 */
@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('点击编辑');
          }
        })
    }
  }
}

5. 动态图标状态

/**
 * 可切换状态的图标
 */
@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);
    }
  }
}

6. 图标大小规范

/**
 * 图标尺寸常量
 */
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' });
  }
})

最佳实践

1. 图标命名规范

// ✅ 推荐命名
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

2. 统一管理

// ✅ 推荐:集中管理
export class AppIcons {
  static readonly ADD = $r('app.media.ic_add');
}

// ❌ 不推荐:分散使用
Image($r('app.media.ic_add'));  // 到处硬编码

3. 响应式尺寸

// ✅ 推荐:使用vp单位
.width(24)  // vp,自动适配屏幕密度

// ❌ 不推荐:使用px
.width('24px')  // 不同屏幕显示大小不一致

总结

图标管理要点:

✅ AppIcons统一管理资源 ✅ 单色图标用SVG+fillColor ✅ 封装AppIcon组件复用 ✅ 遵循命名规范 ✅ 使用vp单位响应式

规范的图标管理提升开发效率!

更多关于HarmonyOS 鸿蒙Next中图标资源怎样统一管理的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


鸿蒙Next中图标资源统一管理通过资源索引实现。在resources/base/media目录下放置图标文件,系统自动生成资源ID。开发者可在代码中通过$r('app.media.icon_name')引用,或在XML中通过@media:icon_name调用。支持多设备适配,可在resources下按屏幕密度(如ldpihdpi)或设备类型(如tablet)建立子目录存放差异化资源。资源类型限定为PNG、JPG、SVG等格式,SVG需经转换工具处理。

在HarmonyOS Next中,图标资源的统一管理主要通过资源管理系统和声明式UI范式实现。

1. 系统图标与自定义图标

  • 系统图标:使用Resource对象引用预置的$r('sys.media.ic_xxx')资源。这些图标由系统提供并维护,风格统一。
  • 自定义图标:将图标文件(如SVG、PNG)放置在工程的resources/base/media/目录下。通过$r('app.media.icon_name')方式引用。建议按模块或功能建立子目录进行分类管理。

2. 统一管理与引用resources/base/element/string.json中可定义图标资源的ID映射,实现逻辑名与资源文件的解耦。在UI组件中,使用资源引用语法:

Image($r('app.media.home_icon'))
  .width(24)
  .height(24)

3. 动态切换颜色 对于SVG图标,可通过fillColor属性动态修改颜色:

Image($r('app.media.ic_setting'))
  .fillColor(Color.Blue) // 动态设置颜色

对于多色图标或需要更复杂控制的情况,建议使用SVG格式并通过组件属性控制。

4. SVG与PNG选择

  • SVG:矢量格式,无损缩放,支持通过属性动态修改颜色、大小。适合界面图标、可交互元素。
  • PNG:位图格式,适合复杂细节图像(如照片、游戏纹理)。需提供不同密度的版本(如base/medium/high/xhigh目录)。

最佳实践

  • 建立统一的图标资源目录结构
  • 界面图标优先使用SVG格式
  • string.json中维护图标ID,提高可维护性
  • 复杂场景可封装自定义图标组件,统一处理尺寸、颜色等属性

这种管理方式能有效提升图标资源的维护效率和应用性能。

回到顶部