2. 资源目录结构设计:Android、iOS、Web 的标准对比

做界面移植,第一关就是目录结构。说白了,你得知道每个平台把图片「藏」在哪儿,又是怎么按设备密度去匹配的。我见过不少新手,直接把 iOS 的 @2x 图塞进 Android 的 drawable 里,结果跑起来要么图太大要么直接崩——嗯,今天咱们就把这三个平台的规矩捋清楚。

2.1 Android:res/drawable-* 的密度体系

Android 的资源目录是按 屏幕密度 来划分的。你想想看,同一张图在 320dpi 和 480dpi 的屏幕上,物理像素差了一倍,所以必须给不同密度准备不同尺寸的图。

标准目录长这样:

res/
├── drawable-ldpi/     # 低密度 (0.75x)
├── drawable-mdpi/     # 中密度 (1.0x)  — 基准
├── drawable-hdpi/     # 高密度 (1.5x)
├── drawable-xhdpi/    # 超高密度 (2.0x)
├── drawable-xxhdpi/   # 超超高密度 (3.0x)
├── drawable-xxxhdpi/  # 超超超高密度 (4.0x)
└── drawable/          # 通用兜底目录

核心规则:Android 以 mdpi 为 1x 基准。一张 100×100 的图,放到 xhdpi 里就得是 200×200,放到 xxhdpi 里是 300×300。系统会根据设备密度自动选对应目录的图。

我个人习惯在 drawable-xxhdpi 里放最大尺寸的图,因为现在主流设备基本都在 3x 以上。但注意,drawable 目录(不带后缀)是兜底目录,千万别把图只放这里——所有设备都会去拿,但不会做任何缩放,结果就是低端机内存爆炸。

我曾经踩过的坑:把一张 4K 背景图只放在 drawable 里,结果在 ldpi 的老平板上直接 OOM。后来学乖了,每个密度目录至少放一张占位图,或者用 drawable-nodpi 明确告诉系统「这张图别缩放」。

2.2 iOS:Assets.xcassets 的语义化管理

iOS 这边就优雅多了。从 Xcode 5 开始,苹果推荐用 Assets.xcassets 来管理图片。它不再按密度目录名来区分,而是用 Image Set 的概念——一张图对应一个名字,里面包含 1x、2x、3x 三个版本。

目录结构是这样的:

Assets.xcassets/
├── AppIcon.appiconset/       # 应用图标
├── LaunchImage.launchimage/  # 启动图(旧版)
├── Images.xcassets/          # 主资源包
│   ├── icon_home.imageset/   # 一个图片集
│   │   ├── icon_home@1x.png
│   │   ├── icon_home@2x.png
│   │   └── icon_home@3x.png
│   ├── bg_splash.imageset/
│   │   ├── bg_splash@1x.png
│   │   ├── bg_splash@2x.png
│   │   └── bg_splash@3x.png
│   └── Contents.json         # 描述文件
└── Contents.json             # 根描述文件

每个 .imageset 目录里都有一个 Contents.json,里面记录了每张图对应的设备类型和缩放因子。你想想看,这比 Android 的纯目录命名要灵活得多——你可以给 iPhone 和 iPad 指定不同的图,甚至区分横竖屏。

我的建议:iOS 移植时,直接从设计稿导出 @3x 图,然后用脚本自动生成 @2x 和 @1x。手动缩放出错率太高,我吃过一次亏——@2x 图比 @3x 还大,结果 iPhone 8 上直接卡死。

2.3 Web:static/images 的扁平化结构

Web 端就简单粗暴了。没有密度概念,没有 Image Set,就是一个 static/images 目录,所有图片平铺或者按模块分文件夹。

常见结构:

static/
├── images/
│   ├── logo.png
│   ├── banner.jpg
│   ├── icons/
│   │   ├── home.svg
│   │   └── user.svg
│   └── backgrounds/
│       ├── bg_desktop.jpg
│       └── bg_mobile.jpg
├── fonts/
└── videos/

