值得仔細看下的 android 適配文章

原文地址:


http://blog.csdn.net/hitlion2008/article/details/9200135


 

Android實戰技術: 用Dimension解決多屏幕適配的問題

分類: Android 7245人閱讀 評論(2) 收藏 舉報

目錄(?)[+]

開閉原則--可變與不變的分離,且容易定製

應用程序的目的是儘可能做到適用於多種設備,這些設備的配置不盡相同,有些不同的物理尺寸,分辨率.爲了達到最佳的適配效果,和最少的代碼重複,以及最好的可擴展性,就需要分離資源的使用和資源.用一個統一的資源管理者來管理資源.代碼通過資源管理者提供的統一的接口來獲取資源.這樣對於使用者來講資源的獲取的方式是統一,資源者無需關心如何爲不同的設備獲取不同的資源.這樣就把隨不同設備變化而變化的代碼降到最低,只有資源管理者需要操心不同的設備相關的不同的資源.
比如:字串的獲取和使用.最直接的方式就是直接在代碼中使用字面常量.但處理多語言時,這就非常的麻煩,需要代碼去判斷當前系統語言,然後決定使用哪個!但如果使用資源管理器來獲取字串,就把代碼耦合降到最低:只有資源管理器需要判斷當前系統語言,然後去去相應的字串.其他人則無需要關心.
Android當中也是這樣子的,代碼通過統一的接口獲取系統資源,而由資源管理器來處理設備相關性的問題.

Android的資源裏面常見的hdpi等這都是什麼意思呢?要理解這些,先要了解一下標準的Graphic Design中的一些度量和尺寸的


基本的術語和概念:

屏幕尺寸 Screen size

是指屏幕的物理尺寸,度量的方式是以屏幕對角線的長度,單位是英寸(1英寸=2.54釐米).
屏幕尺寸的大與小,通常能決定屏幕究竟能顯示多少內容,比如電腦上能顯示的區域一般都要比手機要多.

分辨率Resolution

通常以像素爲單位,像素的定義爲設備能顯示的基本單元,比如480x800,或者1024x768,這意思就是說這個屏幕在長度上能顯示1024這麼多個點,在高度上能顯示768個點.分辨率通常用於表示圖像的尺寸.但是分辨率不能代表圖像的真實物理尺寸.也不能決定在屏幕上顯示的物理尺寸.

屏幕密度Density

是指這個屏幕在單位長度上能顯示的像素點數,也就是一英寸上能顯示的像素點數,計算方式就是分辨率除以物理尺寸.屏幕密度能反應設備的清晰程度.單位通常是PPI(Pixels Per Inch). PPI值越高,證明屏幕的清晰程度越好,能顯示的越清楚.比如蘋果的Retina系列屏幕,達到了326PPI.人眼的識別能力在300PPI左右,所以達到326時,人眼就無法分辨出像素點了,所以它顯示的就更清晰.
有了屏幕密度的概念就可以看到,同樣尺寸(分辨率)的圖片在高密度的屏幕上顯示的物理尺寸就會小(看起來小了),在低密度的屏幕上顯示的物理尺寸就會變大(看起來大了).
所以爲什麼,沒有爲MBP視網膜屏做適配的軟件,比如PS的圖標會看起來模糊.因爲MBP視網膜屏幕的密度大大提高,是以前的4倍,所以同樣尺寸的圖片看起來就會比原來小,是原來的1/4,太小了會看不見的,但是這樣使用起來是很不方便的.所以Mac系統爲了適應新的視網膜屏幕,就必須把圖片進行縮放,放大原來的四倍,這樣在用戶眼中"看起來"(物理尺寸)圖片還是那麼大!因爲圖標被拉大了四倍,當然會模糊了!所以,Mac上面的應用,在視網膜之後必須對應用進行專門的優化和適配.

屏幕的長寬比

物理長度與物理寬度的比例.

如何適配不同的屏幕

