你的 Material Icons 为什么突然全红?一文教你搭建可持续的 Android 图标管线

14 days ago

你的图标不是“随机坏掉”的。Google 废弃了 androidx.compose.material.icons 相关库,悄悄终结了“自带整包图标、随叫随用”的时代。现在,图标必须像其它有版本的资源一样被明确管理。

如果没有一套图标方案,升级之后你就会看到一片红色引用、随手丢进仓库的 SVG、以及时好时坏的 CI。本指南会教你如何用一套可重复、CI 友好的图标系统,彻底替代已经废弃的 Material Icons 管线,同时兼顾 Compose 和 XML:从 5 分钟应急补丁,到完整自动化、格式选择、性能与 APK 体积优化。

Compose Material Icons 到底废弃了什么,为什么?

直接结论: 你的 Material Icons 失效,是因为老的 androidx.compose.material:material-icons-* 库已被废弃,工具链不再推荐甚至不再自动引入。修复方式是:改用你自管的图标资源(Material Symbols 或自定义矢量),并替换导入,让代码依赖你自己的图标封装,而不是旧的 Icons.* 枚举。

被废弃的是这些预生成的图标集合:

  • androidx.compose.material:material-icons-core
  • androidx.compose.material:material-icons-extended

根据 Compose Material 发布说明,这些库中的图标之所以被废弃,是因为它们已经不再带来性能或价值上的优势(I0484d)。在强调“只打包真正使用的资源、让应用足够精简”的今天,内置一整大包枚举图标,反而和优化目标相冲突。

同时,Google 的设计重心已经转向 Material Design 3 和 Compose Material 3。这些新组件和主题本身是“图标无关”的:你需要自己提供 ImageVector 或 painter,而不是指望库里自带一整套图标。

废弃并不会立刻删光图标,但会打破你的预期:新建模板和官方示例不再使用旧 API,IDE 的自动补全不再强调它们,将来的大版本甚至可能彻底移除。Google 在 AdMob 生命周期文档 等地方给出的通用节奏是:先支持、再标记废弃、几年后正式下线。

结论就是:从现在开始,你必须自己掌控图标管线。不再有“魔法库”帮你打包图标——你要自己决定:哪些图标进包,怎么生成,如何在 CI 中稳定复现。

直接止血:Material Icons 为何失效?5 分钟补丁方案

直接结论: 你升级了 Compose 或使用了新模板,但仍然依赖已经废弃的 androidx.compose.material:material-icons-* 库,或者这些依赖在升级中被移除了,导致图标相关引用全红。应急做法:重新添加或升级这些依赖、补充明确的 Material Symbols 或自定义矢量资源、更新 import,然后 Clean & Rebuild。先让当前 UI 能编译,再规划长期迁移。

5 分钟检查清单

  • 检查依赖:build.gradle 中确认是否还存在:
    • implementation("androidx.compose.material:material-icons-core:…")
    • implementation("androidx.compose.material:material-icons-extended:…")
    如果这些依赖在升级时被删了,相关图标自然会变红。
  • 补齐资源或新依赖:
    • 快速补丁:在库还可用的前提下,暂时把这些依赖加回来,先恢复应用运行。
    • 更优做法:开始把 Material Symbols 或你自己的 SVG 资源导入工程,并以 ImageVector 或 drawable painter 的形式暴露出来。
  • 重新导入图标: 添加依赖或新资源后,更新 import,让图标引用指向正确的类。
  • Clean & Rebuild: 通过 Build → Clean Project 并重新构建,清理陈旧引用和预览缓存。

废弃之后常见的踩坑模式

  • 缺少 import: Icons.Default.Add 变红,是因为不再导入 androidx.compose.material.icons.Icons
  • 升级 Compose 后一片红: 更新 Compose BOM 却顺手删掉图标依赖,会直接导致一堆 unresolved symbol。
  • IDE 不再自动提示: 输入 Icons. 时发现毫无提示,因为背后的图标库已经不在 classpath。
  • 预览报错: 图标引用在编译阶段抛错时,Compose 预览也会一并失败。

