Android屏幕適配出現(xiàn)的原因

在我們學(xué)習(xí)如何進(jìn)行屏幕適配之前惯殊,我們需要先了解下為什么Android需要進(jìn)行屏幕適配。

由于Android系統(tǒng)的開(kāi)放性土思,任何用戶、開(kāi)發(fā)者崎岂、OEM廠商闪湾、運(yùn)營(yíng)商都可以對(duì)Android進(jìn)行定制,修改成他們想要的樣子途样。

但是這種“碎片化”到底到達(dá)什么程度呢?

在2012年何暇,OpenSignalMaps(以下簡(jiǎn)稱OSM)發(fā)布了第一份Android碎片化報(bào)告,統(tǒng)計(jì)數(shù)據(jù)表明条辟,

2012年,支持Android的設(shè)備共有3997種捂贿。

2013年,支持Android的設(shè)備共有11868種。

2014年了牛,支持Android的設(shè)備共有18796種。

下面這張圖片所顯示的內(nèi)容足以充分說(shuō)明當(dāng)今Android系統(tǒng)碎片化問(wèn)題的嚴(yán)重性甫窟,因?yàn)樵搱D片中的每一個(gè)矩形都代表著一種Android設(shè)備蛙婴。

而隨著支持Android系統(tǒng)的設(shè)備(手機(jī)、平板街图、電視、手表)的增多餐济,設(shè)備碎片化、品牌碎片化醉冤、系統(tǒng)碎片化篙悯、傳感器碎片化和屏幕碎片化的程度也在不斷地加深。而我們今天要探討的鸽照,則是對(duì)我們開(kāi)發(fā)影響比較大的——屏幕的碎片化。

下面這張圖是Android屏幕尺寸的示意圖移宅,在這張圖里面,藍(lán)色矩形的大小代表不同尺寸糠悼,顏色深淺則代表所占百分比的大小浅乔。

而與之相對(duì)應(yīng)的铝条,則是下面這張圖席噩。這張圖顯示了IOS設(shè)備所需要進(jìn)行適配的屏幕尺寸和占比。

當(dāng)然悼枢,這張圖片只是4,4s,5,5c,5s和平板的尺寸,現(xiàn)在還應(yīng)該加上新推出的iphone6和plus莹妒,但是和Android的屏幕碎片化程度相比而言绰上,還是差的太遠(yuǎn)。

詳細(xì)的統(tǒng)計(jì)數(shù)據(jù)請(qǐng)到這里查看蜈块。

現(xiàn)在你應(yīng)該很清楚為什么要對(duì)Android的屏幕進(jìn)行適配了吧?屏幕尺寸這么多爽哎,為了讓我們開(kāi)發(fā)的程序能夠比較美觀的顯示在不同尺寸信峻、分辨率、像素密度(這些概念我會(huì)在下面詳細(xì)講解)的設(shè)備上盹舞,那就要在開(kāi)發(fā)的過(guò)程中進(jìn)行處理,至于如何去進(jìn)行處理踢步,這就是我們今天的主題了。

但是在開(kāi)始進(jìn)入主題之前述雾,我們?cè)賮?lái)探討一件事情兼丰,那就是Android設(shè)備的屏幕尺寸,從幾寸的智能手機(jī)鳍征,到10寸的平板電腦,再到幾十寸的數(shù)字電視匣掸,我們應(yīng)該適配哪些設(shè)備呢?

其實(shí)這個(gè)問(wèn)題不應(yīng)該這么考慮碰酝,因?yàn)閷?duì)于具有相同像素密度的設(shè)備來(lái)說(shuō),像素越高铛嘱,尺寸就越大碱璃,所以我們可以換個(gè)思路,將問(wèn)題從單純的尺寸大小轉(zhuǎn)換到像素大小和像素密度的角度來(lái)嵌器。

下圖是2014年初谐丢,友盟統(tǒng)計(jì)的占比5%以上的6個(gè)主流分辨率,可以看出讥珍,占比最高的是480*800窄瘟,320*480的設(shè)備竟然也占據(jù)了很大比例蹄葱,但是和半年前的數(shù)據(jù)相比較,中低分辨率(320*480图云、480*800)的比例在減少,而中高分辨率的比例則在不斷地增加克婶。雖然每個(gè)分辨率所占的比例在變化丹泉,但是總的趨勢(shì)沒(méi)變情萤,還是這六種摹恨,只是分辨率在不斷地提高。

所以說(shuō)睬塌,我們只要盡量適配這幾種分辨率歇万,就可以在大部分的手機(jī)上正常運(yùn)行了勋陪。

當(dāng)然了,這只是手機(jī)的適配诅愚,對(duì)于平板設(shè)備(電視也可以看做是平板),我們還需要一些其他的處理违孝。

好了雌桑,到目前為止,我們已經(jīng)弄清楚了Android開(kāi)發(fā)為什么要進(jìn)行適配校坑,以及我們應(yīng)該適配哪些對(duì)象,接下來(lái)膏斤,終于進(jìn)入我們的正題了邪驮!

首先,我們先要學(xué)習(xí)幾個(gè)重要的概念毅访。

重要概念

什么是屏幕尺寸、屏幕分辨率敞映、屏幕像素密度磷斧?

什么是dp、dip弛饭、dpi、sp档桃、px憔晒?他們之間的關(guān)系是什么蔑舞?

什么是mdpi嘹屯、hdpi、xdpi州弟、xxdpi?如何計(jì)算和區(qū)分拯杠?

在下面的內(nèi)容中我們將介紹這些概念啃奴。

屏幕尺寸

屏幕尺寸指屏幕的對(duì)角線的長(zhǎng)度,單位是英寸畔咧,1英寸=2.54厘米

比如常見(jiàn)的屏幕尺寸有2.4揖膜、2.8梅桩、3.5、3.7宿百、4.2、5.0雀费、5.5痊焊、6.0等

屏幕分辨率

屏幕分辨率是指在橫縱向上的像素點(diǎn)數(shù),單位是px薄啥,1px=1個(gè)像素點(diǎn)。一般以縱向像素*橫向像素刁愿,如1960*1080到逊。

屏幕像素密度

屏幕像素密度是指每英寸上的像素點(diǎn)數(shù)滤钱,單位是dpi脑题,即“dot per inch”的縮寫(xiě)。屏幕像素密度與屏幕尺寸和屏幕分辨率有關(guān)停团,在單一變化條件下掏熬,屏幕尺寸越小、分辨率越高旗芬,像素密度越大,反之越小幔嫂。

dp誊薄、dip、dpi呢蔫、sp、px

px我們應(yīng)該是比較熟悉的绽昏,前面的分辨率就是用的像素為單位俏脊,大多數(shù)情況下,比如UI設(shè)計(jì)爷贫、Android原生API都會(huì)以px作為統(tǒng)一的計(jì)量單位沸久,像是獲取屏幕寬高等。

dip和dp是一個(gè)意思卷胯,都是Density Independent Pixels的縮寫(xiě),即密度無(wú)關(guān)像素挺峡,上面我們說(shuō)過(guò),dpi是屏幕像素密度橱赠,假如一英寸里面有160個(gè)像素,這個(gè)屏幕的像素密度就是160dpi宰啦,那么在這種情況下饼拍,dp和px如何換算呢?在Android中漓柑,規(guī)定以160dpi為基準(zhǔn)叨吮,1dip=1px,如果密度是320dpi茶鉴,則1dip=2px,以此類推嫩絮。

