Android手機適配,手機尺寸、px、dpi、dp、sp詳解


最近一直在學習Android的適配問題,在學習的過程中發現很多博客抄來抄去,並沒有什么實質的東西,因此決定將自己關於Android手機適配問題的學習筆記整理出來,希望都夠幫助到大家。

要學習Android的適配問最近一直在學習Android的適配問題,在學習的過程中發現很多博客抄來抄去,並沒有什么實質的東西,因此決定將自己關於Android手機適配問題的學習筆記整理出來,希望都夠幫助到大家。題,以下幾個概念是必須要理解的。

px:像素,pixel的縮寫。這個應該不需要過多解釋,平常我們所說的手機的分辨率為1920x1080,這里的單位用的就是px,也就是說高為1920個像素,寬為1080個像素。

手機尺寸:這個大家也不陌生,就是手機斜對角線的長度。Nexus 5的尺寸為4.95英寸,其實指的就是斜對角之間的距離。


dpi:dots per inch 每英寸上像素的點數,它的計算方式為(手機斜對角線上的像素數/手機尺寸)。比如谷歌的親兒子Nexus 5的分辨率為1920x1080,手機尺寸為4.95英寸。那么此款手機的DPI就是(1920*1920+ 1080*1080)½/4.95≈445dpi。

dip或者簡寫為dp:device independent pixels 設備獨立像素(也有人將d理解為density,將dip翻譯成密度無關像素,不過個人認為還是翻譯成設備獨立像素比較好,畢竟TypedValue類中聲明COMPLEX_UNIT_DIP這個變量時就是將d翻譯為device),谷歌推薦布局使用的單位。

sp:scaled pixels 可縮放的像素,谷歌推薦字體使用的單位。

上面簡單介紹了一下幾個概念的意思,接下來咱們就要弄清楚幾個問題:

1.我們知道Android資源文件下有這樣幾個目錄,ldpi、mdpi、hdpi、xhdpi、xxhdpi、xxxhdpi,它們與dpi有什么關系?

2.dp、sp和px之間又有什么關系?

關於第一個問題我們有必要來看一下DisplayMetrics這個類,這個類中定義了一些常量,讓我們來一起看一下都是些什么常量:

[java] view plain copy 在CODE上查看代碼片派生到我的代碼片
  1. /** 
  2.  * Standard quantized DPI for low-density screens. 
  3.  */  
  4. public static final int DENSITY_LOW = 120;  
  5.   
  6. /** 
  7.  * Standard quantized DPI for medium-density screens. 
  8.  */  
  9. public static final int DENSITY_MEDIUM = 160;  
  10.   
  11. /** 
  12.  * This is a secondary density, added for some common screen configurations. 
  13.  * It is recommended that applications not generally target this as a first 
  14.  * class density -- that is, don't supply specific graphics for this 
  15.  * density, instead allow the platform to scale from other densities 
  16.  * (typically {@link #DENSITY_HIGH}) as 
  17.  * appropriate.  In most cases (such as using bitmaps in 
  18.  * {@link android.graphics.drawable.Drawable}) the platform 
  19.  * can perform this scaling at load time, so the only cost is some slight 
  20.  * startup runtime overhead. 
  21.  * 
  22.  * <p>This density was original introduced to correspond with a 
  23.  * 720p TV screen: the density for 1080p televisions is 
  24.  * {@link #DENSITY_XHIGH}, and the value here provides the same UI 
  25.  * size for a TV running at 720p.  It has also found use in 7" tablets, 
  26.  * when these devices have 1280x720 displays. 
  27.  */  
  28. public static final int DENSITY_TV = 213;  
  29.   
  30. /** 
  31.  * Standard quantized DPI for high-density screens. 
  32.  */  
  33. public static final int DENSITY_HIGH = 240;  
  34.   
  35. /** 
  36.  * Standard quantized DPI for extra-high-density screens. 
  37.  */  
  38. public static final int DENSITY_XHIGH = 320;  
  39.   
  40. /** 
  41.  * Intermediate density for screens that sit somewhere between 
  42.  * {@link #DENSITY_XHIGH} (320 dpi) and {@link #DENSITY_XXHIGH} (480 dpi). 
  43.  * This is not a density that applications should target, instead relying 
  44.  * on the system to scale their {@link #DENSITY_XXHIGH} assets for them. 
  45.  */  
  46. public static final int DENSITY_400 = 400;  
  47.   
  48. /** 
  49.  * Standard quantized DPI for extra-extra-high-density screens. 
  50.  */  
  51. public static final int DENSITY_XXHIGH = 480;  
  52.   
  53. /** 
  54.  * Intermediate density for screens that sit somewhere between 
  55.  * {@link #DENSITY_XXHIGH} (480 dpi) and {@link #DENSITY_XXXHIGH} (640 dpi). 
  56.  * This is not a density that applications should target, instead relying 
  57.  * on the system to scale their {@link #DENSITY_XXXHIGH} assets for them. 
  58.  */  
  59. public static final int DENSITY_560 = 560;  
  60.   
  61. /** 
  62.  * Standard quantized DPI for extra-extra-extra-high-density screens.  Applications 
  63.  * should not generally worry about this density; relying on XHIGH graphics 
  64.  * being scaled up to it should be sufficient for almost all cases.  A typical 
  65.  * use of this density would be 4K television screens -- 3840x2160, which 
  66.  * is 2x a traditional HD 1920x1080 screen which runs at DENSITY_XHIGH. 
  67.  */  
  68. public static final int DENSITY_XXXHIGH = 640;  
  69.   
  70. /** 
  71.  * The reference density used throughout the system. 
  72.  */  
  73. public static final int DENSITY_DEFAULT = DENSITY_MEDIUM;  
  74.   
  75. /** 
  76.  * Scaling factor to convert a density in DPI units to the density scale. 
  77.  * @hide 
  78.  */  
  79. public static final float DENSITY_DEFAULT_SCALE = 1.0f / DENSITY_DEFAULT;  
  80.   
  81. /** 
  82.  * The device's density. 
  83.  * @hide because eventually this should be able to change while 
  84.  * running, so shouldn't be a constant. 
  85.  * @deprecated There is no longer a static density; you can find the 
  86.  * density for a display in {@link #densityDpi}. 
  87.  */  
  88. @Deprecated  
  89. public static int DENSITY_DEVICE = getDeviceDensity();  

