
做移动应用开发这几年我越来越觉得图标和应用名这两样东西看起来是最不起眼的“小活”真做起来全是门道。很多项目在启动阶段都是直接把默认机器人图标扔上去先跑通功能等到要上架或者给客户演示了才想起来要定制图标、改应用名结果各种尺寸不匹配、图形被系统裁掉、桌面不刷新、渠道包对不上名……一堆问题全挤在交付前最后一周。这篇文章把我这些年踩过的坑、验证过可行的方案系统梳理一遍从最基础的替换默认图标和修改应用名一直讲到自适应图标、动态图标、多渠道多环境下自动改名的进阶玩法覆盖 Android 和 iOS 两个平台。适合刚入门的移动应用开发者当操作手册也适合已经上线过但想把品牌细节打磨到位的团队拿来查漏补缺。1. 先搞清楚图标和应用名在系统里到底是什么1.1 一个容易被误会的常识以前带实习生的时候我问过一个问题桌面上的应用图标来自哪里好几个人想都不想就说“App 里的一张图片”。这说法对但太笼统了。在 Android 和 iOS 里桌面图标和应用名其实都不是 App 运行时“画”上去的而是系统在安装应用时从安装包里读取特定资源文件交给桌面启动器Launcher去展示的。也就是说你换图标这件事本质上是换安装包里的资源文件或者换个方式“欺骗”系统读取另一个资源。这带来两个直接影响第一次修改图标后需要重新安装或至少让桌面重新扫描否则显示的还是旧图标。图标文件可以不止一个系统会按屏幕密度、系统版本、主题模式去选不同的文件。这也是为什么你只放一张图进去有些手机上会发虚有些手机会被硬生生裁掉一圈。理解了这一点后面所有的操作就都顺理成章了。1.2 图标尺寸和文件名背后的规则Android 从很早开始就用mipmap目录来放图标而不是drawable。很多人只知道“照抄别人的目录结构”不清楚为什么。简单说系统启动器会按设备的屏幕密度去mipmap-*目录里找对应分辨率的图标。如果没有匹配的密度系统会就近拉伸或缩小结果就是清晰度不理想。Android 的常见密度档位和图标建议尺寸是这样的目录屏幕密度建议图标尺寸mipmap-mdpi160dpi48x48mipmap-hdpi240dpi72x72mipmap-xhdpi320dpi96x96mipmap-xxhdpi480dpi144x144mipmap-xxxhdpi640dpi192x192注意这里说的是“系统启动器最舒服的尺寸”。如果你用drawable目录Android 会把图片当作普通资源处理缩放逻辑没问题但部分定制系统在生成快捷方式或主题图标时只认mipmap里的图标容易出兼容性问题。所以我一直建议应用图标老老实实放在mipmap目录里。iOS 这边图标是放在Assets.xcassets里的AppIcon.appiconsetXcode 会用一套类似“单图多尺寸”的方式自动生成。你只要提供一张 1024x1024 的主图Xcode 就能按不同设备自动裁出需要的规格。看起来比 Android 省事但 iOS 有个硬规则图标不能有透明通道不能带 alpha 信息否则上传 App Store 会被直接打回这个后面细说。2. 基础定制替换默认图标和名称的正确姿势2.1 Android按密度准备图标并替换 mipmap 文件新手最容易踩的坑是把一张 1024x1024 的大图直接丢进所有mipmap目录。这样做不是不能跑但你在一台 xxhdpi 的机器上看到的图标会被系统压缩到 144x144 再缩放遇到圆角遮罩或模糊算法边缘就会发虚。正确的操作分三步第一步准备源图。我一般用矢量图或者至少 512x512 以上的 PNG 作为母版。确保主体内容在画面中央四周留出至少 10% 的空白因为 Android 8.0 以后有自适应图标会做前景缩放太贴边的图形会被裁掉。第二步导出各密度尺寸。用 Sketch、Figma、Photoshop 都可以或者用免费的在线工具把 512 的图标一键导出成 48、72、96、144、192 五档。导出时强调两点PNG 格式、不要带圆角。Android 系统版本不同桌面图标的圆角规则不一样你最好给系统一张方方正正的图让系统自己决定怎么裁这样最稳妥。第三步替换文件。在app/src/main/res/目录下用你的ic_launcher.png替换mipmap-mdpi、mipmap-hdpi、mipmap-xhdpi、mipmap-xxhdpi、mipmap-xxxhdpi里对应的文件。同时如果你用了roundIcon特性记得把mipmap-*里的ic_launcher_round.png也一起换掉。然后检查AndroidManifest.xmlapplication android:iconmipmap/ic_launcher android:roundIconmipmap/ic_launcher_round android:labelstring/app_name确认icon和roundIcon都指向了你的新资源重新编译安装就能看到效果。2.2 iOS通过 Assets 替换图标iOS 这边简单不少。打开Assets.xcassets选中AppIcon把 1024x1024 的主图拖进去Xcode 会自动处理其他尺寸。需要注意的是如果你要支持老版本 iOS 或者不同的 iPad 模式会有多个格子需要填充建议都填上特别是 20pt、29pt、40pt、60pt、83.5pt 这几个规格。提交 App Store 前记得检查这张图的 alpha 通道。你可以用预览工具打开图片如果导出时选了带透明度系统会提示“App icon cant be transparent or contain an alpha channel”这种报错我见过太多次了。解决办法是导出时把背景合并成纯色不要保留透明像素。这里多提一句iOS 的图标系统会自动帮你加圆角所以你提供的图不需要自己做圆角甚至最好不要做否则系统再加一层圆角后边角会显得非常奇怪观感一下子业余了。2.3 应用名修改的正确入口应用名在 Android 里是android:label属性在 iOS 里是Info.plist里的CFBundleDisplayName。但直接改这两个地方并不是最优雅的方式尤其是在要支持多语言、多渠道的时候。Android 的标准做法是AndroidManifest.xml里不写死文字而是引用字符串资源application android:labelstring/app_name然后在res/values/strings.xml里定义string nameapp_name我的应用/stringiOS 的标准做法类似Info.plist里的CFBundleDisplayName保持默认的英文然后通过InfoPlist.strings做本地化CFBundleDisplayName 我的应用;这样系统会根据用户的语言环境自动显示对应的应用名。你直接改Info.plist也能生效但一旦要支持多个语言就只能在Xcode的InfoPlist.strings里逐个语言加键值不要所有内容全写死在一处。3. 进阶玩法自适应图标和动态图标3.1 Android 自适应图标的原理和适配Android 8.0API 26以后系统引入了自适应图标。简单讲它把图标拆成了前景层和背景层系统可以根据桌面主题、手势、屏幕形状对图标套用不同的遮罩。自适应图标的开发尺寸是 108x108dp其中前景层只有在中间 72x72dp 的安全区域内才保证不被裁切。所以你在做设计时核心图形一定要放在中间 72dp 以内四周 18dp 是用来“呼吸”和配合系统动效的。在res/mipmap-anydpi-v26/ic_launcher.xml里标准的配置长这样adaptive-icon xmlns:androidhttp://schemas.android.com/apk/res/android background android:drawabledrawable/ic_launcher_background / foreground android:drawabledrawable/ic_launcher_foreground / /adaptive-icon背景层可以是一张纯色图也可以是渐变或图案前景层通常是一个 PNG 或者 VectorDrawable。注意前景层和背景层都是 108x108dp 的画布但内容要分别按照安全区来设计。为什么我强调这个因为很多团队只提供一张 512 的方图系统在运行 Android 12 的机型上会直接把图标套进一个带圆角的背景里如果主体内容太大四个角就会被切掉文字或 Logo 边缘硬生生少一块特别难看。实测下来最省事的方案是背景层用纯色或简单渐变前景层单独抠出 Logo 主体保证 Logo 在安全区内。这样不管桌面是圆形遮罩、圆角方形还是默认方形都能获得不错的视觉效果。3.2 Android 动态图标同一应用多个图标来回切换动态图标需求通常来自几个场景应用有“白天/夜间”模式想体现在图标上或者运营活动期间想换个节日皮肤又或者 Chat 类应用想用角标展示未读状态。Android 上实现动态图标的主流方案是使用Activity-alias。思路是在AndroidManifest.xml里给同一个 Activity 配置多个入口每个入口对应不同的图标和应用名然后用PackageManager.setComponentEnabledSetting()动态切换启用哪个入口。具体操作是这样的。首先定义一个真正干活的入口 Activityactivity android:name.MainActivity android:exportedtrue intent-filter action android:nameandroid.intent.action.MAIN / category android:nameandroid.intent.category.LAUNCHER / /intent-filter /activity注意这个入口里的intent-filter可以不加LAUNCHER因为我们要用 alias 来当启动入口。然后定义多个别名activity-alias android:name.MainActivity_Day android:targetActivity.MainActivity android:iconmipmap/ic_launcher_day android:labelstring/app_name_day android:enabledtrue android:exportedtrue intent-filter action android:nameandroid.intent.action.MAIN / category android:nameandroid.intent.category.LAUNCHER / /intent-filter /activity-alias activity-alias android:name.MainActivity_Night android:targetActivity.MainActivity android:iconmipmap/ic_launcher_night android:labelstring/app_name_night android:enabledfalse android:exportedtrue intent-filter action android:nameandroid.intent.action.MAIN / category android:nameandroid.intent.category.LAUNCHER / /intent-filter /activity-alias切换的时候调用PackageManagerPackageManager pm getPackageManager(); ComponentName day new ComponentName(this, com.example.app.MainActivity_Day); ComponentName night new ComponentName(this, com.example.app.MainActivity_Night); pm.setComponentEnabledSetting( night, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP ); pm.setComponentEnabledSetting( day, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP );这套方案有个关键问题必须处理切换状态不会自动持久化。你这次启动了 Night 图标下次开机系统可能又启用 Day 图标。正确做法是在每次切完以后把当前状态写进SharedPreferences然后在Application启动时读取并重新调用切换逻辑。另外一个隐藏得很深的坑部分国产 ROM 桌面会缓存图标。切换完以后桌面可能要先等几秒甚至重启才能看到变化。这不是你代码的问题别慌。实测下来小米、华为的机器有时候要等 5 到 10 秒或者手动重进一次桌面。3.3 iOS 动态换图标官方支持的 Alternate IconsiOS 从 10.3 开始就支持在应用内动态更换图标了但知道的人不多。它的能力比 Android 这边规范得多不需要搞 Activity-alias系统原生支持。你需要在Info.plist里添加一组CFBundleAlternateIcons例如keyCFBundleIcons/key dict keyCFBundleAlternateIcons/key dict keyNight/key dict keyCFBundleIconFiles/key array stringAppIconNight/string /array keyUIPrerenderedIcon/key false/ /dict /dict /dict然后在代码里调用UIApplication.shared.setAlternateIconName(Night) { error in if let error error { print(切换失败\(error.localizedDescription)) } }想恢复默认图标时传nil即可。注意几个细节备选图标必须提前放进 App Bundle 里不能从网络下载你切过去以后用户会看到系统弹窗提示“已更换主屏幕图标”这是正常现象如果用户在 iOS 的“编辑主屏幕”模式里切换可能会失败需要在正常状态下调用。iOS 这套方案唯一让我觉得麻烦的是所有备选图标都要满足AppIcon的要求不能透明、必须包含所有需要的尺寸。只能老老实实在Assets.xcassets里多建几套AppIcon别想着图省事。4. 应用名的花样定制从多语言到多环境自动改名4.1 多语言应用名用资源文件实现自动切换应用名跟语言绑定这个需求在出海应用里很常见。国内你好端端一个中文名到了海外用户手里显示出来还是中文那就不够本地化。Android 的做法是在res/values-en/strings.xml里加一个同名资源string nameapp_nameMy App/string系统会按用户的语言自动选择values-en里的字符串。iOS 同理在InfoPlist.strings的en.lproj、zh-Hans.lproj里分别写上对应语言的CFBundleDisplayName。这里有个小经验应用名不必和包名、应用内界面语言完全一致。有些团队会把中文应用名翻译成冗长的英文比如“健康生活管理专家”这种结果在桌面上显示不下被系统截断成“健康生活管理专...”特别尴尬。我一般建议英文名控制在 12 个字符以内中文控制在 6 到 8 个字超过这个长度大概率会被截断。4.2 多渠道、多环境下自动改名做过多渠道打包的都知道应用市场要求不同渠道包可以用不同应用名。比如正式版叫“我的应用”测试版叫“我的应用-测试版”这个需求如果用人工改代码的方式打包累死还容易漏。Android 的 Gradle 体系提供了一套很成熟的机制manifestPlaceholders。在build.gradle里配置android { defaultConfig { manifestPlaceholders [ appName: string/app_name ] } productFlavors { dev { manifestPlaceholders [ appName: string/app_name_dev ] } prod { manifestPlaceholders [ appName: string/app_name_prod ] } } }然后在AndroidManifest.xml里application android:label${appName}这样每次通过 flavor 打包应用名会自动替换。同理图标也可以这样玩在src/dev/res/mipmap-xxhdpi/放一张开发版专用图标在src/prod/res/mipmap-xxhdpi/放一张正式版图标文件名都叫ic_launcher.pngGradle 构建时会自动优先使用对应 source set 里的资源不需要改任何 Manifest。如果你用的不是 Android而是 iOS多环境改名稍微麻烦一点。Xcode 里没有直接的 placeholder 机制我一般通过Build Configuration 多个Info.plist来实现或者用xcconfig文件定义变量。不过大多数 iOS 应用的内测分发走的是 TestFlight正式版和测试版应用名相同问题也不大。4.3 运行时动态修改应用名Android 的限制与曲线方案有些需求更激进应用内给用户开放“自定义桌面名称”的功能。iOS 上官方不支持运行时应改名Android 上呢同样不支持直接改android:label。但如果你已经掌握了 3.2 节说的Activity-alias方案其实可以顺势实现应用名切换每个 alias 的android:label各指向一个不同的字符串资源切换图标的同时把应用名一起换掉。比如 Day 主题叫“我的应用”Night 主题叫“夜间模式”用户选择后桌面上的图标和名称一起变。这个方案的缺点是Activity-alias的数量不能无限加每加一个桌面入口包体里就多一份图标资源。而且如果你切换太频繁部分 ROM 的桌面可能出现两个相同图标并存的情况。我的建议是动态改名功能只保留“默认名”和“自定义名”两个即可不要做成 N 个选项。5. 实操中绕不开的坑与排查5.1 图标相关的典型问题速查现象常见原因解决思路图标模糊只放了一张低分辨率图系统放大补齐所有密度目录使用合适的尺寸图标被裁角自适应图标内容超出 72dp 安全区重新设计前景层把主体内容收进安全区图标变黑底个别老启动器不支持透明背景的 legacy icon给背景层加纯色或使用自适应图标动态切换后桌面没变化桌面缓存未刷新等待几秒或重启桌面/设备换图标后快捷方式还是旧图标用户桌面存的是旧快捷方式重新添加到桌面部分系统需要“应用信息→清除默认”iOS 上传报 alpha channel 错误图片带透明通道合并背景去掉 alpha这里重点说下“动态切换后桌面没变化”这个坑。不是每次都会出现但如果你测试的机器恰好是定制 ROM可能需要手动重启桌面。你可以通过adb shell pkill -f launcher或者直接在系统设置里清除桌面应用数据来快速验证不建议用这个方法去问用户“你重启一下手机”体验太差了。5.2 应用名相关的典型问题速查现象常见原因解决思路Android 桌面显示的应用名带包名android:label写成了资源 ID 而不是字符串用string/app_name确保strings.xml里有该键iOS 桌面显示英文App 内是中文只改了CFBundleDisplayName的默认值没加本地化在InfoPlist.strings加对应语言的键值多语言环境下切换语言桌面名字没变桌面缓存或系统未广播重启桌面部分 Android 版本需要重新创建快捷方式应用名被截断名字太长缩短应用名中文 6-8 字以内运行时应改名不生效直接调setComponentEnabledSetting但没有持久化状态保存状态到 SharedPreferences启动时恢复渠道包应用名不对productFlavors 配置漏了某个 flavor检查每个 flavor 的 manifestPlaceholders确保都有值5.3 电子素材和导出工具建议图标制作的素材管理我建议一开始就用矢量格式做源文件。Figma 免费版就够用团队的品牌色、Logo 规范也建议直接做成样式库导出各尺寸图标时一步到位。Android 端的图标转换工具Android Studio 自带的是Image Asset Studio右键res目录 →New→Image Asset可以选择前景图、背景色、缩放方式一键生成mipmap各密度文件。它还会帮你检查安全区范围比手动处理靠谱得多。iOS 端直接 Xcode 的AppIcon编辑器就行注意补全所有空位。虽然有工具会自动生成但不要依赖它填iPhone的图标后就不管iPad了很多团队上架前被审核以“iPad 图标缺失”打回就亏在这里。如果你手里只有一张图片不确定能不能用来做图标可以先拿在线 PNG 转 ICNS 或者转安卓各尺寸的工具跑一遍看看导出结果再决定要不要请设计重新画一张。直接拿低清截图当图标的大有人在最后效果真的没法看。这里还有个关于“文件图标存放位置”的细节可能跟系统桌面有关很多 Windows/Linux 桌面的快捷方式也涉及图标缓存。如果你在桌面系统里改了应用图标却迟迟不刷新可以删掉图标缓存文件后重建这属于桌面系统常见问题网上搜“xx系统清除图标缓存”都能找到不要觉得是自己应用的问题。6. 一点个人体会做定制化开发这几年我最大感受是像图标和应用名这种“边缘功能”越是看起来简单越能体现一个团队工程化水平的高低。有的人改图标靠 PS 一张一张手导改应用名直接写死在 Manifest 里有的人却能把图标、应用名、多渠道、多语言、动态切换一套组合拳打通后面每次上版本都不用再为这些事加班。我自己在最初做动态图标需求时就吃过没持久化的亏——测试同事第二天开手机图标一夜之间变回默认还以为是代码被还原了。后来重新设计这套方案把状态管理、启动检查、桌面缓存刷新都考虑进去才敢正式交付。如果你所在的团队也准备做这件事我建议从最基础的替换图标和改应用名开始先把这层跑通再加自适应图标和动态切换。一步到位不是不行但容易在排查问题上浪费大量时间。等这套流程稳定以后后续不管是要做品牌换肤、应用商店差异包还是客户定制包都能像流水线一样快速产出这才是定制化真正的价值。