最小代码改造示例

旧用法(废弃的图标管线):

import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add

Icon(
  imageVector = Icons.Filled.Add,
  contentDescription = "Add"
)

新用法(你自管的图标):

import androidx.compose.runtime.Composable
import androidx.compose.ui.res.painterResource
// 或者导入你生成的 ImageVector,例如 AppIcons.Add

Icon(
  painter = painterResource(R.drawable.ic_add),
  contentDescription = "Add"
)

如果你用 Kotlin 生成 ImageVector

object AppIcons {
  val Add: ImageVector = /* generated from SVG */
}

Icon(
  imageVector = AppIcons.Add,
  contentDescription = "Add"
)

通过这套 5 分钟补丁,你可以先把 UI 编译恢复。后面的内容会聚焦长期方案:如何搭建一条真正“工程化、自动化”的图标管线。

现代 Android 里的 Material Icons vs Material Symbols

从 Material Icons 进化到 Material Symbols

最初的 Material Icons 只是一批相对简单、单一粗细的图形,比如 homesearchadd 等。Material Symbols 则是它的进化版本:图标数量更多,并引入多条风格轴——粗细(weight)、字重校正(grade)、光学大小(optical size)、填充(fill)等,让你可以更细致地匹配布局与排版。

Google 当前的官方推荐以 Material 3 为中心,Compose Material 3 是其参照实现。你的图标应尽量和这套最新的轮廓与曲线体系保持一致。

对 Android 开发者的实际变化

  • 命名方式: Material Symbols 复用了很多熟悉的名字(例如 home),但会和风格轴组合使用(填充、描边、圆角、锐角等)。
  • 图标家族: 不再是“一整包全收”,而是要有意识地选择 Outlined / Rounded / Sharp 等家族,作为设计决策的一部分。
  • 不再有内置枚举: Compose 不再鼓励类似 Icons.Filled.Home 的自动生成枚举。你应自己通过资源或代码生成管理矢量图标。

数量、多样性与设计影响

Material Symbols 在图标数量和风格组合上远超旧的 Material Icons。定性上,这意味着你可以更精细地匹配字重、间距、密度,而不是“一套图标走天下”。

这些图标的造型也顺应了更大的 UX 趋势:更圆润的曲线、更统一的圆角、更高的易读性。Google Play 在应用图标上做过的大约 30% 曲率调整等讨论,也都指向一个方向:整体视觉趋向柔和、亲近。

对 Android 开发者来说,重点是:Material Symbols 会是中长期默认选项。你的图标工具链应该围绕它做优化,但又保持足够灵活,以便随时接入品牌图标或第三方图标包。

在 Compose 项目中手动导入 Material Symbols

直接结论: 从 Material Symbols / Google Fonts 下载 SVG,通过 Android Studio 的 Vector Asset 工具转换为 VectorDrawable,或者通过代码生成转换为 ImageVector,把资源放入 res/drawable,最后在 Compose 中通过 painterResource()ImageVector 字段引用。

分步操作流程

  • 下载 SVG:
    • 打开 Google Fonts 或 Material Symbols 官网的图标集合。
    • 选择风格(filled / outlined / rounded / sharp),下载对应 SVG。
  • 将 SVG 转换为 VectorDrawable 或 ImageVector:
    • 使用 Android Studio Vector Asset:
      • 右键模块 → New → Vector Asset
      • 选择 Local file (SVG),导入下载的图标文件。
      • 设置名称,例如 ic_nav_home,点击完成。会生成 VectorDrawable XML。
    • 使用 Kotlin ImageVector.Builder(代码生成):
      • 利用脚本/工具解析 SVG,生成带 ImageVector.Builder 路径数据的 Kotlin 文件。
      • 放入 iconsui 包,通过 object AppIcons 暴露。
  • 存放图标:
    • Compose 与 XML 同时使用: 将生成的 VectorDrawable XML 放入 res/drawableres/drawable-anydpi,即可在 View 与 Compose 中共用。
    • 仅 Compose: 如果不考虑 XML,可以直接用生成的 ImageVector 代码,但共用 VectorDrawable 通常更简单。

Compose 最小使用示例(VectorDrawable)

@Composable
fun HomeIcon(contentDescription: String? = null) {
  Icon(
    painter = painterResource(R.drawable.ic_nav_home),
    contentDescription = contentDescription
  )
}

Material 3 Compose 组件(见 compose-material3)对图标本身没有要求,比如 NavigationBarItemIconButton 只是接收一个图标 Composable;这个图标是 VectorDrawable 还是 ImageVector,完全由你决定。

XML/View 应用的手动导入:从 SVG 到 VectorDrawable

传统非 Compose 图标管线

  • 下载 SVG: 从 Material Symbols 或其它提供方获取 SVG 图标。
  • 用 Vector Asset 向导转换:
    • 右键模块 → New → Vector Asset
    • 选择 SVG 文件,设置名称,例如 ic_action_share
    • 完成后会在 res/drawableres/drawable-anydpi 下生成对应的 .xml VectorDrawable。
  • 在布局中引用:
    • <ImageView
        android:layout_width="24dp"
        android:layout_height="24dp"
        android:src="@drawable/ic_action_share" />
    • 或者使用 AppCompat 版本提高兼容性:
      <androidx.appcompat.widget.AppCompatImageView
        android:layout_width="24dp"
        android:layout_height="24dp"
        app:srcCompat="@drawable/ic_action_share" />

API 等级注意事项

VectorDrawable 在现代 Android 上有完整支持,通过 AndroidX AppCompat 的回溯支持,即便在旧版系统上,只要用 app:srcCompat 和相关库,也能正常显示简单图标。矢量图标不需要为每个密度单独准备位图,可以显著减少 APK 体积与维护成本。

这个“下载 SVG → 转 VectorDrawable → 提交 XML”流程,正是你之后要自动化的那一步——用脚本或 Gradle Task 代替手动点 IDE 向导。

批量下载 & 自动导入:用图标管线替代手动拖文件

直接结论: 当然可以。你可以维护一份“宣言式”的图标清单,然后通过 Gradle Task 或外部脚本批量下载 Material Symbols(或其它来源)的 SVG,转换为 VectorDrawable 或 ImageVector 代码,并写入约定目录。在本地和 CI 都执行这一步,就能得到可重复的图标产出。

自动化图标管线的核心原则

  • 声明式图标配置:
    • 用 JSON / YAML / Kotlin 对象列出图标信息,例如:name、source URL、style(filled/outlined)、目标文件名等。
    • 这份配置是应用“究竟打包哪些图标”的单一事实来源。
  • 自动化脚本:
    • 可以实现为 Gradle Task、Kotlin CLI 工具、Python 脚本或 Node 工具。
    • 读取配置,向 Material Symbols / Google Fonts 或其它 API 拉取 SVG。
  • 转换步骤:
    • XML/View:将 SVG 转为 VectorDrawable XML。
    • 仅 Compose:将 SVG 转为包含 ImageVector.Builder 路径的 Kotlin 代码。
  • 确定性的文件放置:
    • 将生成的 XML 写入 src/main/res/drawable-anydpi/res/drawable/
    • 将生成的 Kotlin 源文件写入 build/generated/source/icons/ 或类似目录,并加入 sourceSets。

这套思路和 Android 的整体废弃生命周期非常一致(支持 → 废弃 → 下线,见例如 AdMob 废弃文档):当 Google 废弃一个库或更换图标格式时,你只需要更新管线并重新生成资源,而不必满项目手动找图标替换。

示意性工作流

  • Gradle 任务: ./gradlew generateIcons
  • 图标配置 → 脚本: 任务读取 icons.json 并调用 CLI 工具。
  • CLI 工具:
    • /icons/src 目录下载或更新 SVG。
    • 转换为 VectorDrawable XML,写入 app/src/main/res/drawable-anydpi
    • 可选:生成带 ImageVector 映射的 AppIcons.kt
  • CI 集成:
    • CI 在 assemble 前执行 generateIcons
    • 如果生成结果与仓库不一致,则构建失败。

