资源的定义

在开发应用的过程中,不可避免的会遇到图像,字符串等等各种类型的引用,这些统称为资源(resource)。为了便于管理维护资源,应该在外部提供资源,而不是在代码中直接定义资源,这个过程叫资源外部化。Android中针对资源专门创建了对应的资源目录,在运行的时候,系统会根据当前配置使用正确的资源。比如寻找屏幕适配的图片,语言对应的字符串等等。

在资源外部化之后,系统会自动分配一个资源ID,便于我们方便的访问资源,可以在R文件中查看资源的ID。下面是一个常规的工程目录,res目录下包含四个资源分类:

MyProject/

src/

MyActivity.java

res/

drawable/

graphic.png

layout/

main.xml

info.xml

mipmap/

icon.png

values/

strings.xml

不管在什么情况下,都不应该直接将资源放在res目录下,会导致编译错误。

资源目录分类

外部资源支持的分类如下表(从官网搬运):

目录

资源类型

animator/

用于定义属性动画的 XML 文件。

anim/

定义渐变动画的 XML 文件。(属性动画也可以保存在此目录中,但是为了区分这两种类型,属性动画首选 animator/ 目录。)

color/

用于定义颜色状态列表的 XML 文件。请参阅颜色状态列表资源

drawable/

位图文件(.png、.9.png、.jpg、.gif)或编译为以下可绘制对象资源子类型的 XML 文件: 位图文件, 九宫格(.9.png,可调整大小的位图), 状态列表形状动画,可绘制对象,其他可绘制对象

mipmap/

适用于不同启动器图标密度的可绘制对象文件

layout/

用于定义用户界面布局的 XML 文件。 请参阅布局资源。。

menu/

用于定义应用菜单(如选项菜单、上下文菜单或子菜单)的 XML 文件。请参阅菜单资源。

raw/

要以原始形式保存的任意文件。

要使用原始 InputStream 打开这些资源,请使用资源 ID(即 R.raw.filename)调用 Resources.openRawResource()。

但是,如需访问原始文件名和文件层次结构,则可以考虑将某些资源保存在 assets/ 目录下(而不是 res/raw/)。

assets/ 中的文件没有资源 ID,因此您只能使用 AssetManager 读取这些文件。

values/

包含字符串、整型数和颜色等简单值的 XML 文件。

xml/

可以在运行时通过调用 Resources.getXML() 读取的任意 XML 文件。各种 XML 配置文件(如可搜索配置)都必须保存在此处。

values/目录中的文件可描述多个资源。对于此目录中的文件, 元素的每个子元素均定义一个资源。例如, 元素创建 R.string 资源, 元素创建 R.color 资源。

每个资源均用其自己的 XML 元素定义,可以根据自己的需要命名文件,并将不同的资源类型放在一个文件中。但是,为了清晰起见,建议将不同的资源类型放在不同的文件中。 例如,对于可在此目录中创建的资源,下面给出了相应的文件名约定:

arrays.xml,用于资源数组(类型化数组)。

colors.xml:颜色值。

dimens.xml:尺寸值。

strings.xml:字符串值。

styles.xml:样式。

请参阅字符串资源、样式资源和更多资源类型。

资源匹配

为了能够正确的寻找到合适的资源,Android系统提供了限定符功能,程序运行的时候,系统会根据当前的设置寻找对应的限定资源。正确使用限定符的步骤如下:

在 res/ 中创建一个以 - 形式命名的新目录。

是相应默认资源的目录名称(如表 1 中所定义)。

是指定要使用这些资源的各个配置的名称(如表 2 中所定义)。

可以追加多个 。以短划线将其分隔。

注意:追加多个限定符时,必须按照这个表格 中列出的相同顺序放置它们。如果限定符的顺序错误,则该资源将被忽略。

将相应的备用资源保存在此新目录下。这些资源文件的名称必须与默认资源文件完全一样。

例如,以下是一些默认资源和备用资源:

drawable/ ---->默认资源

icon.png

background.png

drawable-hdpi/ ---->备用资源,限定符hdpi

icon.png

background.png

关于资源的匹配规则,官网已经讲解的非常详细了,这里只介绍一些常用的内容。

Drawable的限定符

drawable文件夹后面的限定符常见的有ldpi,mdpi,hdpi,xhdpi,xxhdpi,xxxhpdi。他们所代表的都是屏幕的密度。除此之外,还有nodpi,tvdpi,anydpi,他们的区别如下:

限定符

含义

ldpi:

低密度屏幕;约为 120dpi。

mdpi:

中等密度(传统 HVGA)屏幕;约为 160dpi。

hdpi:

高密度屏幕;约为 240dpi。

xhdpi:

超高密度屏幕;约为 320dpi。此项为 API 级别 8 中新增配置

xxhdpi:

超超高密度屏幕;约为 480dpi。此项为 API 级别 16 中新增配置

xxxhdpi:

超超超高密度屏幕使用(仅限启动器图标);约为 640dpi。 此项为 API 级别 18 中新增配置

nodpi:

它可用于您不希望缩放以匹配设备密度的位图资源。

tvdpi:

密度介于 mdpi 和 hdpi 之间的屏幕;约为 213dpi。它并不是“主要”密度组, 主要用于电视,而大多数应用都不需要它。对于大多数应用而言,提供 mdpi 和 hdpi 资源便已足够,系统将根据需要对其进行缩放。此项为 API 级别 13 中新增配置

anydpi:

此限定符适合所有屏幕密度,其优先级高于其他限定符。 这对于矢量可绘制对象很有用。 此项为 API 级别 21 中新增配置

