想做出1款出色的App,僅僅編寫(xiě)Java代碼還不夠。在代碼中調(diào)用資源(Resources ),如位圖(bitmaps)、布局資源( layout definitions)、UI中需要展現(xiàn)的字符串資源(user interface strings)、動(dòng)畫(huà)資源(animation instructions)等,可讓您的App更加出色。
本文將介紹Android中各種類(lèi)型的資源、和獲得資源的方式,如需訪問(wèn)官方原文,您可以點(diǎn)擊這個(gè)鏈接:《App Resources》。
在本小節(jié)中,將介紹可為App提供的資源類(lèi)型、如何存儲(chǔ)這些資源,和如作甚特定的裝備配置替換的資源 等。
您應(yīng)當(dāng)將字符串、圖片等資源從Java代碼中剝離出來(lái),這樣方便獨(dú)立地管理。另外,通過(guò)建立帶有特定后綴名的資源文件夾,系統(tǒng)可以自動(dòng)將這些文件夾中的資源設(shè)置到符合要求的裝備上(如分辨率、屏幕尺寸 等)。
所有的資源都寄存在res/
目錄下,下面是1個(gè)演示MyProject的工程結(jié)構(gòu):
MyProject/
src/
MyActivity.java
res/
drawable/
graphic.png
layout/
main.xml
info.xml
mipmap/
icon.png
values/
strings.xml
需要注意的是,mipmap/
目錄下應(yīng)寄存的是圖標(biāo)資源,而drawable/
目錄下寄存的是普通的圖片資源,下面的表格介紹了res/
目錄下可以寄存的子目錄名稱(chēng)和它們可以寄存的資源類(lèi)型。
子目錄名稱(chēng)(Directory) | 資源類(lèi)型(Resource Type) |
---|---|
animator/ |
定義屬性動(dòng)畫(huà)的XML文件 |
anim/ |
定義補(bǔ)間動(dòng)畫(huà)的XML文件 |
color/ |
定義色彩狀態(tài)列表的XML文件 |
drawable/ |
各種格式的位圖文件(.png, .9.png, .jpg, .gif),編譯為以下 Drawable 資源子類(lèi)型的 XML 文件:位圖文件(Bitmap files)、Nine-Patches圖片、State lists、Shapes、Animation drawables、Other drawables |
mipmap/ |
各種分辨率的啟動(dòng)圖標(biāo)資源 |
layout/ |
定義布局的XML文件 |
menu/ |
定義菜單資源的XML文件。如Options Menu、Context Menu、或Sub Menu 等 |
raw/ |
以原始格式保存的任意文件。使用InputStream 打開(kāi)這些資源,調(diào)用Resources.openRawResource() 方法傳入資源id,即:R.raw.filename .如需訪問(wèn)原始文件名和文件層次結(jié)構(gòu),則可以斟酌將某些資源保存在 assets/ 目錄下,保存在該目錄下文件的文件名不會(huì)映照到R類(lèi)中,即不會(huì)生成資源ID,如需獲得資源,需使用AssetManager 類(lèi)。 |
values/ |
包括字符串、整型數(shù)和色彩等簡(jiǎn)單值的 XML 文件,需要使用<resources> 作為根標(biāo)簽生命不同類(lèi)型的資源,如子標(biāo)簽為<string> 表示字符串資源、子標(biāo)簽<color> 表示色彩資源,映照到R類(lèi)分別為R.string 、R.color 。為了辨別這些不同標(biāo)簽的文件,應(yīng)在values/ 下建立不同的XML文件,如arrays.xml 中為數(shù)組資源,colors.xml 中為色彩資源,dimens.xml 中為尺寸資源,strings.xml 中為字符串資源,styles.xml 中為樣式資源 |
xml/ |
任何XML格式的文件,可以在程序運(yùn)行時(shí)調(diào)用Resources.getXML() 方法獲得到。 |
!請(qǐng)注意:請(qǐng)不要在res/
的根路徑下直接寄存資源文件,否則會(huì)引發(fā)異常。
在上表中定義的子目錄名稱(chēng)都是缺省目錄名稱(chēng),也就是說(shuō),定義在這些目錄名稱(chēng)下的資源不1定是程序需要加載的“上佳”資源,而是沒(méi)有最好資源時(shí)加載的“下策”資源。那末甚么是“最好”資源?Android裝備千差萬(wàn)別,有的屏幕大、有的分辨率高:若為1個(gè)高分辨率的大屏裝備加載缺省的圖片資源,1定有未被利用的屏幕空間;即使是型號(hào)相同的裝備,不同國(guó)家的用戶(hù)可能需要設(shè)置不同的語(yǔ)言:若為1個(gè)設(shè)置了“中文”主題語(yǔ)言的裝備加載“英文”的缺省資源,明顯不太適合。為不同類(lèi)型的裝備(屏幕、語(yǔ)言、分辨率 等等)專(zhuān)門(mén)提供不同的資源,稱(chēng)為“最好”資源。
幾近所有的app都會(huì)為適配各式各樣的裝備而提供相應(yīng)的資源,比如,您可能需要為不同屏幕密度的裝備提供不同的drawable資源、為設(shè)置了不同系統(tǒng)語(yǔ)言的裝備提供不同的string資源。在app運(yùn)行時(shí),程序會(huì)自動(dòng)檢測(cè)當(dāng)前裝備的配置信息,并為其加載適合的資源。
提供不同種類(lèi)資源的方式以下:
在res/
目錄下創(chuàng)建1個(gè)類(lèi)似于<resources_name>-<config_qualifier>
格式的子目錄:
其中<resources_name>
為資源的缺省子目錄名,如上1小節(jié)的表格所示;
<qualifier>
是1個(gè)限定符,表示為滿足該限定符條件的裝備加載資源。以下表所示。
您可以同時(shí)使用多個(gè)<qualifier>
限定符后綴,限定符之間用英文半角的破折號(hào)連接,即 “ - ”。這些限定符需要依照下面表格的順序排列,否則該資源文件夾將被疏忽。
res/
drawable/
icon.png
background.png
drawable-hdpi/
icon.png
background.png
其中限定符hdpi
表示該資源文件夾中的文件將被加載至具有高分辨率屏幕的裝備上。在這些不同的drawable文件夾下,圖片具有不同的分辨率,但是它們的名字相同。這樣就能夠使用1致的ID名將該資源援用至Java代碼中。
下表羅列了您可以添加的限定符名稱(chēng),再1次強(qiáng)調(diào),如有多個(gè)限定符,那末需要依照下表的順序排列。
資源類(lèi)型(Configuration) | 限定符舉例(Qualifier Values) | 描寫(xiě)(Description) |
---|---|---|
國(guó)家移動(dòng)代碼 和 網(wǎng)絡(luò)移動(dòng)代碼(MCC and MNC) | 舉例:mcc310、mcc310-mnc004、mcc208-mnc00 等 |
MCC=The mobile country code ,國(guó)家移動(dòng)代碼。后面可以有選擇性地跟隨MNC=mobile network code,即移動(dòng)網(wǎng)絡(luò)代碼。例如,mcc310 代表美國(guó)的國(guó)家移動(dòng)代碼(MCC),而mcc310-mnc004 代表美國(guó)的Verizon運(yùn)營(yíng)商;而mcc208-mnc00 代表法國(guó)的Orange運(yùn)營(yíng)商。如果裝備使用的是GSM網(wǎng)絡(luò),那末MCC 和 MNC的代碼可以在SIM卡上找到。 |
語(yǔ)言和地區(qū)(Language and region) | en、fr、en-rUS、fr-rFR、fr-rCA 等 |
其中兩個(gè)小寫(xiě)英文字母表示語(yǔ)言代碼,具體可參照ISO 639⑴ 標(biāo)準(zhǔn);由小寫(xiě)字母“r”開(kāi)頭、兩個(gè)大寫(xiě)字母緊隨其后的限定符表示地區(qū)碼,地區(qū)碼是1個(gè)可選限定符,您可以有選擇地加入,但是地區(qū)碼不能脫離語(yǔ)言代碼而獨(dú)立存在,具體可參照ISO 3166⑴-alpha⑵ 標(biāo)準(zhǔn)。當(dāng)用戶(hù)改變裝備的系統(tǒng)語(yǔ)言時(shí),系統(tǒng)加載的語(yǔ)言資源也應(yīng)當(dāng)隨之改變,具體可以參照我的后續(xù)翻譯,或直接點(diǎn)擊這個(gè)鏈接《Handling Runtime Changes》 。 |
布局方向(Layout Direction) | ldrtl 、ldltr |
ldrtl 表示布局的方式為從右向左(layout-direction-right-to-left),而ldltr 表示從左向右(layout-direction-left-to-right),這也是默許值。這個(gè)限定符可以添加到任何資源名后,如 layout、drawable, 或 values 等。!請(qǐng)注意:為了添加從右至左的布局資源,需要將supportsRtl 屬性設(shè)置為true,并且將targetSdkVersion 設(shè)置為17或以上。 |
最小寬度(smallestWidth) | 通式情勢(shì):sw<N>dp ;比如:sw320dp 、sw600dp 、sw720dp 等 |
表示屏幕的最小寬度。比如某個(gè)布局資源需要加載到最少600dp寬的裝備屏幕上,那末您只需要在res/layout-sw600dp/ 目錄下添加該布局文件便可,需要注意的是,屏幕最小寬度(smallestWidth )是1個(gè)裝備的固定屬性,它不會(huì)隨著用戶(hù)所持裝備的方向而改變( The smallestWidth is a fixed screen size characteristic of the device; the device’s smallestWidth does not change when the screen’s orientation changes)。通過(guò)android:requiresSmallestWidthDp 屬性可以指定程序支持的最小屏幕寬度。通過(guò)smallestScreenWidthDp 屬性可以查詢(xún)當(dāng)前裝備的最小寬度。 |
可用寬度(Available width) | 通式:w<N>dp ;比如:w720dp 、w1024dp 等 |
指定1個(gè)可用的最小寬度。這個(gè)屬性會(huì)隨著屏幕的橫豎變化而變化。通過(guò)screenWidthDp ,可以查詢(xún)當(dāng)前的屬性值。 |
可用高度(Available height) | 通式:h<N>dp ;比如:h720dp 、h1024dp 等 |
與可用寬度類(lèi)似。使用screenHeightDp 屬性可以查詢(xún)當(dāng)前的裝備的可用寬度。 |
屏幕大小(Screen size) | small 、normal 、large 、xlarge |
1般情況下使用xlarge ,表示屏幕尺寸接近720dpx960dp。!請(qǐng)注意:如果您的限定符尺寸超過(guò)了屏幕的實(shí)際尺寸,程序?qū)⒈罎ⅲ缒馁Y源都放在了large 文件夾下,而裝備的屏幕只有normal 大小。 |
屏幕縱橫比(Screen aspect) | long 、notlong |
long :寬屏,如 WQVGA、WVGA、FWVGA;notlong :非寬屏,如 QVGA、HVGA 和 VGA |
屏幕形狀(Round screen) | round 、notround |
round :圓形屏幕,如手表等裝備;notround :矩形屏幕,如手機(jī)或平板.該限定符于API23引入,通過(guò)isScreenRound() 可以判斷裝備的屏幕形狀是不是為圓形。 |
屏幕方向(Screen orientation) | port 、land |
port :屏幕為豎直方向;land :屏幕為水平方向 |
UI 模式(UI mode) | car 、desk 、television 、appliance 、watch |
car :車(chē)載系統(tǒng)的UI;desk :桌面系統(tǒng)的UI;television :電視的UI;appliance 無(wú)屏幕的裝備;watch :手表裝備。 |
夜間模式(Night mode) | night 、notnight |
night :晚間;notnight :白天。 |
屏幕像素密度(Screen pixel density (dpi)) | ldpi 、mdpi 、hdpi 、xhdpi 、xxhdpi 、xxxhdpi 、nodpi 、tvdpi 、anydpi |
主流:xhdpi :接近320dpi;xxhdpi :接近480dpi;xxxhdpi :只針對(duì)啟動(dòng)圖標(biāo),接近640dpi。 |
觸屏類(lèi)型(Touchscreen type) | notouch 、finger |
notouch :裝備無(wú)觸屏;finger :裝備包括觸摸屏。 |
鍵盤(pán)可用性(Keyboard availability) | keysexposed 、keyshidden 、keyssoft |
keysexposed :裝備具有可用的鍵盤(pán);keyshidden :裝備具有可用的硬鍵盤(pán),但它處于隱藏狀態(tài),且裝備沒(méi)有啟用軟鍵盤(pán);keyssoft :裝備已啟用軟鍵盤(pán)(不管是不是可見(jiàn))。 |
主要文本輸入法(Primary text input method) | nokeys 、qwerty 、12key |
nokeys :裝備沒(méi)有用于文本輸入的硬按鍵;qwerty :裝備具有標(biāo)準(zhǔn)硬鍵盤(pán)(不管是不是對(duì)用戶(hù)可見(jiàn));12key :裝備具有 12 鍵硬鍵盤(pán)(不管是不是對(duì)用戶(hù)可見(jiàn))。 |
導(dǎo)航鍵可用性(Navigation key availability) | navexposed 、navhidden |
navexposed :導(dǎo)航鍵可供用戶(hù)使用;navhidden:導(dǎo)航鍵不可用(例如,位于密封蓋子后面)。` |
主要非觸摸導(dǎo)航方法(Primary non-touch navigation method) | nonav 、dpad 、trackball 、wheel |
nonav :除使用觸摸屏之外,裝備沒(méi)有其他導(dǎo)航設(shè)施;dpad :裝備具有用于導(dǎo)航的方向鍵;trackball :裝備具有用于導(dǎo)航的軌跡球;wheel :裝備具有用于導(dǎo)航的方向盤(pán)(不常見(jiàn))。 |
平臺(tái)版本(API 級(jí)別)(Platform Version (API level)) | v3 、v4 、v7 等 |
裝備支持的 API 級(jí)別。例如,v1 對(duì)應(yīng)于 API 級(jí)別 1(帶有 Android 1.0 或更高版本系統(tǒng)的裝備),v4 對(duì)應(yīng)于 API 級(jí)別 4(帶有 Android 1.6 或更高版本系統(tǒng)的裝備)。 |
!請(qǐng)注意:其實(shí)不是所有的限定符都是在API 1 中加入的,所以,為了避免某些高版本的限定符不被加載(裝備版本較低),請(qǐng)務(wù)必為所有資源設(shè)置默許的資源文件夾。
drawable-en-rUS-land
,這表示當(dāng)裝備處于橫屏狀態(tài)且系統(tǒng)語(yǔ)言為英文時(shí)加載該文件夾下的文件。多個(gè)限定符必須依照上表的順序排列,比如:
drawable-hdpi-port/
,毛病;
drawable-port-hdpi/
,正確。
每種資源文件夾不可被嵌套,比如:res/drawable/drawable-en/
。
文件夾的名稱(chēng)是大小寫(xiě)不敏感的(case-insensitive)。在處理之前,資源編譯器會(huì)將目錄名稱(chēng)轉(zhuǎn)換為小寫(xiě),以免不辨別大小寫(xiě)的文件系統(tǒng)出現(xiàn)問(wèn)題。 名稱(chēng)中使用的任何大寫(xiě)字母只是為了便于認(rèn)讀。
對(duì)每種限定符類(lèi)型,僅支持1個(gè)值。例如,若要對(duì)西班牙語(yǔ)和法語(yǔ)使用相同的 Drawable 文件,則您肯定不能具有名為 drawable-rES-rFR/
的目錄,而是需要兩個(gè)包括相應(yīng)文件的資源目錄,如 drawable-rES/
和 drawable-rFR/
。但是,實(shí)際上您無(wú)需將相同的文件都復(fù)制到這兩個(gè)位置。相反,您可以創(chuàng)建指向資源的別名。
假定您有1個(gè)利用圖標(biāo) icon.png
,并且需要不同區(qū)域設(shè)置的獨(dú)特版本。 但是,加拿大英語(yǔ)和加拿大法語(yǔ)這兩種區(qū)域設(shè)置需要使用同1版本。 您可能會(huì)認(rèn)為需要將相同的圖象復(fù)制到加拿大英語(yǔ)和加拿大法語(yǔ)對(duì)應(yīng)的資源目錄中,但事實(shí)并不是如此。 相反,您可以將用于2者的圖象另存為 icon_ca.png
(除 icon.png
之外的任何名稱(chēng)),并將其放入默許 res/drawable/
目錄中。然后,在 res/drawable-en-rCA/
和 res/drawable-fr-rCA/
中創(chuàng)建 icon.xml
文件,使用 <bitmap>
標(biāo)簽; 元素援用 icon_ca.png
資源。這樣,您只需存儲(chǔ) PNG 文件的1個(gè)版本和兩個(gè)指向該版本的小型 XML 文件。(XML 文件示例以下。)
<?xml version="1.0" encoding="utf⑻"?>
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
android:src="@drawable/icon_ca" />
如果將此文件另存為 icon.xml
(例如,在備用資源目錄中,另存為 res/drawable-en-rCA/
),則會(huì)編譯到可作為 R.drawable.icon
援用的資源中,但實(shí)際上它是 R.drawable.icon_ca
資源(保存在 res/drawable/
中)的別名。
要?jiǎng)?chuàng)建指向現(xiàn)有布局的別名,請(qǐng)使用包裝在 <merge>
標(biāo)簽中的 <include>
元素。例如:
<?xml version="1.0" encoding="utf⑻"?>
<merge>
<include layout="@layout/main_ltr"/>
</merge>
如果將此文件另存為 main.xml
,則會(huì)編譯到可作為 R.layout.main
援用的資源中,但實(shí)際上它是 R.layout.main_ltr
資源的別名.
要?jiǎng)?chuàng)建指向現(xiàn)有字符串的別名,只需將所需字符串的資源 ID 用作新字符串的值便可。例如:
<?xml version="1.0" encoding="utf⑻"?>
<resources>
<string name="hello">Hello</string>
<string name="hi">@string/hello</string>
</resources>
R.string.hi
資源現(xiàn)在是 R.string.hello
的別名。
其他簡(jiǎn)單數(shù)值資源的別名使用與Strings類(lèi)似,例如:
<?xml version="1.0" encoding="utf⑻"?>
<resources>
<color name="red">#f00</color>
<color name="highlight">@color/red</color>
</resources>
請(qǐng)務(wù)必提供默許資源。比如,如果利用支持多種語(yǔ)言,請(qǐng)始終包括不帶語(yǔ)言和區(qū)域限定符的 values/
目錄(用于保存字符串)。相反,如果您將所有字符串放入帶有語(yǔ)言和區(qū)域限定符的目錄中,則在語(yǔ)言設(shè)置不支持您的字符串的裝備上運(yùn)行利用時(shí),利用將會(huì)崩潰。
一樣,如果您根據(jù)屏幕方向提供不同的布局資源,則應(yīng)選擇1個(gè)方向作為默許方向。 例如,不要在 layout-land/
和 layout-port/
中分別提供橫向和縱向的布局資源,而是保存其中之1作為默許設(shè)置,例如:layout/
用于橫向,layout-port/
用于縱向。
當(dāng)您要求要為其提供備用資源的資源時(shí),Android 會(huì)根據(jù)當(dāng)前的裝備配置選擇要在運(yùn)行時(shí)使用的備用資源。假定Drawable 目錄分別包括相同圖象的不同版本:
drawable/
drawable-en/
drawable-fr-rCA/
drawable-en-port/
drawable-en-notouch-12key/
drawable-port-ldpi/
drawable-port-notouch-12key/
假定某個(gè)裝備的配置參數(shù)以下:
Locale = en-GB
Screen orientation = port
Screen pixel density = hdpi
Touchscreen type = notouch
Primary text input method = 12key
裝備終究加載的是drawable-en-port
目錄下的資源。
系統(tǒng)使用以下邏輯決定要使用的資源:
1、 淘汰與裝備配置沖突的資源文件:
drawable-fr-rCA/
目錄與 en-GB
區(qū)域設(shè)置沖突,因此被淘汰。
drawable/
drawable-en/
drawable-fr-rCA/ //被淘汰
drawable-en-port/
drawable-en-notouch-12key/
drawable-port-ldpi/
drawable-port-notouch-12key/
2、 選擇上表中(下1個(gè))優(yōu)先級(jí)最高的限定符。(先從 MCC 開(kāi)始,然后下移。)
3、 是不是有資源目錄包括此限定符?
若無(wú),請(qǐng)返回到第 2 步,看看下1個(gè)限定符。(在該示例中,除非到達(dá)語(yǔ)言限定符,否則答案始終為“否”。)
若有,請(qǐng)繼續(xù)履行第 4 步。
4、淘汰不含此限定符的資源目錄。在該示例中,系統(tǒng)會(huì)淘汰所有不含語(yǔ)言限定符的目錄:
drawable/ //淘汰
drawable-en/
drawable-en-port/
drawable-en-notouch-12key/
drawable-port-ldpi/ //淘汰
drawable-port-notouch-12key/ //淘汰
5、返回并重復(fù)第 2 步、第 3 步和第 4 步,直到只剩下1個(gè)目錄為止。在此示例中,屏幕方向是下1個(gè)判斷是不是匹配的限定符。因此,未指定屏幕方向的資源被淘汰:
drawable-en/ //淘汰
drawable-en-port/
drawable-en-notouch-12key/ //淘汰
流程圖以下所示:
!請(qǐng)注意:注:限定符的優(yōu)先順序(上表 中)比與裝備完全匹配的限定符數(shù)量更加重要。例如,在上面的第 4 步中,列表剩下的最后選項(xiàng)包括3個(gè)與裝備完全匹配的限定符(方向、觸摸屏類(lèi)型和輸入法),而 drawable-en
只有1個(gè)匹配參數(shù)(語(yǔ)言)。但是,語(yǔ)言的優(yōu)先順序高于其他兩個(gè)限定符,因此 drawable-port-notouch⑴2key
被淘汰。
您在利用中提供資源后,可通過(guò)援用其資源 ID 來(lái)獲得該資源,所有的資源ID均被定義在R類(lèi) 中。
資源 ID 由以下部份組成:
string
、drawable
和 layout
。訪問(wèn)資源的方法有兩種:
在代碼中:使用來(lái)自 R 類(lèi)的某個(gè)子類(lèi)的靜態(tài)整型數(shù),例如:
R.string.hello
string 是資源類(lèi)型,hello 是資源名稱(chēng)。
在 XML 中:使用一樣與您 R 類(lèi)中定義的資源 ID 對(duì)應(yīng)的特殊 XML 語(yǔ)法,如:
@string/hello
string 是資源類(lèi)型,hello 是資源名稱(chēng)。
ImageView imageView = (ImageView) findViewById(R.id.myimageview);
imageView.setImageResource(R.drawable.myimage);
// Load a background for the current screen from a drawable resource
getWindow().setBackgroundDrawableResource(R.drawable.my_background_image) ;
// Set the Activity title by getting a string from the Resources object, because
// this method requires a CharSequence rather than a resource ID
getWindow().setTitle(getResources().getText(R.string.main_title));
// Load a custom layout for the current screen
setContentView(R.layout.main_screen);
// Set a slide in animation by getting an Animation from the Resources object
mFlipper.setInAnimation(AnimationUtils.loadAnimation(this,
R.anim.hyperspace_in));
// Set the text on a TextView object using a resource ID
TextView msgTextView = (TextView) findViewById(R.id.msg);
msgTextView.setText(R.string.hello_message);
<Button
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/submit" />
如果您具有以下資源文件,其中包括1個(gè)色彩資源和1個(gè)字符串資源:
<?xml version="1.0" encoding="utf⑻"?>
<resources>
<color name="opaque_red">#f00</color>
<string name="hello">Hello!</string>
</resources>
您可以在以下布局文件中使用這些資源來(lái)設(shè)置文本色彩和文本字符串:
<?xml version="1.0" encoding="utf⑻"?>
<EditText xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:textColor="@color/opaque_red"
android:text="@string/hello" />
要援用系統(tǒng)資源,您需要加入包名稱(chēng)。 例如:
<?xml version="1.0" encoding="utf⑻"?>
<EditText xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:textColor="@android:color/secondary_text_dark"
android:text="@string/hello" />
要援用樣式屬性,名稱(chēng)語(yǔ)法幾近與普通資源格式完全相同,只不過(guò)將 at 符號(hào) (@) 改成問(wèn)號(hào) (?),資源類(lèi)型部份為可選項(xiàng)。 例如:
?[<package_name>:][<resource_type>/]<resource_name>
例如,您可以通過(guò)以下代碼援用1個(gè)屬性,將文本色彩設(shè)置為與系統(tǒng)風(fēng)格主題的“主要”文本色彩匹配:
<EditText id="text"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textColor="?android:textColorSecondary"
android:text="@string/hello_world" />
在以上代碼中,android:textColor
屬性表示當(dāng)前風(fēng)格主題中某個(gè)樣式屬性的名稱(chēng)。
Android 包括許多標(biāo)準(zhǔn)資源,例如樣式、風(fēng)格主題和布局。要訪問(wèn)這些資源,請(qǐng)通過(guò) android 包名稱(chēng)限定您的資源援用。例如,您可以將 Android 提供的布局資源用于 ListAdapter
中的列表項(xiàng):
setListAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, myarray));
simple_list_item_1
是平臺(tái)為 ListView
中的項(xiàng)目定義的布局資源。您可使用它,而沒(méi)必要自行創(chuàng)建列表項(xiàng)布局。
有些裝備配置可能會(huì)在運(yùn)行時(shí)產(chǎn)生變化(例如屏幕方向、鍵盤(pán)可用性及語(yǔ)言)。 產(chǎn)生這類(lèi)變化時(shí),Android 會(huì)重啟正在運(yùn)行的 Activity(前后調(diào)用 onDestroy() 和 onCreate())。重啟行動(dòng)旨在通過(guò)利用與新裝備配置匹配的備用資源自動(dòng)重新加載您的利用,來(lái)幫助它適應(yīng)新配置。
要妥善處理重啟行動(dòng),Activity 必須通過(guò)常規(guī)的Activity 生命周期恢復(fù)其之前的狀態(tài),在 Activity 生命周期中,Android 會(huì)在燒毀 Activity 之前調(diào)用 onSaveInstanceState(),以便您保存有關(guān)利用狀態(tài)的數(shù)據(jù)。 然后,您可以在 onCreate() 或 onRestoreInstanceState() 期間恢復(fù) Activity 狀態(tài)。
但是,重啟利用并恢復(fù)大量數(shù)據(jù)不但本錢(qián)高昂,而且給用戶(hù)留下糟的使用體驗(yàn)。 在這類(lèi)情況下,您有兩個(gè)其他選擇:
在配置變更期間保存對(duì)象:
自行處理配置變更:
如果重啟 Activity 需要恢復(fù)大量數(shù)據(jù)、重新建立網(wǎng)絡(luò)連接或履行其他密集操作,那末因配置變更而引發(fā)的完全重啟可能會(huì)給用戶(hù)留下利用運(yùn)行緩慢的體驗(yàn)。 另外,依托系統(tǒng)通過(guò) onSaveInstanceState() 回調(diào)為您保存的 Bundle,可能沒(méi)法完全恢復(fù) Activity 狀態(tài),由于它 并不是設(shè)計(jì)用于攜帶大型對(duì)象(例如位圖),而且其中的數(shù)據(jù)必須先序列化,再進(jìn)行反序列化, 這可能會(huì)消耗大量?jī)?nèi)存并使得配置變更速度緩慢。
在這類(lèi)情況下,如果 Activity 因配置變更而重啟,則可通過(guò)保存 Fragment 來(lái)減輕重新初始化 Activity 的負(fù)擔(dān)。此Fragment可能包括對(duì)您要保存的有狀態(tài)對(duì)象的援用。
當(dāng) Android 系統(tǒng)因配置變更而關(guān)閉 Activity 時(shí),不會(huì)燒毀您已標(biāo)記為要保存的 Activity 的Fragment。您可以將此類(lèi)Fragment添加到 Activity 以保存有狀態(tài)的對(duì)象。
要在運(yùn)行時(shí)配置變更期間將有狀態(tài)的對(duì)象保存在Fragment中,請(qǐng)履行以下操作:
1、繼承 Fragment 類(lèi)并聲明對(duì)有狀態(tài)對(duì)象的援用。
2、在創(chuàng)建Fragment后調(diào)用 setRetainInstance(boolean)
。
3、將Fragment添加到 Activity。
4、重啟 Activity 后,使用 FragmentManager 檢索Fragment。
public class RetainedFragment extends Fragment {
// data object we want to retain
private MyDataObject data;
// this method is only called once for this fragment
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// retain this fragment
setRetainInstance(true);
}
public void setData(MyDataObject data) {
this.data = data;
}
public MyDataObject getData() {
return data;
}
}
!請(qǐng)注意:雖然您可以存儲(chǔ)任何對(duì)象,但是切勿傳遞與 Activity 綁定的對(duì)象,例如,Drawable、Adapter、View 或其他任何與 Context 關(guān)聯(lián)的對(duì)象。否則,它將泄漏原始 Activity 實(shí)例的所有視圖和資源。 (泄漏資源意味著利用將繼續(xù)持有這些資源,但是沒(méi)法對(duì)其進(jìn)行垃圾回收,因此可能會(huì)丟失大量?jī)?nèi)存。)
然后,使用 FragmentManager 將Fragment添加到 Activity。在運(yùn)行時(shí)配置變更期間再次啟動(dòng) Activity 時(shí),您可以取得Fragment中的數(shù)據(jù)對(duì)象。 例如,按以下所示定義 Activity:
public class MyActivity extends Activity {
private RetainedFragment dataFragment;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// find the retained fragment on activity restarts
FragmentManager fm = getFragmentManager();
dataFragment = (DataFragment) fm.findFragmentByTag(“data”);
// create the fragment and data the first time
if (dataFragment == null) {
// add the fragment
dataFragment = new DataFragment();
fm.beginTransaction().add(dataFragment, “data”).commit();
// load the data from the web
dataFragment.setData(loadMyData());
}
// the data is available in dataFragment.getData()
...
}
@Override
public void onDestroy() {
super.onDestroy();
// store the data in the fragment
dataFragment.setData(collectMyLoadedData());
}
}
如果利用在特定配置變更期間無(wú)需更新資源,并且因性能限制您需要盡可能避免重啟,則可聲明 Activity 將自行處理配置變更,這樣可以禁止系統(tǒng)重啟 Activity。
!請(qǐng)注意:自行處理配置變更可能致使備用資源的使用更加困難,由于系統(tǒng)不會(huì)為您自動(dòng)利用這些資源。 只能在您必須避免Activity因配置變更而重啟這1萬(wàn)般無(wú)奈的情況下,才斟酌采取自行處理配置變更這類(lèi)方法,而且對(duì)大多數(shù)利用其實(shí)不建議使用此方法。
要聲明由 Activity
處理配置變更,請(qǐng)?jiān)谇鍐挝募芯庉嬒鄳?yīng)的 <activity>
標(biāo)簽,以包括 android:configChanges
屬性和代表要處理的配置的值。android:configChanges
屬性的文檔中列出了該屬性的可能值(最經(jīng)常使用的值包括 “orientation
” 和 “keyboardHidden
“,分別用于避免因屏幕方向和可用鍵盤(pán)改變而致使重啟)。您可以在該屬性中聲明多個(gè)配置值,方法是用 ” | ” 字符分隔這些配置值:
<activity android:name=".MyActivity"
android:configChanges="orientation|keyboardHidden"
android:label="@string/app_name">
當(dāng)orientation 或 keyboardHidden
配置產(chǎn)生變化時(shí),MyActivity 不會(huì)重啟。相反,MyActivity
會(huì)收到對(duì) onConfigurationChanged()
的調(diào)用。向此方法傳遞 Configuration
對(duì)象指定新裝備配置。您可以通過(guò)讀取 Configuration
中的字段,肯定新配置,然后通過(guò)更新界面中使用的資源進(jìn)行適當(dāng)?shù)母摹U{(diào)用此方法時(shí),Activity
的 Resources
對(duì)象會(huì)相應(yīng)地進(jìn)行更新,以根據(jù)新配置返回資源,這樣,您就可以夠在系統(tǒng)不重啟 Activity
的情況下輕松重置 UI 的元素。
!請(qǐng)注意:從 Android 3.2(API 級(jí)別 13)開(kāi)始,當(dāng)裝備在縱向和橫向之間切換時(shí),“屏幕尺寸”也會(huì)產(chǎn)生變化。因此,在開(kāi)發(fā)針對(duì) API 級(jí)別 13 或更高版本系統(tǒng)的利用時(shí),若要避免由于裝備方向改變而致使運(yùn)行時(shí)重啟,則除 “orientation
” 值之外,您還必須添加 “screenSize
” 值。即,您必須聲明 android:configChanges="orientation|screenSize"
。
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
// Checks the orientation of the screen
if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
Toast.makeText(this, "landscape", Toast.LENGTH_SHORT).show();
} else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT){
Toast.makeText(this, "portrait", Toast.LENGTH_SHORT).show();
}
}