1. 程式人生 > >Android開發必備知識--適配問題(全)+資源值載入問題+dpi解析度問題

Android開發必備知識--適配問題(全)+資源值載入問題+dpi解析度問題

一些名詞的解釋:

      (1)螢幕尺寸:單位英寸(inch),指的是螢幕對角線長度。

  (2)螢幕密度:單位dpi,指的是每inch上可以顯示多少畫素點即px。

  (3)螢幕解析度:單位px * px,如1280×800,我使用的小米853×480等,指的是一屏顯示多少畫素點。

  (4)螢幕無關畫素:單位dp/dip,指的是自適應螢幕密度的畫素,用於指定控制元件寬高。

  (5)刻度無關畫素:單位sp,指的是自適應字型的畫素,用於指定文字大小。

      (6)更精確的講法:ppi

關於安卓適配問題是比較麻煩的,其中理解drawable資原始檔載入方式和一些必要的解析度問題是非常重要的。

in:英寸inch,物理尺寸,1in =2.54釐米(cm)。4.2寸手機等等就是這個in,通常說的手機4.2寸,都是手機對角線4.2in。

dpi:這個知道英文名有助於理解意義,its english name is:  Dots Per Inch.每英寸的點數嘛,每英寸的畫素個數。例如:320X480解析度的手機,寬2in,高3in,那麼每英寸畫素點個數(dpi)是:320/2=160.使用正方形畫素點橫縱向計算結果一樣,一般是計算對角線。

density:這個攪屎棍極具混淆作用,這貨是螢幕密度,螢幕密度density和dpi的關係是density = dpi/160,協議規範。這個是對dpi的一個規範,160dpi就是密度為1的意思。

dp:主角閃亮登場,也就是dip,裝置獨立畫素,device independent pixels的簡寫,android特有,在螢幕密度為1也就是dpi=160的螢幕,1dp = 1px。這是規範。

sp:和dp類似,用來設定字型,和dp不同的是它可以自動根據使用者的字型大小偏好來縮放,比如說你字型用的sp,使用者系統設定字型偏好是偏大,那麼sp會相應放大字型,而如果你用dp,則不會放大。

px:pixel,畫素,螢幕上的點,是數碼裝置最小的獨立顯示單位,px均是整數,解析度480X800就是,畫素點個數。

說到這裡,問題來了,一個圖片載入到記憶體的話佔用多大空間呢?一個32位的圖解析度是1280*768,所佔記憶體大小:(1280*768*(32/8))/(1024*1024) = 3.75MB    畫素點數*一個畫素點所佔用的byte數,32位圖表示一個畫素佔用32個bit位,也就是4byte。

ppi: 和螢幕密度一個意思, 全稱是pixel per inch.  是專業一點的叫法.


一個bitmap,是解析度是1024*1024,要是32位的大小就是32/8=4m。

適配的時候drawable級別:android會根據螢幕尺寸自動選擇相應資原始檔進行渲染,sdk檢測到你的手機dpi是160的話,優先去drawable-mdpi下找相應圖片資源,找不到會區別的資料夾找,並根據density做相應縮放。比如dpi。=160的裝置,wrap-content設定資源,在mdpi沒找到圖片,從xhdpi找到了,240X240px的圖片,240會除以2乘以1得到120px

關於Drawable資源對應dpi值
mdpi120dpi-160dpi
hdpi160dpi-240dpi
xhdpi240dpi-320dpi
xxhdpi320dpi-480dpi
xxxdpi480dpi-640dpi

當一個apk執行起來時,Android系統會根據其所執行的手機的螢幕密度去相對應的圖片資料夾裡找指定名稱的圖片。 注意, 先去哪個目錄裡找,完全是根據這個手機的螢幕密度決定的。


其中注意兩點:

1, 中等解析度,即mdpi的螢幕密度是160,他是標準的參考密度。所以計算比例的時候它的比例值是1. 其他螢幕密度的參考比例都是以這個為依據。

2, 預設的drawble目錄(一般是自己建的),和mdpi是一樣的。將圖片放到這個目錄和放到drawble-mdpi目錄是一樣的效果。不過一般習慣性的放一些自定義selector或者點9的圖片在這裡。