假如同樣都是畫(huà)一條320px的線围肥,在480*800分辨率手機(jī)上顯示為2/3屏幕寬度蜂怎,在320*480的手機(jī)上則占滿了全屏,如果使用dp為單位杠步,在這兩種分辨率下,160dp都顯示為屏幕一半的長(zhǎng)度朵锣。這也是為什么在Android開(kāi)發(fā)中甸私,寫(xiě)布局的時(shí)候要盡量使用dp而不是px的原因。

而sp诬烹,即scale-independent pixels,與dp類似绞吁,但是可以根據(jù)文字大小首選項(xiàng)進(jìn)行放縮,是設(shè)置字體大小的御用單位颜说。

mdpi汰聋、hdpi、xdpi马僻、xxdpi

其實(shí)之前還有個(gè)ldpi韭邓,但是隨著移動(dòng)設(shè)備配置的不斷升級(jí),這個(gè)像素密度的設(shè)備已經(jīng)很罕見(jiàn)了女淑,所在現(xiàn)在適配時(shí)不需考慮。

mdpi屈张、hdpi袱巨、xdpi、xxdpi用來(lái)修飾Android中的drawable文件夾及values文件夾愉老,用來(lái)區(qū)分不同像素密度下的圖片和dimen值。

那么如何區(qū)分呢焰盗?Google官方指定按照下列標(biāo)準(zhǔn)進(jìn)行區(qū)分:

在進(jìn)行開(kāi)發(fā)的時(shí)候咒林,我們需要把合適大小的圖片放在合適的文件夾里面。下面以圖標(biāo)設(shè)計(jì)為例進(jìn)行介紹澎粟。

在設(shè)計(jì)圖標(biāo)時(shí),對(duì)于五種主流的像素密度(MDPI捌议、HDPI、XHDPI倦逐、XXHDPI 和 XXXHDPI)應(yīng)按照 2:3:4:6:8 的比例進(jìn)行縮放宫补。例如,一個(gè)啟動(dòng)圖標(biāo)的尺寸為48x48 dp健民,這表示在 MDPI 的屏幕上其實(shí)際尺寸應(yīng)為 48x48 px贫贝,在 HDPI 的屏幕上其實(shí)際大小是 MDPI 的 1.5 倍 (72x72 px),在 XDPI 的屏幕上其實(shí)際大小是 MDPI 的 2 倍 (96x96 px)稚晚,依此類推。

雖然 Android 也支持低像素密度 (LDPI) 的屏幕鸳劳,但無(wú)需為此費(fèi)神也搓,系統(tǒng)會(huì)自動(dòng)將 HDPI 尺寸的圖標(biāo)縮小到 1/2 進(jìn)行匹配。

下圖為圖標(biāo)的各個(gè)屏幕密度的對(duì)應(yīng)尺寸:

解決方案

支持各種屏幕尺寸

使用wrap_content幔摸、match_parent颤练、weight

要確保布局的靈活性并適應(yīng)各種尺寸的屏幕,應(yīng)使用 “wrap_content” 和 “match_parent” 控制某些視圖組件的寬度和高度昔案。

使用 “wrap_content”电媳,系統(tǒng)就會(huì)將視圖的寬度或高度設(shè)置成所需的最小尺寸以適應(yīng)視圖中的內(nèi)容匾乓,而 “match_parent”(在低于 API 級(jí)別 8 的級(jí)別中稱為 “fill_parent”)則會(huì)展開(kāi)組件以匹配其父視圖的尺寸。

如果使用 “wrap_content” 和 “match_parent” 尺寸值而不是硬編碼的尺寸,視圖就會(huì)相應(yīng)地僅使用自身所需的空間或展開(kāi)以填滿可用空間彰亥。此方法可讓布局正確適應(yīng)各種屏幕尺寸和屏幕方向衰齐。

下面是一段示例代碼

下圖是在橫縱屏切換的時(shí)候的顯示效果,我們可以看到這樣可以很好的適配屏幕尺寸的變化耻涛。

weight是線性布局的一個(gè)獨(dú)特的屬性,我們可以使用這個(gè)屬性來(lái)按照比例對(duì)界面進(jìn)行分配澈蟆,完成一些特殊的需求卓研。

但是,我們對(duì)于這個(gè)屬性的計(jì)算應(yīng)該如何理解呢寥闪?

首先看下面的例子志珍,我們?cè)诓季种羞@樣設(shè)置我們的界面

我們?cè)诓季掷锩嬖O(shè)置為線性布局,橫向排列伦糯,然后放置兩個(gè)寬度為0dp的按鈕敛纲,分別設(shè)置weight為1和2,在效果圖中淤翔,我們可以看到兩個(gè)按鈕按照1:2的寬度比例正常排列了,這也是我們經(jīng)常使用到的場(chǎng)景监嗜,這是時(shí)候很好理解抡谐,Button1的寬度就是1/(1+2) = 1/3,Button2的寬度則是2/(1+2) = 2/3麦撵,我們可以很清楚的明白這種情景下的占比如何計(jì)算。

但是假如我們的寬度不是0dp(wrap_content和0dp的效果相同)音五,則是match_parent呢?

下面是設(shè)置為match_parent的效果

我們可以看到厨钻,在這種情況下诞挨,占比和上面正好相反,這是怎么回事呢惶傻?說(shuō)到這里,我們就不得不提一下weight的計(jì)算方法了涂佃。

android:layout_weight的真實(shí)含義是:如果View設(shè)置了該屬性并且有效蜈敢,那么該 View的寬度等于原有寬度(android:layout_width)加上剩余空間的占比。

從這個(gè)角度我們來(lái)解釋一下上面的現(xiàn)象伯病。在上面的代碼中否过,我們?cè)O(shè)置每個(gè)Button的寬度都是match_parent,假設(shè)屏幕寬度為L(zhǎng)苗桂,那么每個(gè)Button的寬度也應(yīng)該都為L(zhǎng)煤伟,剩余寬度就等于L-(L+L)= -L。

Button1的weight=1便锨,剩余寬度占比為1/(1+2)= 1/3,所以最終寬度為L(zhǎng)+1/3*(-L)=2/3L姚建,Button2的計(jì)算類似卿叽,最終寬度為L(zhǎng)+2/3(-L)=1/3L。

這是在水平方向上的贩虾,那么在垂直方向上也是這樣嗎沥阱?

下面是測(cè)試代碼和效果

如果是垂直方向,那么我們應(yīng)該改變的是layout_height的屬性考杉,下面是0dp的顯示效果

下面是match_parent的顯示效果,結(jié)論和水平是完全一樣的

雖然說(shuō)我們演示了match_parent的顯示效果咽袜,并說(shuō)明了原因枕稀,但是在真正用的時(shí)候,我們都是設(shè)置某一個(gè)屬性為0dp凹联,然后按照權(quán)重計(jì)算所占百分比哆档。

使用相對(duì)布局,禁用絕對(duì)布局

在開(kāi)發(fā)中瓜浸,我們大部分時(shí)候使用的都是線性布局、相對(duì)布局和幀布局偶惠,絕對(duì)布局由于適配性極差朗涩,所以極少使用。

