HarmonyOS7 SVG 与图标:SymbolGlyph + 自定义 SVG 图标库搭建

📅 2026/7/24 21:01:20 👁️ 阅读次数 📝 编程学习
HarmonyOS7 SVG 与图标:SymbolGlyph + 自定义 SVG 图标库搭建

文章目录

      • 前言
      • 图标方案对比
      • SymbolGlyph 系统图标
      • 自定义 SVG 资源
      • Svg 组件绘制
      • 图标库管理方案
      • 动态着色
      • 写在最后

前言

之前做项目图标都是切 PNG,1x 2x 3x 一堆文件塞得资源目录乱七八糟。后来发现 HarmonyOS7 的 SymbolGlyph 直接用系统图标,爽到飞起——直到产品说"这个图标系统里没有,要自定义的"。

好吧,自定义 SVG 图标这条路迟早得走。今天把从系统图标到自定义 SVG 的完整方案梳理一遍。
图标是 UI 里看着不起眼但特别重要的东西。一个 App 动辄几十上百个图标,管理不好就是灾难。HarmonyOS7 给了三条路:

  1. 系统 Symbol 图标—— 零配置,拿来就用
  2. Svg 组件绘制—— 纯代码画,适合简单图形
  3. SVG 资源文件—— 设计师出图,开发引用,最常用

三条路各有利弊,往下看。

图标方案对比

方案适用场景优点缺点
SymbolGlyph系统标准图标零配置、支持动效、自动适配字体粗细只有系统预置图标,约433个
Svg 组件绘制简单几何图形纯代码、动态可控复杂图形代码量爆炸
SVG 资源文件自定义设计图标任意图形、矢量不失真需要管理资源文件
PNG 图片位图/特殊效果兼容性好多分辨率、文件大、不能动态着色

我的建议:系统图标能用的就用,不够的用 SVG 资源,别再用 PNG 了。

SymbolGlyph 系统图标

SymbolGlyph 是 HarmonyOS7 最省心的图标方案,直接引用系统图标库:

@Entry@Componentstruct SymbolGlyphDemo{build(){Flex({direction:FlexDirection.Row,wrap:FlexWrap.Wrap,alignItems:ItemAlign.Center}){SymbolGlyph($r('sys.symbol.ohos_wifi')).fontSize(28).fontColor([Color.Black]).renderingStrategy(SymbolRenderingStrategy.SINGLE)SymbolGlyph($r('sys.symbol.ohos_folder_badge_plus')).fontSize(28).fontColor([Color.Blue]).renderingStrategy(SymbolRenderingStrategy.SINGLE)SymbolGlyph($r('sys.symbol.airplane_fill')).fontSize(28).fontColor([Color.Red]).renderingStrategy(SymbolRenderingStrategy.SINGLE)SymbolGlyph($r('sys.symbol.checkmark_circle')).fontSize(28).fontColor([Color.Black,Color.Green,Color.White]).renderingStrategy(SymbolRenderingStrategy.MULTIPLE_COLOR)}.width('100%').padding(16).justifyContent(FlexAlign.SpaceAround)}}

关键代码讲解:

  • $r('sys.symbol.ohos_wifi')—— 引用系统图标,sys.symbol.是固定前缀,后面接图标标识名
  • .fontSize(28)—— 图标大小跟字号一致,设 28 就是 28x28vp
  • .fontColor([Color.Black])—— 颜色数组,单色渲染时只生效第一个
  • renderingStrategy—— 渲染策略,决定了多颜色怎么分配到图层

渲染策略详解:

策略说明
SINGLE单色,只用第一个颜色
MULTIPLE_OPACITY分层,一个颜色按不同透明度渲染各层
MULTIPLE_COLOR多色,每个图层独立设色

图标名称去哪找?华为开发者官网有完整的 Symbol 图标库,每个图标下面都标着标识名。

自定义 SVG 资源

系统图标不够用的时候,把设计师给的 SVG 文件放进资源目录:

resources/ base/ media/ icon_home.svg icon_search.svg icon_profile.svg

代码中引用:

Image($r('app.media.icon_home')).width(24).height(24).fillColor('#333333').objectFit(ImageFit.Contain)

关键代码讲解:

  • $r('app.media.icon_home')—— 引用 media 目录下的 SVG 文件
  • .fillColor('#333333')——SVG 专用属性,动态修改填充颜色,PNG 不支持这个
  • .objectFit(ImageFit.Contain)—— 保持比例缩放

这里的.fillColor()就是 SVG 比 PNG 强的地方——同一张 SVG 可以在不同场景复用不同颜色。

Svg 组件绘制

如果图标很简单(比如一个圆形、一个箭头),可以直接用 Svg 组件代码画:

@Entry@Componentstruct SvgDrawDemo{build(){Column({space:24}){Shape(){Circle().width(48).height(48).fill('#007DFF')}.width(48).height(48)Shape(){Path().commands('M12 4 L24 24 L0 24 Z').fill('#ff4444')}.width(24).height(24).viewBox({x:0,y:0,width:24,height:24})Shape(){Line().startPoint([0,0]).endPoint([24,24]).stroke(Color.Black).strokeWidth(2)Line().startPoint([24,0]).endPoint([0,24]).stroke(Color.Black).strokeWidth(2)}.width(24).height(24)}.padding(24)}}

关键代码讲解:

  • Shape—— 所有绘制组件的容器,类似 SVG 的<svg>标签
  • Circle()—— 画圆,width/height控制直径,fill填充颜色
  • Path().commands()—— 自定义路径,M移动,L画线,Z闭合。M12 4 L24 24 L0 24 Z画了一个三角形
  • viewBox()—— 定义坐标系,类似 SVG 的 viewBox 属性,确保路径在不同尺寸下比例正确
  • Line()—— 画直线,startPoint/endPoint是起止坐标

复杂图形别硬画,代码维护成本太高。交给设计师出 SVG 文件才是正道。

图标库管理方案

图标一多就得有管理方案,不然东一个西一个,改起来要命。

推荐结构:

resources/ base/ media/ icons/ tab/ icon_home.svg icon_search.svg icon_message.svg icon_profile.svg action/ icon_add.svg icon_delete.svg icon_edit.svg status/ icon_success.svg icon_warning.svg icon_error.svg

命名规范:

  • 前缀按用途分类:tab_action_status_
  • 全小写+下划线,别用驼峰
  • 一个图标一个文件,别把所有图标塞一个 SVG 里

动态着色

图标着色是高频需求——Tab 栏选中变色、暗黑模式变色、主题切换变色。SVG 的.fillColor()就是干这个的:

@Componentstruct TabIconItem{@Propicon:Resource@PropisActive:boolean=falsebuild(){Image(this.icon).width(24).height(24).fillColor(this.isActive?'#007DFF':'#999999').objectFit(ImageFit.Contain)}}@Entry@Componentstruct DynamicColorDemo{@StatecurrentTab:number=0privatetabs=[{icon:$r('app.media.icon_home'),label:'首页'},{icon:$r('app.media.icon_search'),label:'搜索'},{icon:$r('app.media.icon_profile'),label:'我的'}]build(){Row(){ForEach(this.tabs,(tab:Record<string,Resource|string>,index:number)=>{Column(){TabIconItem({icon:tab.iconasResource,isActive:this.currentTab===index})Text(tab.labelasstring).fontSize(12).fontColor(this.currentTab===index?'#007DFF':'#999999').margin({top:4})}.layoutWeight(1).onClick(()=>{this.currentTab=index})})}.width('100%').height(56).padding({top:8,bottom:8})}}

关键代码讲解:

  • .fillColor(this.isActive ? '#007DFF' : '#999999')——核心!选中蓝色,未选中灰色,一个属性搞定
  • @Prop—— 子组件接收父组件传值,单向绑定够用
  • 同一张 SVG 文件,不同颜色渲染——这就是矢量的魅力

写在最后

图标方案就三句话:

  1. 能用 SymbolGlyph 就用,省心省力还带动效
  2. 自定义图标用 SVG 资源文件,fillColor 动态着色是杀手锏
  3. 别用 PNG,都 2026 了还切多分辨率图片,太古老了

图标管理也是工程问题,命名规范、目录结构提前定好,后面维护才不痛苦。