通常情况下,我们将各种尺寸对应的图片放到各自文件夹下就可以了,如果系统找不到对应的资源,会去寻找相近的最适合的资源,这时候,显示出来的效果就会有差异了:

这6个主要密度的比例为 3:4:6:8:12:16,假设当前屏幕密度为240dpi,对应的资源为hdpi,我们有一张图片尺寸为100*100px,把它放在hdpi目录下,显示的效果是没有问题的,仍然是100*100px。如果hdpi下面没有会怎么样?

系统会寻找最相近的合适的资源。在hdpi下找不到,会去更高密度的xhdpi寻找,还是找不到就继续向上,一直到最高的xxhdpi,都没有找到的话,再去nodpi寻找,仍然没有的话就只能向下寻找了,到mdp-->ldpi。

再回到上边的例子,如果我们把图片放到xhdpi目录下,系统找到图片之后,并不会直接引用。它会认为该图片是为xhdpi准备的,为了适应当前的hdpi,会对图片进行缩放,在这里就是缩小了,缩小比例就是hdpi:xhdpi = 6:8,最终显示出来的图片大小也不再是100*100px,而是100(6/8)=75*75px

所以,如果我们想在高密度的屏幕上显示图片,应该为他制定对应的资源,否则系统会去低密度目录下寻找资源,然后对它放大,会占用极高的内存。

nodpi目录下的图片不会被缩放,在任何屏幕上都是它本身的尺寸,只有在更高密度的目录下没有找到资源时才会到这里寻找。

mipmap

mipmap是android Studio里为应用启动图标专门设置的文件夹。建议的启动图标尺寸对应如下:

密度

建议尺寸

mipmap-mdpi

48 * 48

mipmap-hdpi

72 * 72

mipmap-xhdpi

96 * 96

mipmap-xxhdpi

144 * 144

mipmap-xxxhdpi

192 * 192

Values资源

上面已经提过了,values支持多种资源,这些资源都是一些常量的定义,它们统统放在values目录下。values的根标签为,可包含的类型主要有以下这些

String, String Array, Quantity Strings,plurals

单独用标签可以设定单个字符串

Hello!

----------------------------

textview中调用

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:text="@string/hello" />

--------------------

在代码中使用:

String string = getString(R.string.hello);

字符串也可以使用占位符,%1

表示位置结束。

hello, %1$s, you have %2$d messages!
----------------------
引用:
getString(R.string.msg,"jim",5);

可以接收的类型:%s(字符串),c(字符),b(布尔型),d(十进制整型),o(8进制整型),x(16进制整型),n(换行),f(浮点型),%(百分比)

定义字符串的时候,是不需要用 双引号 "" 来包裹的,如果字符串本身包含了单引号 ' 或者双引号 " ,必须进行转义处理,前边加一个反斜杠 \ 。只包含单引号的话,可以用双引号将整个字符串包裹起来。如果不进行处理,引号显示不出来。

This\'ll work
"This'll also work"
This doesn't work
--------------------------
This is a \"good string\".
This is a "bad string".

复数plurals可以指定根据输入内容(0,1,2等等),选择对应的字符串。

%d person 
%1$d persons 
-----------------------------------
str+=getResources().getQuantityString(R.plurals.person, 1,1);
str+="\n";
str+=getResources().getQuantityString(R.plurals.person, 2,2);
tx.setText(str);
String Array用来定义一个数组:
Mercury
Venus
Earth
Mars
--------------
Resources res = getResources();
String[] planets = res.getStringArray(R.array.planets_array);
Color,Dimension
Color用来定义颜色,Dimension定义尺寸。定义好之后就可以在xml文件或者代码中直接使用了。
#4caf50
8dip
---------------------
android:background="@color/colorPrimary"
android:layout_width="@dimen/material_8dp"
android:layout_height="wrap_content"
android:src="@drawable/ic_launcher"/>
Integer, Integer Array,fraction,Boolean
这些都是基本数据类型,使用很简单,直接看下面例子就可以了:
75
5
Resources res = getResources();
int maxSpeed = res.getInteger(R.integer.max_speed);
------------------------------
4
8
16
32
Resources res = getResources();
int[] bits = res.getIntArray(R.array.bits);
---------------------------
true
true
Resources res = getResources();
boolean screenIsSmall = res.getBoolean(R.bool.screen_small);
android:layout_height="fill_parent"
android:layout_width="fill_parent"
android:src="@drawable/logo"
android:adjustViewBounds="@bool/adjust_view_bounds" />
Color
Type Array
TypeArray用来定义其他类型资源的数组,比如drawable,颜色等。
@drawable/home
@drawable/settings
@drawable/logout
#FFFF0000
#FF00FF00
#FF0000FF
Resources res = getResources();
TypedArray icons = res.obtainTypedArray(R.array.icons);
Drawable drawable = icons.getDrawable(0);
TypedArray colors = res.obtainTypedArray(R.array.colors);
int color = colors.getColor(0,0);
Item

的子标签是一个比较特殊的标签,它可以通过type来表示任何类型,它相当于为已存在的资源指定一个别名。看下边的例子:

@drawable/ic_launcher
@layout/activity_main

这里定义了两个item,一个是drawable,另一个是layout,我们使用的时候,使用R.drawable.ic_luncher和R.drawable.luncher,最终指向的都是同一个图片。

Android里的资源类型比较多,在备用资源不多的时候,对每一种类型都设置限定符来专门分配一个目录似乎有些麻烦,这时候可以使用item。

首先新建一个存放item的xml文件,比如refs.xml,在这里定义一个item,指向默认的资源。然后新建带限定符的values目录,例如values-sw600dp,新建一个同样的xml文件,在这个文件中定义一个同样name的item,指向600dp所需的资源。在使用的使用,引用item中指定的name就可以了。