關於Android Resource的點點滴滴

1 什麼是Resource

Resource,即資源,在Android中,資源是指代碼中使用附加文件和靜態內容,例如位圖、界面佈局、界面字符串、動畫說明等等。html

咱們將資源放在分類目錄下,以便單獨對其進行維護,例如圖像和代碼中的字符串。此外,還應爲特定設備提供備用資源,例如根據屏幕尺寸提供不一樣界面佈局,或者根據語言設置提供不一樣的字符串,java

1.1 分組資源類型

各類類型的資源被放入項目res/目錄的特定子目錄中。以下所示爲一個簡單項目的文件層次結構。android

注意:切勿將資源文件直接保存在 res/ 目錄內,由於這樣會形成編譯錯誤,應按類別新建子目錄。
MyProject/
    src/
        MyActivity.java
    res/
        drawable/
            graphic.png
        layout/
            main.xml
            info.xml
        mipmap/
            icon.png
        values/
            strings.xml

Android支持的分組資源類型請參考官方文檔:分組資源類型json

1.2 提供備用資源

之因此提供備用資源,是爲了支持特定的設備配置。如針對不一樣的屏幕密度和語言,應分別加入備用的可繪製對象資源和備用字符串資源。在運行時,Android會檢測當前設備配置併爲應用加載合適的資源。
格式:在res/目錄下建立以<resources_name>-<config_qualifier>形式命名的新目錄app

舉例:以下所示爲備用的可繪製對象資源:
備用可繪製資源類型
更多有關可繪製對象資源類型請參考下圖:
可繪製對象資源類型ide

更多配置限定符的內容請參考官方文檔 提供備用資源,以及關於建立別名資源的方法請參考 建立別名資源

2 如何引用(訪問)Resource

編譯應用時,aapt會生成R類,其中包含res/目錄下全部資源的資源ID,每種資源類型都有對應的R子類,而且該類型下的每一個資源都有靜態的整型數——資源ID。因而,咱們能夠在項目中藉助R類中生成的資源ID來訪問這些資源。例如R.drawable對應於全部可繪製對象資源,引用方式爲R.drawable.icon。佈局

2.1 在代碼中引用資源

格式:[<package_name>.]R.<resource_type>.<resource_name>測試

說明:動畫

  • <package_name>:是資源所在包的名稱,若是引用的資源是本身的提供的資源,則不須要;
  • <resource_type>:是資源類型的R子類;
  • <resource_name>:是不帶擴展名的資源文件名。
注意資源名稱是不包含擴展名的,例如引用圖片時沒有必要加上 .jpg或者 .png等。

舉例:R.drawable.myimageR.string.main_titleR.layout.main_screen等。ui

2.2 在xml中引用資源-@

格式:@[<package_name>:]<resource_type>/<resource_name>

說明(同上):

  • <package_name>:是資源所在包的名稱,若是引用的資源是本身的提供的資源,則不須要;
  • <resource_type>:是資源類型的R子類;
  • <resource_name>:是不帶擴展名的資源文件名。

舉例:@color/opaque_red、@string/hello

如要引用系統資源,則您須要加入包名稱。例如:android:textColor="@android:color/secondary_text_dark"

因爲系統資源分爲public和非public,public的聲明是在 <sdk_path>\platforms\android-版本號\data\res\values\public.xml,而 @*表明引用系統的非public資源。

格式:@*android:<resource_type>/<resource_name>,能夠調用系統定義的全部資源,

而上面的@android:<resource_type>/<resource_name>,則只可以調用public屬性的資源。
例如將 Android 提供的佈局資源用於 ListAdapter 中的列表項:setListAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, myarray));,其中,simple_list_item_1是平臺爲 ListView 中的項目所定義的佈局資源,便可以使用該資源,而沒必要自行建立列表項佈局。

2.3 引用樣式屬性-?

樣式屬性資源容許你在當前應用的主題中引用屬性的值。 引用樣式屬性,能夠經過對UI元素進行樣式設置以匹配當前主題提供的標準變體來自定義UI元素的外觀,而不是提供硬編碼的值(即具體的值)。 引用樣式屬性本質上說:「在當前主題中使用此屬性定義的樣式。」

格式:?[<package_name>:][<resource_type>/]<resource_name>

舉例1,在xml文件中引用主題屬性:

<EditText
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:textColor="?android:textColorSecondary"
    android:text="@string/hello_world" />
<!-- Secondary text color. -->
<attr name="textColorSecondary" format="reference|color" />

在以上代碼中,是將android:textColor屬性指定爲當前主題背景中某個樣式屬性的名稱,即將android:textColorSecondary樣式屬性的值用做此控件textColor的值。

顯示聲明的格式爲: ?android:attr/textColorSecondary,可是這裏無須顯示聲明。

舉例2,在style文件中引用主題屬性:

