Android中的应用资源

资源的定义

在开发应用的过程中,不可避免的会遇到图像,字符串等等各种类型的引用,这些统称为资源(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系统提供了限定符功能,程序运行的时候,系统会根据当前的设置寻找对应的限定资源。正确使用限定符的步骤如下:

  1. res/ 中创建一个以 <resources_name>-<config_qualifier> 形式命名的新目录。
    <resources_name> 是相应默认资源的目录名称(如表 1 中所定义)。
    <qualifier> 是指定要使用这些资源的各个配置的名称(如表 2 中所定义)。
    可以追加多个 <qualifier>。以短划线将其分隔。
    注意:追加多个限定符时,必须按照这个表格 中列出的相同顺序放置它们。如果限定符的顺序错误,则该资源将被忽略。

  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的根标签为<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);

字符串也可以使用占位符,%1s,%s/d表示类型,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_luncherR.drawable.luncher,最终指向的都是同一个图片。

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

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,902评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,037评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,978评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,867评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,763评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,104评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,565评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,236评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,379评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,313评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,363评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,034评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,637评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,719评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,952评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,371评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,948评论 2 341

推荐阅读更多精彩内容