首先,咱們看到了第一個定義的常量是DENISTY_LOW。咦,好巧,咱們的ldpi中l不就是low的縮寫嗎?接着往下看,第9行定義了MEDIUM,33行定義了HIGH,38行定義了XHIGH,51行定義了XXHIGH,68行定義了XXXHIGH,至此咱們所有的文件目錄都找到了相對應的值。也就是說,當手機dpi為120時就會去加載ldpi目錄下的資源,依次類推,手機dpi為160時會去加載mdpi目錄下的資源,等等。至於其它的幾個值比如TV、400一般情況下大家不會用到,講起來也比較麻煩,所以就不做解釋了。

OK,這幾個目錄所對應的的dpi的值咱們搞清楚了,接下來咱們來看一下第二個問題,dp、sp和px之間到底有着什么關系。

其實咱們在布局文件中寫的寬和高的值都會先調用TypedValue類中的applyDimension()方法進行一次轉換,那么這個方法是干什么用的呢,讓我們先看一下它的源碼:

[java] view plain copy 在CODE上查看代碼片派生到我的代碼片
  1. /** 
  2.  * Converts an unpacked complex data value holding a dimension to its final floating  
  3.  * point value. The two parameters <var>unit</var> and <var>value</var> 
  4.  * are as in {@link #TYPE_DIMENSION}. 
  5.  *   
  6.  * @param unit The unit to convert from. 
  7.  * @param value The value to apply the unit to. 
  8.  * @param metrics Current display metrics to use in the conversion --  
  9.  *                supplies display density and scaling information. 
  10.  *  
  11.  * @return The complex floating point value multiplied by the appropriate  
  12.  * metrics depending on its unit.  
  13.  */  
  14. public static float applyDimension(int unit, float value,  
  15.                                    DisplayMetrics metrics)  
  16. {  
  17.     switch (unit) {  
  18.     case COMPLEX_UNIT_PX:  
  19.         return value;  
  20.     case COMPLEX_UNIT_DIP:  
  21.         return value * metrics.density;  
  22.     case COMPLEX_UNIT_SP:  
  23.         return value * metrics.scaledDensity;  
  24.     case COMPLEX_UNIT_PT:  
  25.         return value * metrics.xdpi * (1.0f/72);  
  26.     case COMPLEX_UNIT_IN:  
  27.         return value * metrics.xdpi;  
  28.     case COMPLEX_UNIT_MM:  
  29.         return value * metrics.xdpi * (1.0f/25.4f);  
  30.     }  
  31.     return 0;  
  32. }  