由于各種布局的特點(diǎn)不一樣兄一,所以不能說(shuō)哪個(gè)布局好用识腿,到底應(yīng)該使用什么布局只能根據(jù)實(shí)際需求來(lái)確定。我們可以使用 LinearLayout 的嵌套實(shí)例并結(jié)合 “wrap_content” 和 “match_parent”渡讼,以便構(gòu)建相當(dāng)復(fù)雜的布局。不過(guò)展箱,我們無(wú)法通過(guò) LinearLayout 精確控制子視圖的特殊關(guān)系;系統(tǒng)會(huì)將 LinearLayout 中的視圖直接并排列出攀隔。

如果我們需要將子視圖排列出各種效果而不是一條直線栖榨,通常更合適的解決方法是使用 RelativeLayout,這樣就可以根據(jù)各組件之間的特殊關(guān)系指定布局了婴栽。例如居夹,我們可以將某個(gè)子視圖對(duì)齊到屏幕左側(cè),同時(shí)將另一個(gè)視圖對(duì)齊到屏幕右側(cè)准脂。

下面的代碼以官方Demo為例說(shuō)明。

在上面的代碼中我們使用了相對(duì)布局沟饥,并且使用alignXXX等屬性指定了子控件的位置湾戳,下面是這種布局方式在應(yīng)對(duì)屏幕變化時(shí)的表現(xiàn)

在小尺寸屏幕的顯示

在平板的大尺寸上的顯示效果

雖然控件的大小由于屏幕尺寸的增加而發(fā)生了改變,但是我們可以看到幼驶,由于使用了相對(duì)布局韧衣,所以控件之前的位置關(guān)系并沒(méi)有發(fā)生什么變化,這說(shuō)明我們的適配成功了畅铭。

使用限定符

使用尺寸限定符

上面所提到的靈活布局或者是相對(duì)布局硕噩,可以為我們帶來(lái)的優(yōu)勢(shì)就只有這么多了。雖然這些布局可以拉伸組件內(nèi)外的空間以適應(yīng)各種屏幕炉擅,但它們不一定能為每種屏幕都提供最佳的用戶體驗(yàn)阳惹。因此穆端,我們的應(yīng)用不僅僅只實(shí)施靈活布局仿便,還應(yīng)該應(yīng)針對(duì)各種屏幕配置提供一些備用布局攒巍。

如何做到這一點(diǎn)呢?我們可以通過(guò)使用配置限定符柒莉,在運(yùn)行時(shí)根據(jù)當(dāng)前的設(shè)備配置自動(dòng)選擇合適的資源了,例如根據(jù)各種屏幕尺寸選擇不同的布局窿凤。

很多應(yīng)用會(huì)在較大的屏幕上實(shí)施“雙面板”模式跨蟹,即在一個(gè)面板上顯示項(xiàng)目列表,而在另一面板上顯示對(duì)應(yīng)內(nèi)容夯秃。平板電腦和電視的屏幕已經(jīng)大到可以同時(shí)容納這兩個(gè)面板了痢艺,但手機(jī)屏幕就需要分別顯示。因此堤舒,我們可以使用以下文件以便實(shí)施這些布局:

res/layout/main.xml舌缤,單面板(默認(rèn))布局:

res/layout-large/main.xml,雙面板布局:

請(qǐng)注意第二種布局名稱目錄中的 large 限定符友驮。系統(tǒng)會(huì)在屬于較大屏幕(例如 7 英寸或更大的平板電腦)的設(shè)備上選擇此布局。系統(tǒng)會(huì)在較小的屏幕上選擇其他布局(無(wú)限定符)走越。

使用最小寬度限定符

在版本低于 3.2 的 Android 設(shè)備上耻瑟,開(kāi)發(fā)人員遇到的問(wèn)題之一是“較大”屏幕的尺寸范圍赏酥,該問(wèn)題會(huì)影響戴爾 Streak谆构、早期的 Galaxy Tab 以及大部分 7 英寸平板電腦。即使這些設(shè)備的屏幕屬于“較大”的尺寸呵晨,但很多應(yīng)用可能會(huì)針對(duì)此類別中的各種設(shè)備(例如 5 英寸和 7 英寸的設(shè)備)顯示不同的布局熬尺。這就是 Android 3.2 版在引入其他限定符的同時(shí)引入“最小寬度”限定符的原因。

最小寬度限定符可讓您通過(guò)指定某個(gè)最小寬度(以 dp 為單位)來(lái)定位屏幕季二。例如揭措,標(biāo)準(zhǔn) 7 英寸平板電腦的最小寬度為 600 dp,因此如果您要在此類屏幕上的用戶界面中使用雙面板(但在較小的屏幕上只顯示列表)绊含,您可以使用上文中所述的單面板和雙面板這兩種布局躬充,但您應(yīng)使用 sw600dp 指明雙面板布局僅適用于最小寬度為 600 dp 的屏幕,而不是使用 large 尺寸限定符麻裳。

res/layout/main.xml,單面板(默認(rèn))布局:

res/layout-sw600dp/main.xml妙蔗,雙面板布局:

也就是說(shuō)疆瑰,對(duì)于最小寬度大于等于 600 dp 的設(shè)備,系統(tǒng)會(huì)選擇 layout-sw600dp/main.xml(雙面板)布局寸五,否則系統(tǒng)就會(huì)選擇 layout/main.xml(單面板)布局耿币。

但 Android 版本低于 3.2 的設(shè)備不支持此技術(shù),原因是這些設(shè)備無(wú)法將 sw600dp 識(shí)別為尺寸限定符十性,因此我們?nèi)孕枋褂?large 限定符叛溢。這樣一來(lái),就會(huì)有一個(gè)名稱為 res/layout-large/main.xml 的文件(與 res/layout-sw600dp/main.xml 一樣)劲适。但是沒(méi)有太大關(guān)系楷掉,我們將馬上學(xué)習(xí)如何避免此類布局文件出現(xiàn)的重復(fù)。

使用布局別名

最小寬度限定符僅適用于 Android 3.2 及更高版本霞势。因此烹植,如果我們?nèi)孕枋褂门c較低版本兼容的概括尺寸范圍(小愕贡、正常刊橘、大和特大)。例如颂鸿,如果要將用戶界面設(shè)計(jì)成在手機(jī)上顯示單面板,但在 7 英寸平板電腦攒庵、電視和其他較大的設(shè)備上顯示多面板嘴纺,那么我們就需要提供以下文件:

res/layout/main.xml: 單面板布局

res/layout-large: 多面板布局

res/layout-sw600dp: 多面板布局

后兩個(gè)文件是相同的,因?yàn)槠渲幸粋€(gè)用于和 Android 3.2 設(shè)備匹配浓冒,而另一個(gè)則是為使用較低版本 Android 的平板電腦和電視準(zhǔn)備的栽渴。

要避免平板電腦和電視的文件出現(xiàn)重復(fù)(以及由此帶來(lái)的維護(hù)問(wèn)題),您可以使用別名文件稳懒。例如闲擦,您可以定義以下布局:

res/layout/main.xml,單面板布局

res/layout/main_twopanes.xml场梆,雙面板布局

然后添加這兩個(gè)文件:

res/values-large/layout.xml:

res/values-sw600dp/layout.xml:

