Android开发中使用的度量单位px、dp、sp、pt、in、mm

关于多屏的支持,最好的文章当然是官方文档:Supporting Multiple Screens。一篇文章,从概念到解决方案,非常全面。

下面主要讨论一下开发过程中使用的度量单位的问题。

单位的意义及其之间的转换

对于不同的度量单位(pxdpspptinmm),可以从下面一张图看出它们之间的关系:

android_font_size_tesst

左上->右上->左下->右下,依次为ldpi->mdpi->hdpi->xhdpi(其大小不代表绝对大小,只用关注它们之间的相对大小即可)。结论如下:

px

px是唯一一个显示大小与dpi有关系的单位,其它单位的显示大小都与dpi无关。

dp

dp也叫dip,即density-independent-pixel,在dpi等于160的时候,其大小与px相等。因此,我们可以算出1dp等于densityDpi / DENSITY_DEFAULT(160)) px,也就是density px。具体关系参考下面介绍DisplayMetrics中属性的表格。

sp

除了具有dp的特征之外,sp还可以响应用户的font size preference设置其值像dp一样,也只能取一些离散的值。font size preference的设置界面如下:

android_font_preference_setting

ptinmm

这几个单位都是长度单位,其中pt等于1/72inch,都是用户最终看到的大小。这些值都是根据设备真实物理像素密度(xdpiydpi)算出来的,不会像dpsp一样,只有几个离散的值。

在TypedValue.java中有所有单位到px的转换方法:

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

该函数将所有的单位转换为px,其中参数意义如下:

属性 意义
metrics.density 默认值为densityDpi / (float) DENSITY_DEFAULT。该值为归整之后的值:比如150dpi的设备,density值为1,即按照160dpi进行处理。density的典型值为0.75、1.0、1.5、2.0、3.0等。
metrics.densityDpi 设备物理像素密度,非精确的归整值,可取值为DENSITY_LOW(120)DENSITY_MEDIUM(160)DENSITY_HIGH(240)等。density的值是根据densityDpi / DENSITY_DEFAULT计算出来的,所以取值也是一些典型的离散值。
metrics.scaledDensity 具有density所有的特征,如果用户设置了font size preference,还需要乘以一个scale系数。
metrics.xdpi x方向的物理像素密度,精确的值,单位px/inch。同样的有ydpi
DENSITY_DEFAULT dpi的参考值,值为160px/inch。

dp转换为px

有时候我们必须使用px作为单位,比如某个api接受的参数只能是px。这个时候,需要将dp转化为px。在code中我们可以使用下面的方法将dp转化为px

1
2
3
4
5
6
7
8
9
// The gesture threshold expressed in dp
private static final float GESTURE_THRESHOLD_DP = 16.0f;

// Get the screen's density scale
final float scale = getResources().getDisplayMetrics().density;
// Convert the dps to pixels, based on density scale
mGestureThreshold = (int) (GESTURE_THRESHOLD_DP * scale + 0.5f);

// Use mGestureThreshold as a distance in pixels...

Best Practice

  1. Use wrap_content, match_parent, or dp units when specifying dimensions in an XML layout file
  2. Do not use hard coded pixel values in your application code
  3. Do not use AbsoluteLayout (it’s deprecated). Use RelativeLayout instead.
  4. Supply alternative bitmap drawables for different screen densities

另外一篇关于网页上字体大小单位的讨论:Font Size in Html - Px, Em, Rem