后面的内容会继续讨论:采用哪些格式、放在哪些目录、如何命名和做版本管理,才能让图标完美融入这条自动化管线。

Android 上图标格式的最佳实践:SVG、VectorDrawable、ImageVector、PNG

直接结论: 用 SVG 作为设计源文件,用 VectorDrawable XML 作为 XML/View 的运行时格式,在 Compose 中使用 ImageVector 或从 @drawable 加载的 painterResource。PNG 留给复杂多色插画或历史兼容。矢量资源统一放在 res/drawable(-anydpi),颜色通过主题 tint 控制,而不是写死在图标里。

各格式的优劣势

  • SVG(源文件):
    • 利于设计协作与版本管理。
    • Android 运行时不会直接使用 SVG。
    • 适合作为 /icons 目录下的“唯一真源”,供脚本读取处理。
  • VectorDrawable XML(View 端矢量运行时):
    • 体积小、可缩放、由 Android 原生支持。
    • 单文件覆盖多密度,不像 PNG 要准备多套。
    • 是 XML/View UI 的主要图标格式。
  • ImageVector(Compose 矢量):
    • 用 Kotlin 表达矢量路径。
    • 与 Compose 的 Icon()Image() API 无缝配合。
    • 可以由 SVG 直接生成,或通过 painterResource() 间接使用 VectorDrawable。
  • PNG(位图):
    • 适合复杂多色插画、照片或难以矢量化的品牌图。
    • 体积重,通常需为多种分辨率分别导出。
    • 缩放不当容易发糊。

一般来说,用 SVG & VectorDrawable 比维护多密度 PNG 体积更小,但具体大小仍取决于路径复杂度与细节。

矢量还能帮你控制 APK 体积:无需为每个密度单独打包资源,可以在不同主题(浅色/深色)之间通过 tint 复用同一图标。Compose Material 3 本身就是围绕矢量资源和动态主题设计的,自然和矢量图标最搭。