后兩個(gè)文件的內(nèi)容相同墅冷,但它們并未實(shí)際定義布局。它們只是將 main 設(shè)置成了 main_twopanes 的別名或油。由于這些文件包含 large 和 sw600dp 選擇器寞忿,因此無(wú)論 Android 版本如何,系統(tǒng)都會(huì)將這些文件應(yīng)用到平板電腦和電視上(版本低于 3.2 的平板電腦和電視會(huì)匹配 large顶岸,版本高于 3.2 的平板電腦和電視則會(huì)匹配 sw600dp)。

使用屏幕方向限定符

某些布局會(huì)同時(shí)支持橫向模式和縱向模式,但我們可以通過(guò)調(diào)整優(yōu)化其中大部分布局的效果虱歪。在新聞閱讀器示例應(yīng)用中谴咸,每種屏幕尺寸和屏幕方向下的布局行為方式如下所示:

小屏幕,縱向:?jiǎn)蚊姘寰硖福瑤Щ諛?biāo)

小屏幕杯拐,橫向:?jiǎn)蚊姘澹瑤Щ諛?biāo)

7 英寸平板電腦,縱向:?jiǎn)蚊姘迮菏瑤Р僮鳈?br>

7 英寸平板電腦寇损,橫向:雙面板,寬裳食,帶操作欄

10 英寸平板電腦矛市,縱向:雙面板,窄诲祸,帶操作欄

10 英寸平板電腦浊吏,橫向:雙面板,寬救氯,帶操作欄

電視找田,橫向:雙面板,寬着憨,帶操作欄

因此墩衙,這些布局中的每一種都定義在了 res/layout/ 目錄下的某個(gè) XML 文件中。為了繼續(xù)將每個(gè)布局分配給各種屏幕配置甲抖,該應(yīng)用會(huì)使用布局別名將兩者相匹配:

res/layout/onepane.xml:(單面板)

res/layout/onepane_with_bar.xml:(單面板帶操作欄)

res/layout/twopanes.xml:(雙面板漆改,寬布局)

res/layout/twopanes_narrow.xml:(雙面板,窄布局)

既然我們已定義了所有可能的布局准谚,那就只需使用配置限定符將正確的布局映射到各種配置即可挫剑。

現(xiàn)在只需使用布局別名技術(shù)即可做到這一點(diǎn):

res/values/layouts.xml:

res/values-sw600dp-land/layouts.xml:

res/values-sw600dp-port/layouts.xml:

res/values-large-land/layouts.xml:

res/values-large-port/layouts.xml:

使用自動(dòng)拉伸位圖

支持各種屏幕尺寸通常意味著您的圖片資源還必須能適應(yīng)各種尺寸。例如柱衔,無(wú)論要應(yīng)用到什么形狀的按鈕上樊破,按鈕背景都必須能適應(yīng)。

如果在可以更改尺寸的組件上使用了簡(jiǎn)單的圖片唆铐,您很快就會(huì)發(fā)現(xiàn)顯示效果多少有些不太理想哲戚,因?yàn)橄到y(tǒng)會(huì)在運(yùn)行時(shí)平均地拉伸或收縮您的圖片。解決方法為使用自動(dòng)拉伸位圖艾岂,這是一種格式特殊的 PNG 文件惫恼,其中會(huì)指明可以拉伸以及不可以拉伸的區(qū)域。

.9的制作澳盐,實(shí)際上就是在原圖片上添加1px的邊界祈纯,然后按照我們的需求,把對(duì)應(yīng)的位置設(shè)置成黑色線叼耙,系統(tǒng)就會(huì)根據(jù)我們的實(shí)際需求進(jìn)行拉伸腕窥。

下圖是對(duì).9圖的四邊的含義的解釋,左上邊代表拉伸區(qū)域筛婉,右下邊代表padding box簇爆,就是間隔區(qū)域癞松,在下面,我們給出一個(gè)例子入蛆,方便大家理解响蓉。

先看下面兩張圖,我們理解一下這四條線的含義哨毁。

上圖和下圖的區(qū)別枫甲,就在于右下邊的黑線不一樣,具體的效果的區(qū)別扼褪,看右邊的效果圖想幻。上圖效果圖中深藍(lán)色的區(qū)域,代表內(nèi)容區(qū)域话浇,我們可以看到是在正中央的脏毯,這是因?yàn)槲覀冊(cè)谟蚁逻叺氖莾蓚€(gè)點(diǎn),這兩個(gè)點(diǎn)距離上下左右四個(gè)方向的距離就是padding的距離幔崖,所以深藍(lán)色內(nèi)容區(qū)域在圖片正中央食店,我們?cè)倏聪聢D,由于右下邊的黑線是圖片長(zhǎng)度赏寇,所以就沒(méi)有padding吉嫩,從效果圖上的表現(xiàn)就是深藍(lán)色區(qū)域和圖片一樣大,因此蹋订,我們可以利用右下邊來(lái)控制內(nèi)容與背景圖邊緣的padding。

如果你還不明白刻伊,那么我們看下面的效果圖露戒,我們分別以圖一和圖二作為背景圖,下面是效果圖捶箱。

我們可以看到智什,使用wrap_content屬性設(shè)置長(zhǎng)寬,圖一比圖二的效果大一圈丁屎,這是為什么呢荠锭?還記得我上面說(shuō)的padding嗎?

這就是padding的效果提現(xiàn)晨川,怎么證明呢证九?我們?cè)倏聪旅嬉粡垐D,給圖一添加padding=0共虑,這樣背景圖設(shè)置的padding效果就沒(méi)了愧怜,是不是兩個(gè)一樣大了?

ok妈拌,我想你應(yīng)該明白右下邊的黑線的含義了拥坛,下面我們?cè)倏匆幌伦笊线叺男Ч?br>

下面我們只設(shè)置了左上邊線,效果圖如下

上面的線沒(méi)有包住圖標(biāo),下面的線正好包住了圖標(biāo)猜惋,從右邊的效果圖應(yīng)該可以看出差別丸氛,黑線所在的區(qū)域就是拉伸區(qū)域,上圖黑線所在的全是純色著摔,所以圖標(biāo)不變形缓窜,下面的拉伸區(qū)域包裹了圖標(biāo),所以在拉伸的時(shí)候就會(huì)對(duì)圖標(biāo)進(jìn)行拉伸梨撞,但是這樣就會(huì)導(dǎo)致圖標(biāo)變形雹洗。注意到下面紅線區(qū)域了嘛?這是系統(tǒng)提示我們的卧波,因?yàn)檫@樣拉伸时肿,不符合要求,所以會(huì)提示一下港粱。

支持各種屏幕密度

使用非密度制約像素

由于各種屏幕的像素密度都有所不同螃成,因此相同數(shù)量的像素在不同設(shè)備上的實(shí)際大小也有所差異,這樣使用像素定義布局尺寸就會(huì)產(chǎn)生問(wèn)題查坪。因此寸宏,請(qǐng)務(wù)必使用 dp 或 sp 單位指定尺寸。dp 是一種非密度制約像素偿曙,其尺寸與 160 dpi 像素的實(shí)際尺寸相同氮凝。sp 也是一種基本單位,但它可根據(jù)用戶的偏好文字大小進(jìn)行調(diào)整(即尺度獨(dú)立性像素)望忆,因此我們應(yīng)將該測(cè)量單位用于定義文字大小罩阵。

