资源的定义
在开发应用的过程中,不可避免的会遇到图像,字符串等等各种类型的引用,这些统称为资源(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就可以了。