资源的定义
在开发应用的过程中,不可避免的会遇到图像,字符串等等各种类型的引用,这些统称为资源(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/
目录中的文件可描述多个资源。对于此目录中的文件,<resources>
元素的每个子元素均定义一个资源。例如,<string> 元素创建 R.string 资源,<color> 元素创建 R.color 资源。
每个资源均用其自己的 XML 元素定义,可以根据自己的需要命名文件,并将不同的资源类型放在一个文件中。但是,为了清晰起见,建议将不同的资源类型放在不同的文件中。 例如,对于可在此目录中创建的资源,下面给出了相应的文件名约定:
arrays.xml
,用于资源数组(类型化数组)。
colors.xml
:颜色值。
dimens.xml
:尺寸值。
strings.xml
:字符串值。
styles.xml
:样式。
请参阅字符串资源、样式资源和更多资源类型。
资源匹配
为了能够正确的寻找到合适的资源,Android系统提供了限定符功能,程序运行的时候,系统会根据当前的设置寻找对应的限定资源。正确使用限定符的步骤如下:
-
在
res/
中创建一个以<resources_name>-<config_qualifier>
形式命名的新目录。
<resources_name>
是相应默认资源的目录名称(如表 1 中所定义)。
<qualifier>
是指定要使用这些资源的各个配置的名称(如表 2 中所定义)。
可以追加多个 <qualifier>。以短划线将其分隔。
注意:追加多个限定符时,必须按照这个表格 中列出的相同顺序放置它们。如果限定符的顺序错误,则该资源将被忽略。 -
将相应的备用资源保存在此新目录下。这些资源文件的名称必须与默认资源文件完全一样。
例如,以下是一些默认资源和备用资源:
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的根标签为<resources>
,可包含的类型主要有以下这些
- String, String Array, Quantity Strings,plurals
单独用<string>标签可以设定单个字符串
<resources>
<string name="hello">Hello!</string>
</resources>
----------------------------
textview中调用
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello" />
--------------------
在代码中使用:
String string = getString(R.string.hello);
字符串也可以使用占位符,%1表示位置结束。
<string name="msg">hello, %1$s, you have %2$d messages!</string>
----------------------
引用:
getString(R.string.msg,"jim",5);
可以接收的类型:%s(字符串),c(字符),b(布尔型),d(十进制整型),o(8进制整型),x(16进制整型),n(换行),f(浮点型),%(百分比)
定义字符串的时候,是不需要用 双引号 "" 来包裹的,如果字符串本身包含了单引号 ' 或者双引号 " ,必须进行转义处理,前边加一个反斜杠 \ 。只包含单引号的话,可以用双引号将整个字符串包裹起来。如果不进行处理,引号显示不出来。
<string name="good_example">This\'ll work</string>
<string name="good_example_2">"This'll also work"</string >
<string name="bad_example">This doesn't work</string>
--------------------------
<string name="good_example">This is a \"good string\".</string>
<string name="bad_example">This is a "bad string".</string>
<!-- Quotes are stripped; displays as: This is a bad string. -->
复数plurals可以指定根据输入内容(0,1,2等等),选择对应的字符串。
<plurals name="person">
<item quantity="one">%d person </item>
<item quantity="other">%1$d persons </item>
</plurals>
-----------------------------------
str+=getResources().getQuantityString(R.plurals.person, 1,1);
str+="\n";
str+=getResources().getQuantityString(R.plurals.person, 2,2);
tx.setText(str);
String Array用来定义一个数组:
<resources>
<string-array name="planets_array">
<item>Mercury</item>
<item>Venus</item>
<item>Earth</item>
<item>Mars</item>
</string-array>
</resources>
--------------
Resources res = getResources();
String[] planets = res.getStringArray(R.array.planets_array);
-
Color,Dimension
Color用来定义颜色,Dimension定义尺寸。定义好之后就可以在xml文件或者代码中直接使用了。
<color name="colorPrimary">#4caf50</color>
<dimen name="material_8dp">8dip</dimen>
---------------------
<ImageView
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
这些都是基本数据类型,使用很简单,直接看下面例子就可以了:
<integer name="max_speed">75</integer>
<integer name="min_speed">5</integer>
Resources res = getResources();
int maxSpeed = res.getInteger(R.integer.max_speed);
------------------------------
<integer-array name="bits">
<item>4</item>
<item>8</item>
<item>16</item>
<item>32</item>
</integer-array>
Resources res = getResources();
int[] bits = res.getIntArray(R.array.bits);
---------------------------
<bool name="screen_small">true</bool>
<bool name="adjust_view_bounds">true</bool>
Resources res = getResources();
boolean screenIsSmall = res.getBoolean(R.bool.screen_small);
<ImageView
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,颜色等。
<array name="icons">
<item>@drawable/home</item>
<item>@drawable/settings</item>
<item>@drawable/logout</item>
</array>
<array name="colors">
<item>#FFFF0000</item>
<item>#FF00FF00</item>
<item>#FF0000FF</item>
</array>
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
<resources>的子标签<item>是一个比较特殊的标签,它可以通过type来表示任何类型,它相当于为已存在的资源指定一个别名。看下边的例子:
<item name="luncher"type="drawable">@drawable/ic_launcher</item>
<item name="main_layout" type="layout">@layout/activity_main</item>
这里定义了两个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就可以了。
网友评论