例如,請(qǐng)使用 dp(而非 px)指定兩個(gè)視圖間的間距:

請(qǐng)務(wù)必使用 sp 指定文字大衅羯恪:

除了介紹這些最基礎(chǔ)的知識(shí)之外稿壁,我們下面再來(lái)討論一下另外一個(gè)問(wèn)題。

經(jīng)過(guò)上面的介紹歉备,我們都清楚傅是,為了能夠規(guī)避不同像素密度的陷阱,Google推薦使用dp來(lái)代替px作為控件長(zhǎng)度的度量單位蕾羊,但是我們來(lái)看下面的一個(gè)場(chǎng)景喧笔。

假如我們以Nexus5作為書(shū)寫(xiě)代碼時(shí)查看效果的測(cè)試機(jī)型,Nexus5的總寬度為360dp龟再,我們現(xiàn)在需要在水平方向上放置兩個(gè)按鈕溃斋,一個(gè)是150dp左對(duì)齊,另外一個(gè)是200dp右對(duì)齊吸申,中間留有10dp間隔梗劫,那么在Nexus5上面的顯示效果就是下面這樣

<

但是如果在Nexus S或者是Nexus One運(yùn)行呢享甸?下面是運(yùn)行結(jié)果

可以看到,兩個(gè)按鈕發(fā)生了重疊梳侨。

我們都已經(jīng)用了dp了蛉威,為什么會(huì)出現(xiàn)這種情況呢?

你聽(tīng)我慢慢道來(lái)走哺。

雖然說(shuō)dp可以去除不同像素密度的問(wèn)題蚯嫌,使得1dp在不同像素密度上面的顯示效果相同,但是還是由于Android屏幕設(shè)備的多樣性丙躏,如果使用dp來(lái)作為度量單位择示,并不是所有的屏幕的寬度都是相同的dp長(zhǎng)度,比如說(shuō)晒旅,Nexus S和Nexus One屬于hdpi栅盲,屏幕寬度是320dp,而Nexus 5屬于xxhdpi废恋,屏幕寬度是360dp谈秫,Galaxy Nexus屬于xhdpi,屏幕寬度是384dp鱼鼓,Nexus 6 屬于xxxhdpi拟烫,屏幕寬度是410dp。所以說(shuō)迄本,光Google自己一家的產(chǎn)品就已經(jīng)有這么多的標(biāo)準(zhǔn)硕淑,而且屏幕寬度和像素密度沒(méi)有任何關(guān)聯(lián)關(guān)系,即使我們使用dp嘉赎,在320dp寬度的設(shè)備和410dp的設(shè)備上置媳,還是會(huì)有90dp的差別。當(dāng)然曹阔,我們盡量使用match_parent和wrap_content半开,盡可能少的用dp來(lái)指定控件的具體長(zhǎng)寬隔披,再結(jié)合上權(quán)重赃份,大部分的情況我們都是可以做到適配的。

但是除了這個(gè)方法奢米,我們還有沒(méi)有其他的更徹底的解決方案呢抓韩?

我們換另外一個(gè)思路來(lái)思考這個(gè)問(wèn)題。

下面的方案來(lái)自Android Day Day Up 一群的【blue-深圳】鬓长,謝謝他的分享精神谒拴。

因?yàn)榉直媛什灰粯樱圆荒苡胮x涉波;因?yàn)槠聊粚挾炔灰粯佑⑸希砸⌒牡挠胐p炭序,那么我們可不可以用另外一種方法來(lái)統(tǒng)一單位,不管分辨率是多大苍日,屏幕寬度用一個(gè)固定的值的單位來(lái)統(tǒng)計(jì)呢惭聂?

答案是:當(dāng)然可以。

我們假設(shè)手機(jī)屏幕的寬度都是320某單位相恃,那么我們將一個(gè)屏幕寬度的總像素?cái)?shù)平均分成320份辜纲,每一份對(duì)應(yīng)具體的像素就可以了。

具體如何來(lái)實(shí)現(xiàn)呢拦耐?我們看下面的代碼:

import java.io.File;

import java.io.FileNotFoundException;

import java.io.FileOutputStream;

import java.io.PrintWriter;

public class MakeXml {

? ? private final static String rootPath = "C:\\Users\\Administrator\\Desktop\\layoutroot\\values-{0}x{1}\\";

? ? private final static float dw = 320f;

? ? private final static float dh = 480f;

? ? private final static String WTemplate = "[dimen name=\"x{0}\"]{1}px[/dimen]\n";

? ? private final static String HTemplate = "[dimen name=\"y{0}\"]{1}px[/dimen]\n";

? ? public static void main(String[] args) {

? ? ? ? makeString(320, 480);

? ? ? ? makeString(480,800);

? ? ? ? makeString(480, 854);

? ? ? ? makeString(540, 960);

? ? ? ? makeString(600, 1024);

? ? ? ? makeString(720, 1184);

? ? ? ? makeString(720, 1196);

? ? ? ? makeString(720, 1280);

? ? ? ? makeString(768, 1024);

? ? ? ? makeString(800, 1280);

? ? ? ? makeString(1080, 1812);

? ? ? ? makeString(1080, 1920);

? ? ? ? makeString(1440, 2560);

? ? }

? ? public static void makeString(int w, int h) {

? ? ? ? StringBuffer sb = new StringBuffer();

? ? ? ? sb.append("[?xml version=\"1.0\" encoding=\"utf-8\"?]\n");

? ? ? ? sb.append("[resources]");

? ? ? ? float cellw = w / dw;

? ? ? ? for (int i = 1; i < 320; i++) {

? ? ? ? ? ? sb.append(WTemplate.replace("{0}", i + "").replace("{1}",

? ? ? ? ? ? ? ? ? ? change(cellw * i) + ""));

? ? ? ? }

? ? ? ? sb.append(WTemplate.replace("{0}", "320").replace("{1}", w + ""));

? ? ? ? sb.append("[/resources]");

? ? ? ? StringBuffer sb2 = new StringBuffer();

? ? ? ? sb2.append("[?xml version=\"1.0\" encoding=\"utf-8\"?]\n");

? ? ? ? sb2.append("[resources]");

? ? ? ? float cellh = h / dh;

? ? ? ? for (int i = 1; i < 480; i++) {

? ? ? ? ? ? sb2.append(HTemplate.replace("{0}", i + "").replace("{1}",

? ? ? ? ? ? ? ? ? ? change(cellh * i) + ""));

? ? ? ? }

? ? ? ? sb2.append(HTemplate.replace("{0}", "480").replace("{1}", h + ""));

? ? ? ? sb2.append("[/resources]");

? ? ? ? String path = rootPath.replace("{0}", h + "").replace("{1}", w + "");

? ? ? ? File rootFile = new File(path);

? ? ? ? if (!rootFile.exists()) {

? ? ? ? ? ? rootFile.mkdirs();

? ? ? ? }

? ? ? ? File layxFile = new File(path + "lay_x.xml");

? ? ? ? File layyFile = new File(path + "lay_y.xml");

? ? ? ? try {

? ? ? ? ? ? PrintWriter pw = new PrintWriter(new FileOutputStream(layxFile));

? ? ? ? ? ? pw.print(sb.toString());

? ? ? ? ? ? pw.close();

? ? ? ? ? ? pw = new PrintWriter(new FileOutputStream(layyFile));

? ? ? ? ? ? pw.print(sb2.toString());

? ? ? ? ? ? pw.close();

? ? ? ? } catch (FileNotFoundException e) {

? ? ? ? ? ? e.printStackTrace();

? ? ? ? }

? ? }

? ? public static float change(float a) {

? ? ? ? int temp = (int) (a * 100);

? ? ? ? return temp / 100f;

? ? }

}

