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是开源的,开源到所有的手机厂商都可以定制自己的系统。所以,当手机厂商定制了属于自己的字体之后,也会导致这两个单位有偏差。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值