適配不同的屏幕的目的也就是讓應用在所有的系統上體驗一致. 這個不同的平臺有不同的策略.有些平臺僅是以屏幕分辨率爲依據.也就是把設備以屏幕分辨率爲分類依據.這也是平常所見的VGA一類的名字的來源:
根據不同的屏幕分辨率來區分不同的設備,比如一些常見的名字:
QVGA:       240*320     Quarter VGA 四分之一的意思(1/2 * 1/2 = 1/4)
HVGA:       320*480     Half size VGA 的意思 二分之一(1/2 * 1 = 1/2)
VGA:         480*640      遠古時代的電腦顯示器的分辨率,其他的都是以此爲基礎來縮放
WVGA:      480*800      Wide VGA
SVGA:       600*800      Super VGA
等等,關於分辨率的區分可以參考維基百科:Graphics display resolution
那麼,有一些的GUI系統就是以分辨率方式來區分不同的資源.
但是,如前面所述,分辨率不能代表屏幕的清晰程度.同樣分辨率的圖片,或者同樣的長度在高密度的屏幕上看起來就會小,在低密度的屏幕上就會變大!因爲它的單位是像素.所以,如果以分辨率作爲依據來區分資源,那麼就必須爲所有不同的分辨率設定資源,並且做適配的優化,否則就不會得到好的顯示效果!而想一想這工作量會多大!

屏幕密度無關的單位長度

適配不同的屏幕的目的是什麼 呢?就是爲了能讓
1.應用在不同的平臺都能正常的顯示,也就是把該顯示的顯示出來,不能太大(低密度上)也不能太小(高密度上)
2. 排版等能夠一致.也就是說元素在大尺寸屏幕上(高分辨率)上要大些,在小尺寸屏幕(低分辨率)上要小些
做爲懶惰的人類的目的是:用最小的工作量來適配最多的屏幕,那麼怎麼做到呢?如果能有一個度量單位能隨屏幕變化而變化,那多好啊,這樣就可以指定一個長度,讓設備自己去處理變化的因素.
所以,就出來了一個新的長度單位DIP---Density Independent Pixels,也就是密度無關的一個抽象的單位長度.它不像像素或者Inch之類的固定不變,而是隨着設備的密度而變化的.在低密度的設備上它的值很小,而在高密度的屏幕上它的值會變得大些,這樣一來,開發者就可以指定一個長度從而適應不同密度的屏幕,以解決低密度屏幕變大,高密度屏幕變小的問題.詳細的來講:比如某一窗口的長度是100DIP,在低密度的屏幕上DIP值,假如說是1pixel,那麼長度就變成了100 pixels;而到了高密度屏幕上,DIP可能會變成了1.5 pixel,這樣長度就變成了150pixels. 這樣就達到了,以不變應萬變的目的.而DIP具體取什麼值,也是由設備來給出,因爲設備知道它自己是什麼樣的密度.

Android上的適配策略

Android上面如何解決適配不同尺寸(分辨率)和密度的問題呢?它主要是通過以密度分類,再加上分辨率的方式來減化適配不同尺寸屏幕的工作量.