代碼應(yīng)該很好懂耕腾,我們將一個(gè)屏幕寬度分為320份,高度480份杀糯,然后按照實(shí)際像素對(duì)每一個(gè)單位進(jìn)行復(fù)制扫俺,放在對(duì)應(yīng)values-widthxheight文件夾下面的lax.xml和lay.xml里面,這樣就可以統(tǒng)一所有你想要的分辨率的單位了火脉,下面是生成的一個(gè)320*480分辨率的文件牵舵,因?yàn)閷捀叻指钪罂偡謹(jǐn)?shù)和像素?cái)?shù)相同,所以x1就是1px倦挂,以此類推畸颅。

那么1080*1960分辨率下是什么樣子呢?我們可以看下方援,由于1080和320是3.37倍的關(guān)系没炒,所以x1=3.37px

無(wú)論在什么分辨率下,x320都是代表屏幕寬度犯戏,y480都是代表屏幕高度送火。

那么,我們應(yīng)該如何使用呢先匪?

首先种吸,我們要把生成的所有values文件夾放到res目錄下,當(dāng)設(shè)計(jì)師把UI高清設(shè)計(jì)圖給你之后呀非,你就可以根據(jù)設(shè)計(jì)圖上的尺寸坚俗,以某一個(gè)分辨率的機(jī)型為基礎(chǔ),找到對(duì)應(yīng)像素?cái)?shù)的單位岸裙,然后設(shè)置給控件即可猖败。

下圖還是兩個(gè)Button,不同的是降允,我們把單位換成了我們?cè)趘alues文件夾下dimen的值恩闻,這樣在你指定的分辨率下,不管寬度是320dp剧董、360dp幢尚,還是410dp破停,就都可以完全適配了。

但是尉剩,還是有個(gè)問(wèn)題辱挥,為什么下面的三個(gè)沒(méi)有適配呢?

這是因?yàn)橛捎谠谏傻膙alues文件夾里边涕,沒(méi)有對(duì)應(yīng)的分辨率晤碘,其實(shí)一開(kāi)始是報(bào)錯(cuò)的,因?yàn)槟J(rèn)的values沒(méi)有對(duì)應(yīng)dimen功蜓,所以我只能在默認(rèn)values里面也創(chuàng)建對(duì)應(yīng)文件园爷,但是里面的數(shù)據(jù)卻不好處理,因?yàn)椴恢婪直媛适胶常抑缓媚J(rèn)為x1=1dp保證盡量兼容童社。這也是這個(gè)解決方案的幾個(gè)弊端,對(duì)于沒(méi)有生成對(duì)應(yīng)分辨率文件的手機(jī)著隆,會(huì)使用默認(rèn)values文件夾扰楼,如果默認(rèn)文件夾沒(méi)有,就會(huì)出現(xiàn)問(wèn)題美浦。

所以說(shuō)弦赖,這個(gè)方案雖然是一勞永逸,但是由于實(shí)際上還是使用的px作為長(zhǎng)度的度量單位浦辨,所以多少和google的要求有所背離蹬竖,不好說(shuō)以后會(huì)不會(huì)出現(xiàn)什么不可預(yù)測(cè)的問(wèn)題。其次流酬,如果要使用這個(gè)方案币厕,你必須盡可能多的包含所有的分辨率,因?yàn)檫@個(gè)是使用這個(gè)方案的基礎(chǔ)芽腾,如果有分辨率缺少旦装,會(huì)造成顯示效果很差,甚至出錯(cuò)的風(fēng)險(xiǎn)摊滔,而這又勢(shì)必會(huì)增加軟件包的大小和維護(hù)的難度阴绢,所以大家自己斟酌,擇優(yōu)使用惭载。

更多信息可參考鴻洋的新文章:Android 屏幕適配方案旱函。

提供備用位圖

由于 Android 可在具有各種屏幕密度的設(shè)備上運(yùn)行响巢,因此我們提供的位圖資源應(yīng)始終可以滿足各類普遍密度范圍的要求:低密度描滔、中等密度、高密度以及超高密度踪古。這將有助于我們的圖片在所有屏幕密度上都能得到出色的質(zhì)量和效果含长。

要生成這些圖片券腔,我們應(yīng)先提取矢量格式的原始資源,然后根據(jù)以下尺寸范圍針對(duì)各密度生成相應(yīng)的圖片拘泞。

xhdpi:2.0

hdpi:1.5

mdpi:1.0(最低要求)

ldpi:0.75

也就是說(shuō)纷纫,如果我們?yōu)?xhdpi 設(shè)備生成了 200x200 px尺寸的圖片,就應(yīng)該使用同一資源為 hdpi陪腌、mdpi 和 ldpi 設(shè)備分別生成 150x150辱魁、100x100 和 75x75 尺寸的圖片。

然后诗鸭,將生成的圖片文件放在 res/ 下的相應(yīng)子目錄中(mdpi染簇、hdpi、xhdpi强岸、xxhdpi)锻弓,系統(tǒng)就會(huì)根據(jù)運(yùn)行您應(yīng)用的設(shè)備的屏幕密度自動(dòng)選擇合適的圖片。

這樣一來(lái)蝌箍,只要我們引用 @drawable/id青灼,系統(tǒng)都能根據(jù)相應(yīng)屏幕的 dpi 選取合適的位圖。

還記得我們上面提到的圖標(biāo)設(shè)計(jì)尺寸嗎妓盲?和這個(gè)其實(shí)是一個(gè)意思杂拨。

但是還有個(gè)問(wèn)題需要注意下,如果是.9圖或者是不需要多個(gè)分辨率的圖片悯衬,就放在drawable文件夾即可扳躬,對(duì)應(yīng)分辨率的圖片要正確的放在合適的文件夾,否則會(huì)造成圖片拉伸等問(wèn)題甚亭。

實(shí)施自適應(yīng)用戶界面流程

前面我們介紹過(guò)贷币,如何根據(jù)設(shè)備特點(diǎn)顯示恰當(dāng)?shù)牟季郑沁@樣做亏狰,會(huì)使得用戶界面流程可能會(huì)有所不同役纹。例如,如果應(yīng)用處于雙面板模式下暇唾,點(diǎn)擊左側(cè)面板上的項(xiàng)即可直接在右側(cè)面板上顯示相關(guān)內(nèi)容促脉;而如果該應(yīng)用處于單面板模式下,點(diǎn)擊相關(guān)的內(nèi)容應(yīng)該跳轉(zhuǎn)到另外一個(gè)Activity進(jìn)行后續(xù)的處理策州。所以我們應(yīng)該按照下面的流程瘸味,一步步的完成自適應(yīng)界面的實(shí)現(xiàn)。

確定當(dāng)前布局