Web 的图片管理核心是 响应式图片,通过 <picture> 标签或 srcset 属性来适配不同屏幕。说白了,目录结构只是存储,真正的「密度适配」是在 HTML/CSS 层完成的。

关键区别:Web 没有「系统自动选图」的机制。你得自己写 srcset 告诉浏览器:这张图在 2x 屏上用这个版本,在 1x 屏上用那个版本。目录结构只负责归类,不负责匹配。

2.4 三大平台目录结构对比表

维度 Android iOS Web
管理方式 按密度目录名 按 Image Set + JSON 按文件夹归类
密度基准 mdpi = 1x @1x = 基准 无固定基准
自动匹配 系统自动选目录 系统自动选 Image Set 内版本 需手动写 srcset
命名规则 目录名决定密度 文件名含 @2x/@3x 无强制规则
兜底机制 drawable/ 目录 Contents.json 中 fallback 无,需自己处理
多设备适配 密度 + 屏幕尺寸限定符 Image Set 内可指定设备类型 CSS Media Query

2.5 移植时的目录映射策略

做界面移植时,最头疼的就是把一套设计稿拆成三套目录。我总结了一个映射规则,你可以直接套用:

  1. 从设计稿拿到 @3x 图(iOS 标准)
  2. 缩放到 @2x 和 @1x,分别放入 iOS 的 imageset
  3. 把 @3x 图缩放到 xxhdpi 尺寸(3x),放入 Android 的 drawable-xxhdpi
  4. 依次缩放到 xhdpi、hdpi、mdpi,放入对应目录
  5. Web 端直接用 @2x 图,配合 srcset 做响应式

注意:Android 的 xxxhdpi(4x)目前只有极少数设备用到,我建议只在启动图标等关键资源上放 4x 图,普通界面图放到 xxhdpi 就够了。否则资源包体积会失控。

2.6 知识体系结构图

下面这张图帮你理清三个平台的目录设计逻辑:

三大平台资源目录结构对比 三大平台资源目录结构对比 Android res/drawable-* drawable-mdpi drawable-hdpi drawable-xhdpi drawable-xxhdpi 按密度目录名自动匹配 iOS Assets.xcassets icon_home.imageset/ Contents.json + @1x/@2x/@3x 按 Image Set 语义化管理 Web static/images/ logo.png / banner.jpg icons/ backgrounds/ 按文件夹归类,无自动匹配 移植核心策略 从 iOS @3x 设计稿出发 → 缩放生成各平台密度版本 → 按各自目录规则存放 设计稿 @3x 缩放生成 按平台规则放入对应目录 注意:Web 端需额外处理 srcset 响应式,Android 需注意兜底目录

2.7 避坑指南

  • 不要混用命名风格:Android 用下划线(icon_home),iOS 用驼峰(iconHome),Web 随意但建议统一。我见过一个项目,三端命名风格混在一起,最后脚本批量重命名时全乱了。
  • SVG 优先:能上 SVG 的图就别用 PNG。SVG 没有密度问题,一套图通吃三端。但要注意 Android 对 SVG 支持有限,需要转成 VectorDrawable。
  • 资源版本号:Web 端一定要给图片加版本号或 hash,否则浏览器缓存会让你怀疑人生。Android 和 iOS 的打包机制会自动处理缓存,但 Web 你得自己来。
  • 不要手动维护:写个脚本自动从设计稿导出并生成三端目录结构。我每次移植都靠脚本,手动复制粘贴太容易出错了——有一次把 iOS 的 @2x 图当 Android 的 xhdpi 用,结果图糊了,排查了半天。

我的个人习惯:在项目根目录放一个 assets_source 文件夹,里面是设计稿原始切图。然后写一个 build_assets.py 脚本,一键生成 Android、iOS、Web 三套目录。这样设计改图时,我只用替换源文件再跑一遍脚本,三端同步更新,不会漏掉任何一个平台。