<style name="Widget.CompoundButton">
    <item name="focusable">true</item>
    <item name="clickable">true</item>
    <item name="textAppearance">?attr/textAppearance</item>
    <item name="textColor">?attr/textColorPrimaryDisableOnly</item>
    <item name="gravity">center_vertical|start</item>
</style>

2.4 訪問原始文件

若咱們要訪問原始文件,則不該該將文件放於res/目錄下,由於從res/讀取資源的惟一方法是使用資源 ID,所以,咱們能夠將資源保存在assets/目錄中。

而訪問的方式則須要利用AssetManager讀取原始數據。以下所示

StringBuilder stringBuilder = new StringBuilder();
try {
    AssetManager assetManager = context.getAssets();
    BufferedReader bf = new BufferedReader(new InputStreamReader(
                    assetManager.open(file.json)));
    String line;
    while ((line = bf.readLine()) != null) {
        stringBuilder.append(line);
    }
} catch (IOException e) {
    e.printStackTrace();
}

但如果只須要讀取原始數據的能力,例如讀取視頻文件或音頻文件,則可將文件保存在res/raw/目錄中,並利用openRawResource()讀取字節流。如:mMediaPlayer = MediaPlayer.create(this, R.raw.song);

2.5 用 @+ 建立或引用資源

經過上面講解能夠知道@是引用的意思,那麼+呢?+表示在R.java中名爲type的內部類中添加一條記錄。舉個例子:android:id="@+id/submit_btn"表示新建一個名爲submit_btn資源id。上面的例子就是最多見的用法,即定於控件id。

@+id/ID名稱:表示新建一個資源ID;

@id/ID名稱:引用已定義的資源ID,包括系統ID;

@android:id/ID名稱:引用系統ID,其等效於@+id/ID名稱

3 根據資源名稱獲取資源ID

如何根據文件名來獲得資源ID,這就要藉助Resources的getIdentifier方法了。以下代碼所示:

Resources res = getResources();
final String packageName = getPackageName();
//方法1 測試1
int imageResId = res.getIdentifier("ic_launcher", "mipmap", packageName);
//方法2 測試1
int imageResId2 = res.getIdentifier(packageName + ":mipmap/ic_launcher", null, null);

//測試2
int musicResId=res.getIdentifier("song", "raw", packageName);
//測試3
int notFoundResId=res.getIdentifier("activity_main", "drawable", packageName);
Log.d("TAG", "imageResId: " + imageResId
        + ";imageResId2: " + imageResId2
        + ";musicResId: " + musicResId
        + ";notFoundResId: " + notFoundResId);

運行結果:

imageResId: 2131361792;imageResId2: 2131361792;musicResId: 2131427328;notFoundResId: 0

看看源碼中getIdentifier方法實現:

/**
 * Return a resource identifier for the given resource name.  A fully
 * qualified resource name is of the form "package:type/entry".  The first
 * two components (package and type) are optional if defType and
 * defPackage, respectively, are specified here.
 * 
 * <p>Note: use of this function is discouraged.  It is much more
 * efficient to retrieve resources by identifier than by name.
 * 
 * @param name The name of the desired resource.
 * @param defType Optional default resource type to find, if "type/" is
 *                not included in the name.  Can be null to require an
 *                explicit type.
 * @param defPackage Optional default package to find, if "package:" is
 *                   not included in the name.  Can be null to require an
 *                   explicit package.
 * 
 * @return int The associated resource identifier.  Returns 0 if no such
 *         resource was found.  (0 is not a valid resource ID.)
 */
public int getIdentifier(String name, String defType, String defPackage) {
    return mResourcesImpl.getIdentifier(name, defType, defPackage);
}

註釋內容要點以下:

  • 該方法返回給定資源名稱的資源標識符(即資源ID);
  • 完整的資源名爲package:type/entry,若是資源名這個參數有完整地指定,後面的defType和defPackage能夠省略,如上面代碼中res.getIdentifier(packageName + ":drawable/ic_launcher", null, null);的形式;
  • 並不提倡使用該方法,由於經過資源ID訪問資源效率更高;
  • 若是沒有找到資源,則返回0,而0不是一個有效的資源ID。
  • 如代碼所示,該方法間接調用了AssetManager.class中的native方法
int getIdentifier(String name, String defType, String defPackage) {
    if (name == null) {
        throw new NullPointerException("name is null");
    }
    try {
        return Integer.parseInt(name);
    } catch (Exception e) {
        // Ignore
    }
    return mAssets.getResourceIdentifier(name, defType, defPackage);
}
/**
 * Retrieve the resource identifier for the given resource name.
 */
 /*package*/ native final int getResourceIdentifier(String type,
                                                       String name,
                                                       String defPackage);

參考資料:

  1. 訪問資源
  2. 應用資源概覽
  3. [Android] 關於Android的問號?和@符號的用法
  4. Android根據資源名獲取資源ID
相關文章
相關標籤/搜索