各类文件应放在哪里

  • SVG 源文件: /icons/src/*.svg(不随 APK 发布)。
  • VectorDrawable XML: app/src/main/res/drawable-anydpi/drawable/
  • 生成的 ImageVector 源码(可选): app/build/generated/source/icons/(或单独的 :icons 模块)。
  • PNG(兜底或复杂图): 仅在无法使用矢量时,放入 res/drawable-xxxhdpi/ 等密度目录。

图标放哪儿:资源目录、模块拆分与命名规范

推荐资源位置

  • VectorDrawable XML 图标:
    • 优先放在 app/src/main/res/drawable-anydpi/,或退一步用 res/drawable/
  • 夜间/密度专用资源:
    • 仅在必要时使用 res/drawable-nightdrawable-xxhdpi 等目录,一般用于 PNG 兜底或强品牌图形。
  • 共享图标模块:
    • 在大型项目中,单独创建 :design:icons:core:ui 模块。
    • 将共享的 VectorDrawable 和生成的 AppIcons.kt 放在此模块。
    • 其它业务模块依赖该模块,统一使用图标。

命名规范建议

  • 按场景前缀:
    • 导航图标:ic_nav_home
    • 操作图标:ic_action_share
    • 品牌图标:ic_logo_brand
  • 除非必要,不要把风格写死在名字里:
    • 更推荐 ic_nav_home,而不是 ic_nav_home_filled,除非你同时打包多种风格。
    • 如果确实存在多风格,可用后缀:ic_nav_home_outlinedic_nav_home_filled

为 Compose 提供统一 Kotlin 访问层

用一个 Kotlin 对象集中暴露图标,保证全局用法统一:

object AppIcons {
  val Home = painterResource(R.drawable.ic_nav_home)
  val Share = painterResource(R.drawable.ic_action_share)
}

@Composable
fun NavHomeIcon(contentDescription: String?) {
  Icon(
    painter = AppIcons.Home,
    contentDescription = contentDescription
  )
}

有了统一命名和集中访问层,你就能轻松搜索图标使用处、重命名或通过脚本把配置映射到文件名。如需更换整套图标(例如从 Material Symbols 切到品牌图标包),只需要改 AppIcons,而不是到处手动改调用。

Compose vs XML:图标着色与主题化

直接结论: 在 Compose 中,用 MaterialTheme.colorScheme 的颜色(如 onSurface)通过 Icon() 对图标 tint。在 XML 中,用 android:tintapp:tint 结合主题属性(如 ?attr/colorOnSurface)。避免硬编码颜色,对多色图标尽量不做 tint。

Compose 中的图标着色

  • Icon() 结合主题色进行 tint:
    @Composable
    fun ThemedIcon(painter: Painter, contentDescription: String?) {
      Icon(
        painter = painter,
        contentDescription = contentDescription,
        tint = MaterialTheme.colorScheme.onSurface
      )
    }
  • 对单色图标,tint 是适配浅色/深色主题的最佳方式。
  • 对多色图标(如国旗、Logo),尽量不要使用 tint,而是在路径中适度编码必要的颜色。

Compose Material 3 完全围绕 colorScheme 驱动颜色,图标 tint 与它保持一致,可以自然适配动态配色。

XML/View 中的图标着色

  • 通过 android:tint 或 AppCompat 的 app:tint
    <androidx.appcompat.widget.AppCompatImageView
      android:layout_width="24dp"
      android:layout_height="24dp"
      app:srcCompat="@drawable/ic_nav_home"
      app:tint="?attr/colorOnSurface" />
  • 优先使用主题属性(如 ?attr/colorOnSurface?attr/colorPrimary),而非硬编码十六进制色值。
  • 多色 drawable 一般不做 tint,除非是有意的风格效果。

稳定的 tint 策略是整体视觉质量的重要组成部分。包括 PocketGamer 在内的一些研究指出,优化商店图标可以带来接近 25% 的用户增量——虽然数据针对的是商店图标,但同样说明图标处理对感知质量的影响极大,应用内部图标也不例外。

APK 体积与性能:图标“太多”算多少?

直接结论: 默认使用矢量图标(VectorDrawable / ImageVector),只打包你真正使用的图标,避免重复风格变体。集中管理图标规格,让资源压缩(Resource Shrinking)去除未使用资源,并在 CI 中集成图标检查,让 APK 保持精瘦且性能可控。

APK 体积考虑

  • VectorDrawable:
    • 对简单图标体积很小。
    • 增加几十甚至上百个简单图标,相比多密度 PNG,对整体体积影响通常有限。
  • PNG:
    • 每个图标可能需要多套密度资源。
    • 数量一多、分辨率一高,很容易占掉几 MB。
  • 风格变体:
    • 最浪费的是同时打包 filled + outlined + rounded + sharp,但设计只用了一两种。
    • 尽量选择一个主力家族(例如 Filled 或 Outlined),并坚持到底。

运行时性能权衡

  • 矢量(ImageVector / VectorDrawable):
    • 需要在运行时由 GPU/CPU 光栅化路径。
    • 在现代设备上,对普通 UI 图标的开销几乎可以忽略。
    • 缩放和 tint 几乎是“设计免费”的,无需额外资源。
  • PNG:
    • 由于预先光栅化,绘制速度很快。
    • 适合超复杂插画或重度动画的场景。
    • 存储与网络成本高。

Google 废弃预生成 Material 图标库的原因之一,就是其体积不再带来性能收益(见 compose-material 发布说明)。从“整包内置”转向“只打包实际使用的图标”,更符合现代体积与性能目标。

实用优化策略

  • 中心化图标规格: 用一份配置文件(JSON/Kotlin)定义所有打包图标,没有条目就不生成、不打包。
  • 未用图标检测: 在构建阶段做 lint 或脚本检查,对比规格文件、资源和实际引用,标记“僵尸图标”。
  • 资源压缩: 开启 R8/ProGuard 和 Resource Shrinking,未使用的矢量也可以被移除。
  • 仅在必要时做性能 Profiling: 对绝大多数产品,图标不会是主要性能瓶颈;除非有极复杂矢量并在大尺寸频繁绘制,否则无需刻意微调。

ImageVector vs PNG 的性能基准会随设备、图标复杂度与使用场景变化。与其追求“万能数字”,不如把握几个默认原则:图标用矢量,复杂插画用 PNG,严格控制进包图标数量。

图标版本管理、兼容与回滚策略

图标不再是藏在库里的“匿名二进制文件”,而是你设计系统的一部分,会随着 Material 3 等更新、甚至 Google Play 图标曲率调整等趋势一起演进。你需要可控、可回滚的更新方式。

版本管理策略

  • 为 SVG 建立源目录:
    • 在仓库根目录维护一个 /icons 目录。
    • 所有原始 SVG 放在这里,自动化工具据此生成运行时资源。
  • 视觉变更日志:
    • /icons/CHANGELOG.md 记录图标增加、删除和重大风格变更。
    • 把这些变更和功能发布或版本号关联起来。
  • 语义化图标版本:
    • 在图标规格中加入一个高层版本号,例如从老 Material Icons 切到 Material Symbols 时,将版本改为 "version": "2.0"
    • 用 Git 标签(如 icons-v1icons-v2)标记大改版。

兼容与回滚

  • Legacy 目录:
    • 替换图标时,将旧版本移到 /icons/legacy
    • 在规格或文档中维护旧 → 新的映射关系。
  • 声明式回滚:
    • 回滚的含义是:把图标规格文件回退到某个 Git 版本,然后重新执行生成脚本。
    • 避免手动删改 drawable/XML 文件——所有生成结果都应完全由规格文件驱动。
  • 对齐 Android 生命周期:
    • 就像 Android 框架从“支持 → 废弃 → 下线”(详见 AdMob 废弃文档)一样,对图标集也采取同样节奏。
    • 在团队内发布“图标 v1 已废弃、v2 为当前版本”等声明,并给出移除旧版本的时间窗口。

CI/CD 集成:让图标可复现、可审查

适合 CI 的图标工作流

  • 提交规格文件:
    • icons.json(或 Kotlin/JSON 配置)放入版本控制。
    • 任何图标变更从修改这份规格开始。
  • 生成步骤:
    • 新增 Gradle 或 CLI 任务(例如 generateIcons),读取规格并生成所有运行时资源:VectorDrawable XML,以及可选的 AppIcons.kt(内含 ImageVector 引用)。
    • 生成任务必须是确定性的:同样的规格与 SVG,输出必须完全一致。
  • CI 集成:
    • 在 CI 上,于 assemble 之前执行 generateIcons
    • 如果命令执行后工作区出现未提交变更(例如用 git diff --exit-code 检查),则构建失败,防止“忘记提交生成文件”。

“提交生成文件” vs “仅在 CI 生成”

  • 提交生成结果:
    • 优点: 代码审查时可以直接 diff 向量 XML / Kotlin;即使上游图标源变动或离线,构建仍然可复现。
    • 缺点: 仓库文件增多,需要团队自律保持生成文件与规格同步。
  • 只在 CI 生成:
    • 优点: 仓库更干净,只保存 SVG 和规格。
    • 缺点: 依赖外部图标 API 的稳定性;视觉变更不容易仅靠代码 diff 审查,可能还需要截图对比。

推荐 CI 步骤

  • 在流水线早期执行 ./gradlew generateIcons
  • 通过 git diff --exit-code 等命令检查是否存在未提交变更,确保开发者按要求提交生成资源。
  • 可选:对关键页面做截图回归测试,比较新旧版本的图标表现。

就像 PocketGamer 报道的研究指出:优化应用商店图标能大幅提升用户获取,保持内外一致、稳定的图标表现,对老用户的信任感和品牌质感同样重要。CI 把图标纳入“可重复、可审查”的流程,是这件事的基础设施。

迁移常见问题答疑(PAA 角)

为什么我的 Material Icons 在 Android Studio 里突然失效?如何修复?

你的项目依赖于已经废弃的 androidx.compose.material:material-icons-* 库。升级 Compose 或改用新模板后,IDE 可能不再自动添加或推荐这些依赖,导致 Icons.Filled.* 无法解析。你可以暂时重新添加这些依赖,但更推荐尽快引入 Material Symbols(以 VectorDrawable / ImageVector 形式),并用自己的 AppIcons 封装替换原有引用。

如何在 Compose 项目中手动导入 Material Symbols / Material 图标?

从 Material Symbols / Google Fonts 下载 SVG,用 Android Studio 的 Vector Asset 向导生成 VectorDrawable,或用生成工具生产 Kotlin 中的 ImageVector 代码,把资源放到 res/drawable 或生成目录,在 Compose 里通过 painterResource()AppIcons 对象引用即可。

能否批量下载 Material 图标并自动导入 Android 项目(VectorDrawable / ImageVector)?

可以。先定义一份 JSON/Kotlin 规格列出图标名和来源,再写一个 Gradle/CLI 脚本,从 Material Symbols 或其它服务批量下载 SVG,转换为 VectorDrawable XML 或 ImageVector 源码,写入 res/drawable 和生成源码目录,并在本地与 CI 中统一执行这一任务。

在 Android(Compose vs XML)中,图标的最佳文件格式、存放位置与着色实践是什么?

用 SVG 作为设计/源文件;在 res/drawable(-anydpi) 中使用 VectorDrawable XML 作为运行时资源;Compose 中使用 ImageVectorpainterResource(@drawable);PNG 专供复杂插画。着色使用 Material 3 主题色(Compose 中用 colorScheme,XML 中用主题属性),不要写死颜色,以便轻松支持暗色与动态主题。

如何组织、版本管理与优化图标资源,以减少 APK 体积并适配 CI/CD?

在仓库中建立 /icons 目录保存 SVG,将所有图标信息收敛到一份规格文件,并通过脚本自动生成矢量资源。只打包规格中列出的图标,依赖 Resource Shrinking 清理未使用资源;统一命名(如 ic_nav_*ic_action_*),在 CI 中集成图标生成与一致性检查,为重大图标版本打标签以便快速回滚。

记住,Compose Material 图标库被废弃,是因为它们已经不再有性能优势(见 compose-material),而 Material 3 才是当前官方设计目标(见 compose-material3)。

端到端迁移清单:从废弃 Material Icons 到现代图标系统

  • 审计现有图标: 用 IDE 搜索和 Gradle 依赖报告列出所有图标用法(Compose 里的 Icons.* 和 XML 里的 @drawable)。
  • 移除废弃依赖: 逐步替换 androidx.compose.material:material-icons-corematerial-icons-extended,改用自有图标资源。
  • 选择主力图标集: 确定主要采用 Material Symbols、品牌图标还是混合方案,并明确选用的风格家族(如 Filled)。
  • 定义图标规格文件: 创建 JSON/Kotlin 配置,列出每个图标的 name、style、source URL/path。
  • 确定格式策略: 用 SVG 作为源,VectorDrawable XML 与 ImageVector/painterResource 作为运行时,PNG 只在矢量无法胜任时使用。
  • 统一位置与命名: 将 VectorDrawable 放入 res/drawable-anydpi,在模块化项目中考虑单独的 :design:icons 模块,统一使用类似 ic_nav_home 的命名规范。
  • 实现自动化: 写脚本或 Gradle Task,基于规格文件下载 SVG、转换为 VectorDrawable/ImageVector,并写入正确目录。
  • 集成进 CI: 在构建前执行图标生成任务,如果规格与生成资源不一致则构建失败,可选增加视觉回归测试。
  • 文档化主题规则: 明确 Compose 与 XML 中各类图标的主题色/tint 使用规范,与 Material 3 的 light/dark/dynamic 配色方案对齐。
  • 规划版本与回滚: 为图标发布打标签(如 icons-v1、icons-v2),将旧图标放入单独目录,并通过“回退规格 + 重新生成”的方式实现回滚。

沿着这份清单走下去,你可以从“图标突然全红”走向一套纪律严明、可持续演进的图标系统。

面向未来:为下一次图标或 UI 废弃提前做好准备

从这次 Material Icons 废弃学到什么?

  • 避免依赖超大隐式库: Compose Material 图标库的废弃(见 compose-material)说明:当维护方为了性能与体积考虑精简内容时,依赖“巨无霸内置资源”往往会被动挨刀。
  • 把图标当代码对待: 它们应该像 API Client 或生成 Model 那样,从规格与源文件出发,自动生成、可审查、可重复。
  • 以当前规范为锚,但保持可插拔: 以 Material 3 作为当前基线(见 compose-material3),同时让管线对品牌图标或未来新图标集保持可替换性。

如何提前捕捉未来变更

  • 关注 Android Release Notes 与 “Now in Android” 系列文章,例如 Now in Android,获取关于 drawable、主题或 UI 变更的早期信息。
  • 定期安排设计回顾(例如每年一次),评估当前图标是否仍符合最新规范与品牌方向。

连接更广义的图标优化思路

行业资源如 ASOMobile 的 App 图标趋势与最佳实践,以及 PocketGamer 的 App 图标优化调研,都强调图标对获客与感知的重要性。虽然这些文章主要研究商店图标,但同样的原则在应用内也成立:统一、高质量的图标,会显著提升产品的专业感与一致性。

从废弃的 Material Icons 迁移,实际上是一次“趁机重构图标架构”的机会。通过搭建精瘦、自动化的图标管线,你不仅解决了当前的“图标突然全红”问题,也为未来的一次次设计、框架和库的变化,预先铺好了弹性空间。

5 天落地蓝图

可以按下面这份 5 天蓝图来推进(用线性清单而非表格,方便在手机上阅读与执行):

第 1 天 – 审计与依赖清理

  • 扫描代码库中所有 Icons.* 与 Material Icons 的使用。
  • 运行 Gradle 依赖报告,查找 androidx.compose.material:material-icons-*
  • 梳理所有图标入口(包括 Compose 与 XML)。

第 2 天 – 定义规格与格式策略

  • 按功能/页面列出真正需要的图标。
  • 选定主力图标家族(Material Symbols vs 品牌/自定义)。
  • 定义图标规格文件(name、style、source URL/path)。
  • 确认格式策略:SVG 为源,VectorDrawable/ImageVector 为运行时。

第 3 天 – 实现自动化

  • 创建一个简单脚本或 Gradle Task,能读取图标规格。
  • 实现“下载 SVG → 转为 VectorDrawable/XML 或 ImageVector → 写入 res/drawable/生成源码”的流程。
  • 在本地多次运行,确认输出幂等且稳定。

第 4 天 – 接入 CI 与主题体系

  • 在 CI Pipeline 中,将图标生成任务接入到构建之前。
  • 增加检查步骤,如果规格与生成资源不一致则构建失败。
  • 文档化并强制执行 Compose 与 XML 的图标 tint/主题规则。

第 5 天 – 命名、模块化与完整迁移

  • 完善命名规范(如 ic_nav_*ic_action_*ic_logo_*)。
  • 将图标资源与访问层抽到共享模块(如 :design:icons)。
  • 做一次全面迁移,将所有废弃的 Material Icons 引用替换为新的 AppIcons
  • 为这次图标版本打 Tag(如 icons-v1),并记录清晰的回滚方案。
你的 Material Icons 为什么突然全红?一文教你搭建可持续的 Android 图标管线 | AI Solopreneur