[鸿蒙2025领航者闯关]图标资源统一管理

解决方案

  1. 系统图标使用
    @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);
    }
    }
    }

  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);
    }
    }
    }

  1. 可复用图标组件
    /**
  • 图标组件
    */
    @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
    })
    }
    }
    }

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

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

  1. 图标大小规范
    /**
  • 图标尺寸常量
    */
    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 = KaTeX parse error: Expected 'EOF', got '}' at position 24: ...edia.ic_add'); }̲ // ❌ 不推荐:分散使...r('app.media.ic_add')); // 到处硬编码

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

    // ❌ 不推荐:使用px
    .width('24px') // 不同屏幕显示大小不一致
    总结
    图标管理要点:

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

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

相关推荐
贾伟康8 分钟前
【口算王|19】HarmonyOS ArkTS 回归测试实战:覆盖启动、空数据、异常输入和重复点击
软件测试·harmonyos·arkts·回归测试·hypium
贾伟康15 分钟前
【句匠|09】HarmonyOS ArkTS 学习统计实战:汇总正确率、连续学习和薄弱类型
harmonyos·arkts·appstorage·学习统计·本地统计
贾伟康35 分钟前
【口算王|17】HarmonyOS ArkTS 亮暗色与视觉令牌实战:集中颜色、间距和交互状态避免页面割裂
harmonyos·arkts·arkui·深色模式·ui设计
搬砖的kk38 分钟前
从 0 到 1:react-native-screenshot-aware 鸿蒙适配实战(RNOH 0.84)
react native·华为·harmonyos
贾伟康43 分钟前
【口算王|15】HarmonyOS ArkTS 本地状态持久化实战:让保存、删除和页面返回后的数据即时一致
harmonyos·arkts·数据持久化·状态管理·preferences
贾伟康44 分钟前
【口算王|18】HarmonyOS ArkTS 权限与隐私实战:让 module.json5、功能说明和拒绝路径一致
harmonyos·arkts·权限管理·隐私合规·module.json5
ChinaDragonDreamer1 小时前
HarmonyOS:6.0 新增和增强特性
harmonyos·鸿蒙
Georgewu1 小时前
【HarmonyOS AI】 通用文字识别详解
harmonyos
哈__1 小时前
Flutter 3.44.9 + OpenHarmony7:home_widget 三方库桌面服务卡片(FormKit)的应用
flutter·华为·harmonyos
贾伟康1 小时前
【句匠|08】HarmonyOS ArkTS 句库搜索实战:支持关键词、分类和无结果反馈
harmonyos·arkts·分类筛选·学习应用·搜索功能