由于每種布局的實(shí)施都會(huì)稍有不同够挂,因此我們需要先確定當(dāng)前向用戶顯示的布局旁仿。例如,我們可以先了解用戶所處的是“單面板”模式還是“雙面板”模式孽糖。要做到這一點(diǎn)枯冈,可以通過(guò)查詢指定視圖是否存在以及是否已顯示出來(lái)毅贮。

ublic class NewsReaderActivity extends FragmentActivity {

? ? boolean mIsDualPane;

? ? @Override

? ? public void onCreate(Bundle savedInstanceState) {

? ? ? ? super.onCreate(savedInstanceState);

? ? ? ? setContentView(R.layout.main_layout);

? ? ? ? View articleView = findViewById(R.id.article);

? ? ? ? mIsDualPane = articleView != null &&

? ? ? ? ? ? ? ? ? ? ? ? articleView.getVisibility() == View.VISIBLE;

? ? }

}

請(qǐng)注意,這段代碼用于查詢“報(bào)道”面板是否可用尘奏,與針對(duì)具體布局的硬編碼查詢相比滩褥,這段代碼的靈活性要大得多。

再舉一個(gè)適應(yīng)各種組件的存在情況的方法示例:在對(duì)這些組件執(zhí)行操作前先查看它們是否可用炫加。例如瑰煎,新聞閱讀器示例應(yīng)用中有一個(gè)用于打開(kāi)菜單的按鈕,但只有在版本低于 3.0 的 Android 上運(yùn)行該應(yīng)用時(shí)俗孝,這個(gè)按鈕才會(huì)存在丢间,因?yàn)?API 級(jí)別 11 或更高級(jí)別中的 ActionBar 已取代了該按鈕的功能。因此驹针,您可以使用以下代碼為此按鈕添加事件偵聽(tīng)器:

Button catButton = (Button) findViewById(R.id.categorybutton);

OnClickListener listener = /* create your listener here */;

if (catButton != null) {

? ? catButton.setOnClickListener(listener);

}

根據(jù)當(dāng)前布局做出響應(yīng)

有些操作可能會(huì)因當(dāng)前的具體布局而產(chǎn)生不同的結(jié)果烘挫。例如,在新聞閱讀器示例中柬甥,如果用戶界面處于雙面板模式下饮六,那么點(diǎn)擊標(biāo)題列表中的標(biāo)題就會(huì)在右側(cè)面板中打開(kāi)相應(yīng)報(bào)道;但如果用戶界面處于單面板模式下苛蒲,那么上述操作就會(huì)啟動(dòng)一個(gè)獨(dú)立活動(dòng):

@Override

public void onHeadlineSelected(int index) {

? ? mArtIndex = index;

? ? if (mIsDualPane) {

? ? ? ? /* display article on the right pane */

? ? ? ? mArticleFragment.displayArticle(mCurrentCat.getArticle(index));

? ? } else {

? ? ? ? /* start a separate activity */

? ? ? ? Intent intent = new Intent(this, ArticleActivity.class);

? ? ? ? intent.putExtra("catIndex", mCatIndex);

? ? ? ? intent.putExtra("artIndex", index);

? ? ? ? startActivity(intent);

? ? }

}

同樣卤橄,如果該應(yīng)用處于雙面板模式下,就應(yīng)設(shè)置帶導(dǎo)航標(biāo)簽的操作欄臂外;但如果該應(yīng)用處于單面板模式下窟扑,就應(yīng)使用下拉菜單設(shè)置導(dǎo)航欄。因此我們的代碼還應(yīng)確定哪種情況比較合適:

final String CATEGORIES[] = { "熱門(mén)報(bào)道", "政治", "經(jīng)濟(jì)", "Technology" };

public void onCreate(Bundle savedInstanceState) {

? ? ....

? ? if (mIsDualPane) {

? ? ? ? /* use tabs for navigation */

? ? ? ? actionBar.setNavigationMode(android.app.ActionBar.NAVIGATION_MODE_TABS);

? ? ? ? int i;

? ? ? ? for (i = 0; i < CATEGORIES.length; i++) {

? ? ? ? ? ? actionBar.addTab(actionBar.newTab().setText(

? ? ? ? ? ? ? ? CATEGORIES[i]).setTabListener(handler));

? ? ? ? }

? ? ? ? actionBar.setSelectedNavigationItem(selTab);

? ? }

? ? else {

? ? ? ? /* use list navigation (spinner) */

? ? ? ? actionBar.setNavigationMode(android.app.ActionBar.NAVIGATION_MODE_LIST);

? ? ? ? SpinnerAdapter adap = new ArrayAdapter(this,

? ? ? ? ? ? ? ? R.layout.headline_item, CATEGORIES);

? ? ? ? actionBar.setListNavigationCallbacks(adap, handler);

? ? }

}

重復(fù)使用其他活動(dòng)中的片段

多屏幕設(shè)計(jì)中的重復(fù)模式是指漏健,對(duì)于某些屏幕配置嚎货,已實(shí)施界面的一部分會(huì)用作面板;但對(duì)于其他配置蔫浆,這部分就會(huì)以獨(dú)立活動(dòng)的形式存在殖属。例如,在新聞閱讀器示例中瓦盛,對(duì)于較大的屏幕洗显,新聞報(bào)道文本會(huì)顯示在右側(cè)面板中;但對(duì)于較小的屏幕原环,這些文本就會(huì)以獨(dú)立活動(dòng)的形式存在挠唆。

在類似情況下,通持雎穑可以在多個(gè)活動(dòng)中重復(fù)使用相同的 Fragment 子類以避免代碼重復(fù)玄组。例如,在雙面板布局中使用了 ArticleFragment:

然后又在小屏幕的Activity布局中重復(fù)使用了它 :

ArticleFragment frag = new ArticleFragment();

getSupportFragmentManager().beginTransaction().add(android.R.id.content, frag).commit();

當(dāng)然,這與在 XML 布局中聲明片段的效果是一樣的巧勤,但在這種情況下卻沒(méi)必要使用 XML 布局,因?yàn)閳?bào)道片段是此活動(dòng)中的唯一組件弄匕。

請(qǐng)務(wù)必在設(shè)計(jì)片段時(shí)注意颅悉,不要針對(duì)具體活動(dòng)創(chuàng)建強(qiáng)耦合。要做到這一點(diǎn)迁匠,通呈F浚可以定義一個(gè)接口,該接口概括了相關(guān)片段與其主活動(dòng)交互所需的全部方式城丧,然后讓主活動(dòng)實(shí)施該界面:

例如延曙,新聞閱讀器應(yīng)用的 HeadlinesFragment 會(huì)精確執(zhí)行以下代碼:

public class HeadlinesFragment extends ListFragment {

? ? ...

? ? OnHeadlineSelectedListener mHeadlineSelectedListener = null;

? ? /* Must be implemented by host activity */

? ? public interface OnHeadlineSelectedListener {

? ? ? ? public void onHeadlineSelected(int index);

? ? }

? ? ...

? ? public void setOnHeadlineSelectedListener(OnHeadlineSelectedListener listener) {

? ? ? ? mHeadlineSelectedListener = listener;

? ? }

}

然后,如果用戶選擇某個(gè)標(biāo)題亡哄,相關(guān)片段就會(huì)通知由主活動(dòng)指定的偵聽(tīng)器(而不是通知某個(gè)硬編碼的具體活動(dòng)):

