3、Android资源目录结构:res目录详解
说到Android的资源管理,res目录绝对是绕不开的核心。我个人习惯把res目录比作一个「资源仓库」——你所有用到的图片、布局、字符串、颜色、尺寸,全得往这里面塞。今天我就带你把res目录翻个底朝天。
3.1 res目录到底长什么样?
先看一个典型的res目录结构:
res/
├── drawable/ # 位图、XML图形、形状
├── mipmap/ # 应用图标(启动图标)
├── values/ # 字符串、颜色、尺寸、主题
├── layout/ # 布局文件
├── anim/ # 补间动画
├── animator/ # 属性动画
├── color/ # 颜色状态列表
├── font/ # 自定义字体
├── menu/ # 菜单定义
├── raw/ # 原始资源(音频、视频等)
├── xml/ # 任意XML文件
└── transition/ # 场景过渡动画
嗯,这里要注意:mipmap和drawable的区别。很多新手会搞混。mipmap专门放应用启动图标,drawable放其他图片资源。为什么分开?因为启动图标在不同密度下需要特殊处理,系统会保留所有密度版本,而drawable可能会被压缩。
核心原则:应用图标放mipmap,其他图片放drawable。别混着放,否则打包时可能出问题。
3.2 drawable目录详解
drawable是我打交道最多的目录。它不只是放图片那么简单。你想想看,它还能放XML定义的形状、渐变、选择器。
举个例子,一个圆角按钮的背景:
<!-- res/drawable/rounded_button.xml -->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="8dp" />
<solid android:color="#FF6200EE" />
<padding
android:left="16dp"
android:top="8dp"
android:right="16dp"
android:bottom="8dp" />
</shape>
我在项目中遇到过一个问题:团队里有人把一张超大PNG直接丢进drawable,结果APK体积暴涨。后来我们统一用WebP格式,体积直接砍半。说白了,drawable里的图片格式选择很重要。
我的建议:能用VectorDrawable就用矢量图,实在不行用WebP。PNG只保留必要的透明度场景。
3.3 mipmap目录的秘密
mipmap目录下通常有多个密度版本:
mipmap-mdpi/ # 1x
mipmap-hdpi/ # 1.5x
mipmap-xhdpi/ # 2x
mipmap-xxhdpi/ # 3x
mipmap-xxxhdpi/ # 4x
mipmap-anydpi-v26/ # 自适应图标
为什么要保留所有密度?因为启动图标在桌面显示时,系统会从mipmap中选取最合适的版本。如果你只放了一个mdpi版本,在高密度屏幕上会模糊得一塌糊涂。
我曾经踩过一个坑:为了省事,只放了xxhdpi的图标,结果在旧设备上图标显示异常。嗯,从那以后我老老实实生成所有密度版本。
3.4 values目录:资源的核心
values目录存放的是「值资源」,说白了就是各种常量定义。常见的文件有:
| 文件名 | 用途 | 示例 |
|---|---|---|
| strings.xml | 字符串 | <string name="app_name">我的应用</string> |
| colors.xml | 颜色值 | <color name="primary">#FF6200EE</color> |
| dimens.xml | 尺寸值 | <dimen name="margin">16dp</dimen> |
| styles.xml | 样式/主题 | <style name="AppTheme">...</style> |
| arrays.xml | 数组 | <string-array name="planets">...</string-array> |
我个人习惯把颜色和尺寸单独抽出来,而不是硬编码在布局里。为什么?因为后期改主题色时,你只需要改colors.xml里的一个值,而不是翻遍所有布局文件。
注意:values目录下的文件名可以自定义,但建议遵循Android Studio的默认命名。别搞什么my_custom_values.xml,团队协作时别人根本找不到。
3.5 资源限定符:让资源适配不同设备
资源限定符是Android资源管理的精髓。说白了,就是根据设备的不同特性,自动加载对应的资源文件。
常见的限定符分类:
- 密度限定符:mdpi、hdpi、xhdpi、xxhdpi、xxxhdpi、nodpi、tvdpi
- 语言限定符:en、zh、zh-rCN、de、fr 等
- 屏幕尺寸限定符:small、normal、large、xlarge
- 屏幕方向限定符:port、land
- API级别限定符:v14、v21、v26 等
- 夜间模式限定符:night、notnight
- 布局方向限定符:ldrtl、ldltr
举个例子,多语言适配:
res/
├── values/strings.xml # 默认(英语)
├── values-zh/strings.xml # 中文
├── values-zh-rCN/strings.xml # 简体中文
├── values-de/strings.xml # 德语
└── values-fr/strings.xml # 法语
系统会根据设备的语言设置,自动选择对应的strings.xml。你想想看,这比在代码里写if-else判断语言方便多了。
3.6 密度限定符实战
密度限定符是最常用的。Android设备屏幕密度五花八门,从ldpi到xxxhdpi都有。正确的做法是:
res/
├── drawable-mdpi/ # 基准密度
├── drawable-hdpi/ # 1.5倍
├── drawable-xhdpi/ # 2倍
├── drawable-xxhdpi/ # 3倍
└── drawable-xxxhdpi/ # 4倍
我在项目中遇到过一个问题:设计师只给了xxhdpi的切图,结果在mdpi设备上显示时,图片被缩小了3倍,边缘锯齿明显。后来我要求设计师至少提供mdpi、xhdpi、xxhdpi三套图。
避坑指南:我曾经把一张背景图放在drawable-nodpi里,结果在不同密度设备上显示大小不一致。nodpi的意思是「不缩放」,但图片的物理像素不变,在不同密度下实际显示尺寸会变。正确做法是用dimens定义尺寸,图片放在对应密度目录下。
3.7 限定符的匹配规则
系统选择资源时,遵循「最匹配优先」原则。举个例子:
- 设备是中文、xxhdpi、横屏
- 系统会优先找 values-zh-xxhdpi-land/
- 找不到就降级到 values-zh-xxhdpi/
- 再找不到就 values-zh/
- 最后才是 values/
说白了,限定符越多越精确,但也要考虑降级路径。我建议不要搞太多层限定符,否则维护起来很痛苦。
3.8 资源引用方式
在代码和XML中引用资源:
// Java/Kotlin 代码中
getResources().getString(R.string.app_name)
getResources().getDrawable(R.drawable.ic_launcher)
getResources().getColor(R.color.primary)
// XML 布局中
@string/app_name
@drawable/ic_launcher
@color/primary
@dimen/margin
嗯,这里有个小技巧:在Flutter中引用Android资源时,需要通过MethodChannel或者使用flutter_native_resource插件。不过大多数情况下,Flutter有自己的资源管理方式,Android的res目录主要用于原生模块。
3.9 知识体系总览
下面这张图帮你理清res目录的核心逻辑:
3.10 总结一下
res目录是Android资源管理的基石。记住几个要点:
- mipmap放图标,drawable放其他图片,别搞混
- values目录统一管理字符串、颜色、尺寸,方便后期维护
- 资源限定符是适配利器,密度、语言、屏幕方向都能自动匹配
- 限定符越多越精确,但也要考虑降级路径
我在多个项目里都坚持这套规范,团队协作效率明显提升。说白了,资源管理做得好,后期改需求时能省不少事。