前言:安卓開發(fā)中我們時(shí)常會(huì)需要引用一些特殊的資源,比如設(shè)置一些可點(diǎn)擊組件的波紋效果時(shí),我們會(huì)用到:android:foreground="?attr/selectableItemBackground",但是這些引用方式之間有哪些區(qū)別呢?
獲取資源
首先來復(fù)習(xí)一下安卓中獲取資源的幾種方式。
@[<package_name>:]<resource_type>/<resource_name>
這種方式是最為常見的,直接獲取對(duì)應(yīng)的包下的資源,一般在相同的包下,可以省略包名,比如為 TextView 設(shè)置文字時(shí),就可以通過這樣的方式來獲取我們應(yīng)用內(nèi)定義的 string 資源:
android:text="@string/hello"
另一種獲取資源的方式是通過引用 style 屬性。
android:textColor="?colorAccent"
通過這種方式,我們可以獲取到當(dāng)前應(yīng)用主題下的 style 屬性值,這些屬性值一般可以在屬性文件夾 values 下找到。這里由于當(dāng)前使用的是 Theme.AppCompat 下的主題,這是 com.android.support:appcompat-v7 下的 Theme,所以 colorAccent 會(huì)指向該庫下的 values 文件:

關(guān)于資源引用的方式,具體請(qǐng)參考官方文檔:Accessing your app resources
01 @android: 引用安卓內(nèi)建的系統(tǒng)資源
除了引用自己應(yīng)用中的資源外,我們還可以通過指定引用時(shí)的包名為 android 來獲取安卓平臺(tái)下的一些系統(tǒng)資源,舉個(gè)例子:
android:background=“@android:drawable/ic_menu_delete”
我們知道當(dāng)新建一個(gè)項(xiàng)目的時(shí)候,必須在 gradle 中設(shè)置 compileSdkVersion 來指定編譯我們應(yīng)用的 SDK 版本,這也決定了我們能調(diào)用哪個(gè) level 的 API,同時(shí)也表明了哪個(gè) level 的系統(tǒng)資源可供我們使用,比如如果指定的 compileSdkVersion 是 27,那么我們調(diào)用的就是 Android 8.1 下的系統(tǒng)資源。

02 ?attr/: 引用應(yīng)用內(nèi)的屬性資源
通過這種方式可以讓我們間接地使用應(yīng)用內(nèi)的某些資源。我們知道當(dāng)我們自定義View的時(shí)候,一般會(huì)需要自定義一些屬性資源,通常我們會(huì)在 values/ 文件夾下建一個(gè) attrs 文件,在這里保存一些我們自己的 style 屬性,其實(shí)這些屬性就可以通過 ?attr/ 這種方式來引用了。比如我在 styles 里定義了一個(gè)屬性:
<attr name="colorReallyGreen" format="color"/>
定義完之后,我就可以直接在 layout 中通過引用的方式去使用這個(gè)屬性了:
android:background="?attr/colorReallyGreen"
當(dāng)然,要想讓該屬性起作用還需要在 Theme 下指定值:

另外,由于在 layout 中,可以自動(dòng)識(shí)別出當(dāng)前所需的是屬性資源,所以可以省略 attr/ 而直接使用 ?colorReallyGreen 就可以了。
03 ?android: 引用系統(tǒng)內(nèi)建的屬性資源
了解了前兩種資源引用的方式后,?android 這種引用資源的方式也就不難理解了。與 ?attr/ 類似,通過這種方式可以直接訪問到安卓內(nèi)建的屬性資源,只不過是省略了 attr/ 而已。比如給 TextView 引用一個(gè)系統(tǒng)內(nèi)的 style buttonStyleSmall:
<TextView
style="?android:buttonStyleSmall"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="You Are Beautiful" />
然后 TextView 就變成了這樣:

當(dāng)然如果你嘗試去掉 android 包名之后,發(fā)現(xiàn)該屬性還是可以起作用,說明應(yīng)用內(nèi)也是可以引用這個(gè)資源的。那么這是不是意味著絕大部分屬性資源都不需要加 android 包名呢?其實(shí)我覺得是這樣的,因?yàn)榧恿税笃鋵?shí)限制反而會(huì)更多,比如有些內(nèi)建屬性資源是針對(duì)某個(gè) API level 以上的 Android 平臺(tái)才可以使用的,當(dāng)然這也與你當(dāng)前使用的 Theme 有關(guān)。
結(jié)語
總結(jié)下,獲取資源有兩種方式,一種是通過直接引用(使用 @),一種是通過 style 屬性(使用 ?,除了自定義屬性外,引用的資源類型和當(dāng)前使用的主題有關(guān))。另外系統(tǒng)中內(nèi)置了不少資源,學(xué)會(huì)合理利用它們可以幫助我們節(jié)約不少時(shí)間,建議大家花點(diǎn)精力自己探索下。
參考: