Android dp和px互转、sp和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
/**
* dp转px
*/
public static int dp2px(Context context, float dp) {
float density = context.getResources().getDisplayMetrics().density;
return (int) (dp * density + 0.5f);
}

/**
* sp转px
*/
public static int sp2px(Context context, float sp) {
float scaledDensity = context.getResources().getDisplayMetrics().scaledDensity;
return (int) (sp * scaledDensity + 0.5f);
}

/**
* px转dp
*/
public static int px2dp(Context context, float px) {
float density = context.getResources().getDisplayMetrics().density;
return (int) (px / density + 0.5f);
}

/**
* px转sp
*/
public static int px2sp(Context context, float px) {
float scaledDensity = context.getResources().getDisplayMetrics().scaledDensity;
return (int) (px / scaledDensity + 0.5f);
}

Click and drag to move

源码解析

关于dp和px、sp和px的互转,网上很多都是这样写的,很多人都是copy过来直接使用,但是有没有人想过为什么呢?

其实Android原生Api有提供dip和sp转px的方法,使用如下:

1
2
3
4
// 10dp转px
TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10, context.getResources().getDisplayMetrics());
// 10sp转px
TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 10, context.getResources().getDisplayMetrics());

Click and drag to move

我们再看下TypedValue.applyDimension()方法的源码

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;
}

Click and drag to move

看了以上源码是不是瞬间就明白了dp2px和sp2px的原理,只要看以上源码的第二和第三个case

  • px = dp * density
  • px = sp * scaledDensity

然后我们又可以反推出

  • dp = px / density
  • sp = px / scaledDensity

为什么还要加0.5

可能有人会问了,为什么我们开头的dp2px、sp2px、px2dp、px2sp四个方法里都还要加0.5?

其实也可以不加0.5,如果返回值是float的话

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
/**
* dp转px
*/
public static float dp2px(Context context, float dp) {
float density = context.getResources().getDisplayMetrics().density;
return dp * density;
}

/**
* sp转px
*/
public static float sp2px(Context context, float sp) {
float scaledDensity = context.getResources().getDisplayMetrics().scaledDensity;
return sp * scaledDensity;
}

/**
* px转dp
*/
public static float px2dp(Context context, float px) {
float density = context.getResources().getDisplayMetrics().density;
return px / density;
}

/**
* px转sp
*/
public static float px2sp(Context context, float px) {
float scaledDensity = context.getResources().getDisplayMetrics().scaledDensity;
return px / scaledDensity;
}

Click and drag to move

但是Android中很多有关尺寸的设置都是px,且是int类型,比如view.setWidth()、view.setHeight()的参数都是int类型,所以更多时候我们需要用到的是int类型。

那么如果要转化为int类型就需要加上0.5达到四舍五入的效果。

那么问题又来了,为什么加上0.5就可以四舍五入呢?举个栗子

java中浮点型强制类型转换成int类型是没有四舍五入的,而是直接把小数点后的值直接去掉

1
2
3
4
5
6
i1 = (int) 1.0; // 结果为1
i2 = (int) 1.1; // 结果为1
i3 = (int) 1.4; // 结果为1
i4 = (int) 1.5; // 结果为1
i5 = (int) 1.6; // 结果为1
i6 = (int) 1.9; // 结果为1

Click and drag to move

如果加上0.5再转换,看下结果

1
2
3
4
5
6
i1 = (int) (1.0 + 0.5); // 结果为1
i2 = (int) (1.1 + 0.5); // 结果为1
i3 = (int) (1.4 + 0.5); // 结果为1
i4 = (int) (1.5 + 0.5); // 结果为2
i5 = (int) (1.6 + 0.5); // 结果为2
i6 = (int) (1.9 + 0.5); // 结果为2

Click and drag to move

只要小数点后一位大于等于5再加上0.5就会达到五入的效果,得到更精确的值。这也就是为什么要加0.5的原因。