public class HeadlinesFragment extends ListFragment {

? ? ...

? ? @Override

? ? public void onItemClick(AdapterView parent,

? ? ? ? ? ? ? ? ? ? ? ? ? ? View view, int position, long id) {

? ? ? ? if (null != mHeadlineSelectedListener) {

? ? ? ? ? ? mHeadlineSelectedListener.onHeadlineSelected(position);

? ? ? ? }

? ? }

? ? ...

}

除此之外枝缔,我們還可以使用第三方框架,比如說(shuō)使用“訂閱-發(fā)布”模式的EventBus來(lái)更多的優(yōu)化組件之間的通信蚊惯,減少耦合愿卸。

處理屏幕配置變化

如果我們使用獨(dú)立Activity實(shí)施界面的獨(dú)立部分,那么請(qǐng)注意截型,我們可能需要對(duì)特定配置變化(例如屏幕方向的變化)做出響應(yīng)趴荸,以便保持界面的一致性。

例如宦焦,在運(yùn)行 Android 3.0 或更高版本的標(biāo)準(zhǔn) 7 英寸平板電腦上发钝,如果新聞閱讀器示例應(yīng)用運(yùn)行在縱向模式下,就會(huì)在使用獨(dú)立活動(dòng)顯示新聞報(bào)道波闹;但如果該應(yīng)用運(yùn)行在橫向模式下酝豪,就會(huì)使用雙面板布局。

也就是說(shuō)精堕,如果用戶處于縱向模式下且屏幕上顯示的是用于閱讀報(bào)道的活動(dòng)寓调,那么就需要在檢測(cè)到屏幕方向變化(變成橫向模式)后執(zhí)行相應(yīng)操作,即停止上述活動(dòng)并返回主活動(dòng)锄码,以便在雙面板布局中顯示相關(guān)內(nèi)容:

public class ArticleActivity extends FragmentActivity {

? ? int mCatIndex, mArtIndex;

? ? @Override

? ? protected void onCreate(Bundle savedInstanceState) {

? ? ? ? super.onCreate(savedInstanceState);

? ? ? ? mCatIndex = getIntent().getExtras().getInt("catIndex", 0);

? ? ? ? mArtIndex = getIntent().getExtras().getInt("artIndex", 0);

? ? ? ? // If should be in two-pane mode, finish to return to main activity

? ? ? ? if (getResources().getBoolean(R.bool.has_two_panes)) {

? ? ? ? ? ? finish();

? ? ? ? ? ? return;

? ? ? ? }

? ? ? ? ...

}

通過(guò)上面幾個(gè)步驟夺英,我們就完全可以建立一個(gè)可以根據(jù)用戶界面配置進(jìn)行自適應(yīng)的App了。

最佳實(shí)踐

關(guān)于高清設(shè)計(jì)圖尺寸

Google官方給出的高清設(shè)計(jì)圖尺寸有兩種方案滋捶,一種是以mdpi設(shè)計(jì)痛悯,然后對(duì)應(yīng)放大得到更高分辨率的圖片,另外一種則是以高分辨率作為設(shè)計(jì)大小重窟,然后按照倍數(shù)對(duì)應(yīng)縮小到小分辨率的圖片载萌。

根據(jù)經(jīng)驗(yàn),我更推薦第二種方法,因?yàn)樾》直媛试谏筛叻直媛蕡D片的時(shí)候扭仁,會(huì)出現(xiàn)像素丟失垮衷,我不知道是不是有方法可以阻止這種情況發(fā)生。

而分辨率可以以1280*720或者是1960*1080作為主要分辨率進(jìn)行設(shè)計(jì)乖坠。

ImageView的ScaleType屬性

設(shè)置不同的ScaleType會(huì)得到不同的顯示效果搀突,一般情況下,設(shè)置為centerCrop能獲得較好的適配效果熊泵。

動(dòng)態(tài)設(shè)置

有一些情況下仰迁,我們需要?jiǎng)討B(tài)的設(shè)置控件大小或者是位置,比如說(shuō)popwindow的顯示位置和偏移量等顽分,這個(gè)時(shí)候我們可以動(dòng)態(tài)的獲取當(dāng)前的屏幕屬性徐许,然后設(shè)置合適的數(shù)值

public class ScreenSizeUtil {

? ? public static int getScreenWidth(Activity activity) {

? ? ? ? return activity.getWindowManager().getDefaultDisplay().getWidth();

? ? }

? ? public static int getScreenHeight(Activity activity) {

? ? ? ? return activity.getWindowManager().getDefaultDisplay().getHeight();

? ? }

}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市卒蘸,隨后出現(xiàn)的幾起案子雌隅,更是在濱河造成了極大的恐慌,老刑警劉巖缸沃,帶你破解...
    沈念sama閱讀 216,402評(píng)論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件澄步,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡和泌,警方通過(guò)查閱死者的電腦和手機(jī)村缸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)武氓,“玉大人梯皿,你說(shuō)我怎么就攤上這事∠厮。” “怎么了东羹?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,483評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)忠烛。 經(jīng)常有香客問(wèn)我属提,道長(zhǎng),這世上最難降的妖魔是什么美尸? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,165評(píng)論 1 292
  • 正文 為了忘掉前任冤议,我火速辦了婚禮,結(jié)果婚禮上师坎,老公的妹妹穿的比我還像新娘恕酸。我一直安慰自己,他們只是感情好胯陋,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布蕊温。 她就那樣靜靜地躺著袱箱,像睡著了一般。 火紅的嫁衣襯著肌膚如雪义矛。 梳的紋絲不亂的頭發(fā)上发笔,一...
    開(kāi)封第一講書(shū)人閱讀 51,146評(píng)論 1 297
  • 那天,我揣著相機(jī)與錄音凉翻,去河邊找鬼了讨。 笑死,一個(gè)胖子當(dāng)著我的面吹牛噪矛,可吹牛的內(nèi)容都是我干的量蕊。 我是一名探鬼主播铺罢,決...
    沈念sama閱讀 40,032評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼艇挨,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了韭赘?” 一聲冷哼從身側(cè)響起缩滨,我...
    開(kāi)封第一講書(shū)人閱讀 38,896評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎泉瞻,沒(méi)想到半個(gè)月后脉漏,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,311評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡袖牙,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評(píng)論 2 332
  • 正文 我和宋清朗相戀三年侧巨,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片鞭达。...
    茶點(diǎn)故事閱讀 39,696評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡司忱,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出畴蹭,到底是詐尸還是另有隱情坦仍,我是刑警寧澤,帶...
    沈念sama閱讀 35,413評(píng)論 5 343
  • 正文 年R本政府宣布叨襟,位于F島的核電站繁扎,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏糊闽。R本人自食惡果不足惜梳玫,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評(píng)論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望右犹。 院中可真熱鬧汽纠,春花似錦、人聲如沸傀履。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至碴犬,卻和暖如春絮宁,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背服协。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,815評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工绍昂, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人偿荷。 一個(gè)月前我還...
    沈念sama閱讀 47,698評(píng)論 2 368
  • 正文 我出身青樓窘游,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親跳纳。 傳聞我的和親對(duì)象是個(gè)殘疾皇子忍饰,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容