根據注釋,不難理解,這個方法的作用是將對應的值轉化為實際屏幕上的點值,也就是像素值。此方法接受三個參數,第一個為單位的類型,第二個為數值,第三個為DisplayMetrics對象,可以使用Resources.getSystem().getDisplayMetrics()獲得。

既然知道了這個方法是將各種單位轉化為像素,那咱們就先來看一下傳入dp的話系統是怎么給咱們轉換成px的吧。

首先,第17行會對傳入的參數類型進行一個判斷,如果是dp的話進進入第20行的判斷條件,返回的值為咱們傳進的值value*metrics.density。

那么metrics.density的值為多少呢?咱們再來看一下DisplayMetrics這個類,這次咱們看到它有一個setToDefaults()方法:

[java] view plain copy 在CODE上查看代碼片派生到我的代碼片
  1. public void setToDefaults() {  
  2.         widthPixels = 0;  
  3.         heightPixels = 0;  
  4.         density =  DENSITY_DEVICE / (float) DENSITY_DEFAULT;  
  5.         densityDpi =  DENSITY_DEVICE;  
  6.         scaledDensity = density;  
  7.         xdpi = DENSITY_DEVICE;  
  8.         ydpi = DENSITY_DEVICE;  
  9.         noncompatWidthPixels = widthPixels;  
  10.         noncompatHeightPixels = heightPixels;  
  11.         noncompatDensity = density;  
  12.         noncompatDensityDpi = densityDpi;  
  13.         noncompatScaledDensity = scaledDensity;  
  14.         noncompatXdpi = xdpi;  
  15.         noncompatYdpi = ydpi;  
  16.     }  

從名字上判斷出,這是一個賦值方法。在這個方法中的第4行咱們可以看到給density賦值為DENSITY_DEVICE / (float) DENSITY_DEFAULT。我靠,又是這么巧,我記得剛剛咱們看DisplayMetrics類中定義常量的代碼中是有這兩個值的!事不宜遲,咱們回過頭來再看一下DisplayMetrics類中定義常量的代碼。第89行定義了DENSITY_DEVICE 的值為getDeviceDensity(),其實就是拿到的就是咱們手機本身的dpi。第73行定義了DENSITY_DEFAULT的值為DENSITY_MEDIUM也就是160。

到現在,咱們終於能夠總結出dp和px的換算公式了:px = (手機本身的dpi / 160)  * dp。比如某款手機的dpi為320,那么5dp所對應的的px= (320/160) * 5 = 10px。

接下來咱們再來看一下sp與px的換算。還是TypedValue類中的applyDimension()方法,直接來到第22行,可以看到類型為sp時px的值為values*metrics.scaledDensity。接下來再找到DisplayMetrics類的setToDefaults()方法,來到第6行,scaledDensity=density ,接下來咱們來看一下density是什么。恩?等等,density?這個名字怎么那么熟悉啊?剛剛dp與px是怎么換算的來着?趕緊回過頭來看一下TypedValue類中的applyDimension()方法,直接來到第21行。Oh My God!咱們看到了什么!當單位是dp時px = value*metrics.density,當單位是sp時px = values*metrics.scaledDensity,而且scaledDensity=density !好了不用我多說了,我想大家都明白了,搞了半天原來dp和sp是一回事啊。

但是,它們真的一樣嗎?如果真的一模一樣的話谷歌為什么自找麻煩搞出來sp這個單位,直接用一個dp不是更省事嗎?而且sp為什么叫做scaled pixels?

其實默認情況下咱們認為sp和dp一致是沒有問題的,不過不知道大家記不記得在手機的“系統設置”的“顯示”中可以修改字體的大小。沒錯,默認情況下,字體大小為“普通”,這時候dp和sp兩個單位保持一致。但當修改了字體大小之后,所有以sp為單位的字體都會進行相應的縮放(具體的縮放比例參見我的另一篇博客Android系統設置大號字體后布局錯亂的問題),所以谷歌推薦的字體單位的名字才叫做sp(scaled pixels)。

除了上面這種情況還有一種情況會導致sp與dp不一致。正如大家知道的那樣,Android是開源的,開源到所有的手機廠商都可以定制自己的系統。所以,當手機廠商定制了屬於自己的字體之后,也會導致這兩個單位有偏差。



注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2020 ITdaan.com