現在我們來看, HTC one V手機的螢幕密度是252ppi, 那距離哪一個最靠近呢, 就是hdpi了。 所以當apk執行在這個手機上時,首先會去這個目錄找圖片。

下面是用常見的一些型別的手機總結的一個表格:

需要明確的是螢幕密度、解析度、物理尺寸之間的關係: (參考:https://blog.csdn.net/cloud_castle/article/details/52313858)

這裡寫圖片描述

以一個解析度為1920x1200,物理尺寸為7寸的手持平板而言,根據勾股定理,我們可以得出其對角線上的畫素數大約為2264,用2264除以7就是此螢幕的螢幕密度,結果為323. 
也就是說,螢幕密度、解析度、物理尺寸可以由二推一,hdpi-1920x1200螢幕的物理尺寸絕對要比hdpi-1280x800大,這也是我們之後討論適配的前提。

· 實際密度與系統密度
我們經常見到的Android裝置螢幕密度大多為120、160、240、320、480等,而上述例子中的323dpi則是裝置的實際密度,說明這塊螢幕每寸有323個畫素。得到實際密度以後,Android系統推薦選擇一個最近的密度作為系統密度,通過DisplayMetrics類獲取上述裝置的系統密度就是320dpi。 

但是,現在很多Android廠商不一定會選擇這些值作為系統密度,而是選擇實際的dpi作為系統密度,這就導致了很多手機的dpi也不是在這些值內。例如小米Note這樣的xxhdpi的裝置他的系統密度並不是480,而是它的實際密度440。

drawable目錄

我們經常會給應用程式切幾套圖片,放在drawable-mdpi、drawable-hdpi、drawable-xhdpi等目錄下面。當應用在裝置對應dpi目錄下沒有找到某個資源時,遵循“先高再低”原則,然後按比例縮放圖片:

  • 比如,當前為xhdpi裝置,並且只有以下幾個目錄,則drawable的尋找順序為: 
    xhdpi->xxhdpi->xxxhdpi(如果沒有更高的了)->nodpi(如果有的話)->hdpi->mdpi,如果在xxhdpi中找到目標圖片,則壓縮2/3來使用,如果在mdpi中找到圖片,則放大2倍來使用。
  • 上面說的對應關係,都是首選目錄, 那如果首選目錄裡面找不到圖片呢? 

    Android圖片選擇策略

    如果螢幕所對應的資料夾沒有要找的圖片,怎麼辦。這是很常見的,我們開發專案時一般不會去為每一個級別的螢幕去切一套圖片。那樣做只會讓apk很大。所以一般性的圖片我們只切一兩個典型密度螢幕的圖片。但是apk是有可能會執行在從ldpi到xxhdpi的各種級別的手機上。這個時候就需要根據一定的策略去尋找圖片了。步驟是這樣的:

    1, 去螢幕密度對應的目錄去找。如果找到就拿來用。

    2, 如果沒找到,就去比這個密度高一級的目錄裡面去找,如果找到就拿來用。

    3, 如果沒找到就繼續往上找。以此類推。

    4, 如果到了xxhdpi目錄還沒有找到的話,就會去比自身螢幕密度低一級的目錄去找,如果低一級的目錄>=hdpi,找到了就拿來用。

    5, 如果沒找到, 就去mdpi目錄去找, 如果找到了,就拿來用。

    6, 如果沒找到,就去預設的drawble目錄裡去找, 如果找到了就拿來用。

    7 ,如果沒找到,再去最低的ldpi目錄裡去找。如果找到了,就拿來用。

    8, 如果沒找到, 那就是沒找到了, 圖片無法顯示。(不過一般不會出現這種現象,因為如果每個目錄都沒有這個圖片的話,你是編譯不過的)

    這裡有兩點需要注意:

    ①  首先會去比自己密度高的目錄裡去找,這是因為因為系統相信,你在密度更高的目錄裡會放置解析度更大的圖片,這樣的話這個圖片會被縮小,但同時顯示效果不會有損失,但是如果優先去低一級別的目錄去找的話, 找到的圖片就會被放大,這樣的話這個圖片就會被拉扯模糊了。

    e.g. 同一張圖片,你在mdpi和xxhdpi目錄各放了一份, 這個應用你現在執行在hdpi的手機上, 那應用會選擇哪張圖片呢。答案是xxhdpi目錄裡的。即便hdpi離mdpi更近一點!

    ②,如果在mdpi裡找不到是不會直接去ldpi裡找的, 而是先去預設的drawble目錄裡找,這是drawble目錄和drawble-mdpi是一個級別的。

    下面用一張流程圖來總結:

     


這很好理解,如果我們按規則放置兩張圖片,mdpi中為48x48,xxhdpi中為144x144,那麼不管我們最後從哪個目錄拿到圖片,在xhdpi裝置上顯示的畫素大小都是96x96,只是一個被拉伸而來,一個被壓縮而來。由於xhdpi定義了96個畫素點的物理尺寸,那麼這張圖的物理尺寸實際就被定下來了。 
同樣的,mdpi中48個畫素點的物理尺寸與xhdpi中96個畫素點的物理尺寸是相同的,這就保證了該圖片在任何裝置上顯示出的視覺大小一致。 
那麼,一個結論就是,對於期望保持視覺大小一致的那部分圖片而言,如果你也能接受android為你拉伸/壓縮圖片導致一定程度的模糊或者銳化,那麼這些圖片是不需要在每個drawable目錄下都製作一份的。以現在主流裝置來說一般可能在drawable-xxhdpi放置一份即可,這樣可以儘量避免Android為我們放大圖片所導致的OOM。

當然,在某些情況下,我們會主觀希望打破android提供的“視覺大小一致”這種機制,此時我們就可以建立另外的drawable目錄來放置需要變化的圖片了。附一份比例表: 

這裡寫圖片描述

values目錄(參考https://blog.csdn.net/cloud_castle/article/details/52313858)

values目錄用來放置colors.xml,dimens.xml,strings.xml等,也可以根據螢幕密度設定特定的values目錄讓滿足設定的裝置進行載入,比如values-mdpi、values-hdpi、values-xhdpi、values-xxhdpi等等,然後每個目錄放置一個demins.xml,使不同解析度的裝置應用不同的尺寸設定。當應用裝置在當前dpi對應目錄的demins.xml中沒有找到目標條目時,採用“就近匹配”原則:

  • 比如,當前為hdpi裝置,並且只有以下幾個目錄,則values的尋找順序為: 
    hdpi->xhdpi->mdpi->values,即先向上級dpi目錄查詢,再向下級dpi目錄查詢,最後一路向下查詢到values目錄,如果values下都找不到,就只有找values-ldpi,當然,現在有這個目錄的應用不多了。

那麼,我們需要將mdpi目錄下的值都乘以相應的倍數來放置在其他目錄下面嗎?答案當然是否定的,由於我們對期望螢幕密度無關的值都定義為了dp或者dip的單位,無論android從哪個目錄最終找到該值,都會直接應用這個值與當前裝置的密度來計算最終的尺寸。

也就是說,如果我們同樣在values-xhdpi和values下寫 length=10dp 
那麼在mdpi裝置上得到的都是10px,在xhdpi裝置上得到的都是20px。 
但它們看起來“都是一樣寬”,這樣就已經是“保證視覺大小一致性了”。 
48dp法則告訴我們,48dp的物理尺寸約等於9mm,是人的手指比較容易點選到的大小,並且是獨立於裝置的。

顯而易見,如果我們在values目錄下寫length=10dp,values-xhdpi目錄下寫length=20dp,mdpi裝置上得到的將是10px,而xhdpi裝置上得到的將是40px,得到視覺結果就是,該控制元件在xhdpi裝置上看起來比mdpi裝置上大了一倍。

  • 考慮這樣一個情況:有一個BottomBar,左右兩端各有一個按鈕,按鈕長寬用dp定義,這樣在一個大屏手機中,兩個按鈕可能就相隔更遠了,因為按鈕的視覺尺寸是沒有變化的。如果你想保持按鈕在BottomBar中所佔的比例,最好辦法不是新增一個values-xxx,然後重寫這兩個dp值,而是精心設計你的佈局。

那麼,既然最後都要找到values,並且能夠保證視覺大小一致性,那何必再新增其他values解析度目錄呢?答案是在某些情況下,我們主觀希望某些尺寸不去保持視覺一致性。例如一個Button,在手機上那麼大剛好,但如果在平板裝置上,是的,它看起來和在手機上一樣大,但是,它顯得有點小了。

也就是說,我們應該把希望在任何裝置上視覺大小都一樣的尺寸都放置在values目錄下並且只放置這一份,其他需要有變化的尺寸則放置在對應目錄下即可

一般而言,使用在物理尺寸相差不大的幾套裝置上,一個values可能就夠了,因為它本身就保證了“視覺大小一致性”,但是如果你的應用需要相容平板,甚至電視,那麼這種一致性可能是一種災難。這時可以考慮新增一個對應dpi的values目錄,把需要變化的值拷貝進去重寫,但我更推薦採用values-xhdpi-2560x1600,我們很容易通過這裡的螢幕解析度+dpi計算得到該裝置的物理尺寸,顯而易見這是一個平板裝置,如此我們的改動便不至於影響同DPI的低物理尺寸裝置(手機),而物理尺寸差不多的裝置是可以共用一套dimens.xml的。

  • 那麼,如果當前裝置為xhdpi-1184x800,當前目錄有values-xhdpi-1184x800,values-xhdpi-1184x960,values-xhdpi-1184x720,android的尋找順序則是: 
    values-xhdpi-1184x800->values-xhdpi-1184x720->values-xhdpi

只向低於自己解析度的目錄下尋找,直到values-xhdpi,如果依然沒有找到,按照之前的順序繼續進行。(hdpi-1280x800 -> hdpi-1280x720 -> hdpi -> …) 
也就是說,對於同dpi的多臺不同解析度平板裝置,如果佈局足夠通用,我們可以只針對最低解析度設計dimens即可,上面的例子中,則是values-xhdpi-1184x720。 
我們還可以將這個解析度寫得更低,低到我們有把握:如果再出現比這個解析度更低的裝置,那麼它的物理尺寸一定滿足即使採用values-xhdpi中針對手機物理尺寸設計的大小也沒有問題。


關於字型大小的適配

思路是如上圖,為不同解析度,不同密度準備一套字型大小的尺寸

在values-xxx-xxx資料夾下建立dimen。xml檔案:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <dimen name="size_title">20sp</dimen>
    <dimen name="size_name">18sp</dimen>
    <dimen name="size_code">10sp</dimen>
</resources>

有一些我在配置過程中碰到的問題和大家分享一下:

1.資料夾命名:

    如上圖,有些錯誤的資料夾,我的結論是解析度x號前面的數值要比後面的大,不然系統無法識別,

注意:乘號的輸入法,千萬別用漢字輸入法下的x,不然系統無法系別,不要問我為什麼,我是不會告訴你這玩意花了我起碼3個小時才搞明白

2.系統會自動匹配values資料夾

  舉個例子,之前提到的(2)1280x800的,如果你建立values-mdpi-1250x800,values-mdpi-1200x800,(注意了這兩個都不是1280x800的),最後真機聯調的時候,顯示的是比真實解析度小的最大的那套尺寸即values-mdpi-1250x800,而不是1200x800,這點上虛擬機器和真機有很大的區別,xml檢視只會找1280x800的尺寸,如果沒有,他就顯示預設大小的字型。

可以參考下這一篇:http://www.cnblogs.com/zealotrouge/archive/2012/11/23/2784774.html

3。如果系統系統自動匹配的時候沒有找到最小的尺寸,就會報錯

 所以建議在values下dimen.xml下儲存一份最小的字型尺寸,再難看也好過報錯,是吧


此部落格僅用作交流學習與自我參考,參考部落格:https://blog.csdn.net/xiebudong/article/details/37040263