一般來講,屏幕分辨率越高,清晰度也應該越高,也即其密度也應該越大,否則會看起來很不清楚,比如4寸的屏幕只顯示100個像素,這就近距離看電影,或者看投影儀一樣,非常的粗糙和不清晰.所以,Android主要是以屏幕密度來區分不同的設備:
高密度:        hdpi       (High dots per inch)
中等密度:    mdpi      (Medium dots per inch)
低密度:        ldpi        (Low dots per inch)
並且佈局中推薦使用密度無關單位dip或dp,來作爲長度或者寬度的單位.這樣,從理論上來講,開發者只需要做:
1. 爲不同的密度屏幕準備圖片資源
    (圖片是沒辦法的,因爲圖片的長度和寬度是固定的像素值,不能夠隨密度變化而變化,可以強行拉伸,但圖片會失真.當然也有9 Patch圖片可以解決隨意拉伸的問題.但普通的圖片的長度和寬度是固定的.
2. 用dip作爲單位來指定長度或者寬度
就可以適配所有的設備,讓佈局在所有的屏幕上都得到比較好的顯示效果.
當然,現實的生活沒有這麼完美,各種設備千差萬別.但是總體仍可分爲這三大類,爲這三大類準備好圖片後,其他的只要與某一類較接近,即使稍有拉伸或失真,也不太明顯,是可以接受的.所以,對於一般性的應用程序,寫一個佈局文件在layout中,爲三種密度準備圖片drawable-hdpi, drawable-mdpi, drawable-ldpi,就足以應對80%的設備.
res/
    drawable-hdpi/
          ic_launcher.png
    drawable-mdpi/
          ic_launcher.png
    drawable-ldpi/
          ic_launcher.png
     layout/
          main.xml
(這裏可能有點過時了,因爲現在多了xdpi,而且很多設備也是xdpi的.)
但是光以密度屏幕來分類和處理還不夠.隨着設備的越來越多,以及屏幕尺寸越來越大,還有就是Tablet的出現,又會出現這樣的問題:設備的屏幕密度雖然不高,但其分辨率很高.舉個簡單的例子:iPad2的分辨率是1024x768,iPhone 4 960x640,但是iPhone 4的密度是326ppi,遠大於iPad2.但是,無論密度有多高它的屏幕就那麼,最多能顯示960x640個像素點,一個1024*768的圖片在iPad上可以看到全部,而iPhone上只能看到一大半!這也是爲什麼用iPad來運行iPhone上的應用程序時,只是以屏幕中間的一部分來模擬顯示的原因.
對Android來說也是一樣的.如此一來,即使相同的dpi,假如其屏幕尺寸非常大,那麼爲其準備的圖片將被拉伸很大或者顯示不全.UI元素也會被拉伸很長.這樣並不是很好的體驗.對於尺寸大的屏幕應該讓其顯示更多的內容,而不是把一部分元素拉伸很大.所以,很多手機安卓應用如果未經專門適配,在平板上直接使用體驗將會是非常差的.
爲了解決這樣的問題,就還必須以屏幕尺寸來區分設備
主要有四種屏幕尺寸:small, normal, large and xlarge
這主要是配合屏幕密度來一起使用,比如,適配平板的圖片:
drawable-xlarge-hdpi/ic_launcher.png
這裏就要提到了密度,尺寸和分辨率的對應關係了. 屏幕分辨率是隨設備變化最明顯的一個,上面的二種分類方法僅是對屏幕進行的大致的一個分類.雖然屏幕分辨率與密度沒有直接的關係,但是所有的設備都基本上一致的(約定俗成?):
ldpi            QVGA         240*320           0.8
mdpi          HVGA         320*480           1.0
hdpi           WVGA        480*800           1.5
hdpi           qHD           540*960           1.5
xdpi           WXGA        720*1280          2
對於,如何適配,以及如何提供資源可以閱讀官方文檔,裏面講的還算詳細.http://developer.android.com/guide/topics/resources/providing-resources.html

適配不同屏幕的常見問題

雖然有了上述的策略的方式,但是在實際中還是不夠的.因爲屏幕尺寸的比例與密度的比例並不一致.舉例來說HVGA與WVGA相比,寬度的比例與密度的比例是一致的480/320=1.5.所以說寬度上顯示的內容一樣多.無需操心.但是高度上面就不一樣了.800/480 >1.5,這就造成了什麼情況呢,也就是同樣的東西縮放了1.5倍以後,對於WVGA還會有相當的屏幕空着.或者如果以WVGA爲基準時,放到HVGA上就會顯示不全. 在qHD上這個問題會更明顯.甚至同樣的hdpi的WVGA和qHD都會有問題,qHD能顯示的內容要多於WVGA.

使用dimension資源來解決問題

從上面的問題可以看出,密度無關的單位解決不了上面的問題.當然也可以選擇爲每一分辨率做一佈局,但這樣又會造成大量的重複工作,因爲畢竟佈局是相同的,僅是元素的高度或者寬度需要考慮分辨率和屏幕密度.
這時可以把高度或者寬度抽象出來放到一個單獨 的資源dimensions中,從而把變化的東西降到最小:
比如某個View的高度:
layout/
      main.xml               #  android:layout_height="@dimen/view_height"
values-mdpi
      dimensions.xml                 item name="view_height">20dip</item>
values-hdpi
      dimensions.xml                                                            30dip
values-hdpi-960x540
      dimensions.xml                                                            40dip
還有一點就是,儘可能用相對的值,如wrap_content和fill_parent(or match_parent)它們不是固定的值而是會在具體佈局的時候計算出來.
最後,分享一個坑,資源的默認值並不總是values,drawable和layout. 當你分別在values中指定一個值和在values-mdpi中指定一個值,對於其他的類型如hdpi會使用mdpi中的值,而非values中的值.
另外就是,做圖片時需要注意尺寸問題

因爲圖片的尺寸是以像素爲單位,而Android應用程序中多以dip或dp爲單位,所以就要注意不同密度上它們的對應關係.讓像素仁值和用dip換算過後都是整數.舉例來講HVGA上或者mdpi上1個dip就等於1個像素.但到了WVGA或者hdpi上1個dip就是1.5個像素,所以對於WVGA的圖片的尺寸的像素值最好能是1.5的倍數.這樣就更方便用dip來定義圖片的長度的寬度.


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章