最近这一段时间比较忙,都没怎么写博客了,这篇文章主要想介绍几个有关dp、sp和px相互转换的系统api:getDimension()、getDimensionPixelSize()和getDimensionPixelOffset()的区别。
回想起刚学android时,dp转px或sp转px都是用的网上的工具类DensityUtils,如下:
其实要想验证这个问题也不难,可以直接去手机设置修改字体的大小,这样scaledDensity和density就不一样了。还有一个方法就是直接看这些方法里面的实现,这样就可以百分百确认我们的推论了,进入这三个方法就可知道,它们最后都调用了TypedValue的applyDimension方法,其实现如下:
还有个问题是为啥工具类换算出来的值相差了一点呢?其实这也是小数点和取整造成的,1px左右的差距倒影响不大,具体使用哪几个方法获取px,看自己喜好了。
回想起刚学android时,dp转px或sp转px都是用的网上的工具类DensityUtils,如下:
/**
* dp转px
*/
public static int dp2px(float dpVal) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpVal, Resources.getSystem().getDisplayMetrics());
}
/**
* sp转px
*/
public static int sp2px(float spVal) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, spVal, Resources.getSystem().getDisplayMetrics());
}
/**
* px转dp
*/
public static float px2dp(float pxVal) {
final float scale = Resources.getSystem().getDisplayMetrics().density;
return (pxVal / scale);
}
/**
* px转sp
*/
public static float px2sp(float pxVal) {
return (pxVal / Resources.getSystem().getDisplayMetrics().scaledDensity);
}
其实它们的实现逻辑也是一样的,都是根据系统的api来换算出来的,接下来验证一下,我们来引入utilcodex库:
implementation 'com.blankj:utilcodex:1.30.6'
然后在dimens文件定义几个值:
<dimen name="dp_41">41dp</dimen>
<dimen name="px_41">41px</dimen>
<dimen name="sp_41">41sp</dimen>
最后在Activity里打印日志验证我们的结果:
float density = ScreenUtils.getScreenDensity();
float scaledDensity = Resources.getSystem().getDisplayMetrics().scaledDensity;
int densityDpi = ScreenUtils.getScreenDensityDpi();
Log.e(TAG, "density:" + density);
Log.e(TAG, "scaledDensity:" + scaledDensity);
Log.e(TAG, "densityDpi:" + densityDpi);
float floatDp = this.getResources().getDimension(R.dimen.dp_41);
int pixelSizeDp = this.getResources().getDimensionPixelSize(R.dimen.dp_41);
int pixelOffsetDp = this.getResources().getDimensionPixelOffset(R.dimen.dp_41);
Log.e(TAG, "floatDp:" + floatDp);
Log.e(TAG, "pixelSizeDp:" + pixelSizeDp);
Log.e(TAG, "pixelOffsetDp:" + pixelOffsetDp);
float floatPx = this.getResources().getDimension(R.dimen.px_41);
int pixelSizePx = this.getResources().getDimensionPixelSize(R.dimen.px_41);
int pixelOffsetPx = this.getResources().getDimensionPixelOffset(R.dimen.px_41);
Log.e(TAG, "floatPx:" + floatPx);
Log.e(TAG, "pixelSizePx:" + pixelSizePx);
Log.e(TAG, "pixelOffsetPx:" + pixelOffsetPx);
float floatSp = this.getResources().getDimension(R.dimen.sp_41);
int pixelSizeSp = this.getResources().getDimensionPixelSize(R.dimen.sp_41);
int pixelOffsetSp = this.getResources().getDimensionPixelOffset(R.dimen.sp_41);
Log.e(TAG, "floatSp:" + floatSp);
Log.e(TAG, "pixelSizeSp:" + pixelSizeSp);
Log.e(TAG, "pixelOffsetSp:" + pixelOffsetSp);
Log.e(TAG, "sp2px:" + DensityUtil.sp2px(40f));
Log.e(TAG, "dp2px:" + DensityUtil.dp2px(40f));
其中density 是我们测试手机的屏幕密度,scaledDensity是我们手机的缩放密度,而densityDpi 是像素密度,不太了解的可去度娘看看。
日志结果
2021-08-16 16:52:58.943 17590-17590/com.littlejerk.sample E/DpActivity: density:2.75
2021-08-16 16:52:58.943 17590-17590/com.littlejerk.sample E/DpActivity: scaledDensity:2.75
2021-08-16 16:52:58.943 17590-17590/com.littlejerk.sample E/DpActivity: densityDpi:440
2021-08-16 16:52:58.944 17590-17590/com.littlejerk.sample E/DpActivity: floatDp:112.75
2021-08-16 16:52:58.944 17590-17590/com.littlejerk.sample E/DpActivity: pixelSizeDp:113
2021-08-16 16:52:58.944 17590-17590/com.littlejerk.sample E/DpActivity: pixelOffsetDp:112
2021-08-16 16:52:58.944 17590-17590/com.littlejerk.sample E/DpActivity: floatPx:41.0
2021-08-16 16:52:58.944 17590-17590/com.littlejerk.sample E/DpActivity: pixelSizePx:41
2021-08-16 16:52:58.944 17590-17590/com.littlejerk.sample E/DpActivity: pixelOffsetPx:41
2021-08-16 16:52:58.944 17590-17590/com.littlejerk.sample E/DpActivity: floatSp:112.75
2021-08-16 16:52:58.944 17590-17590/com.littlejerk.sample E/DpActivity: pixelSizeSp:113
2021-08-16 16:52:58.944 17590-17590/com.littlejerk.sample E/DpActivity: pixelOffsetSp:112
2021-08-16 16:52:58.944 17590-17590/com.littlejerk.sample E/DpActivity: sp2px:110
2021-08-16 16:52:58.944 17590-17590/com.littlejerk.sample E/DpActivity: dp2px:110
从打印结果就可以推知
- getDimension()、getDimensionPixelSize和getDimensionPixelOffset方法传入的参数单位不同,换算也不同;对于小数点的精确也不同。
- getDimension()方法返回的是float类型,不会对小数点进行额外的操作,getDimensionPixelSize()和getDimensionPixelOffset()方法返回的虽然都是int类型,但是前者会进行四舍五入操作,而后者会直接舍弃小数点后面的值。
- 三个方法传入参数的单位不同,它们的换算方式也不同。
- 对于dp单位的值,换算方式为density乘以该dp值;对于sp单位的值,换算方式为scaledDensity乘以该sp的值;而对于px来说,没做任何运算,直接等于该px值
其实要想验证这个问题也不难,可以直接去手机设置修改字体的大小,这样scaledDensity和density就不一样了。还有一个方法就是直接看这些方法里面的实现,这样就可以百分百确认我们的推论了,进入这三个方法就可知道,它们最后都调用了TypedValue的applyDimension方法,其实现如下:
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;
}
看到这就一目了然了吧,这完全说明上面的结论是对的。
还有个问题是为啥工具类换算出来的值相差了一点呢?其实这也是小数点和取整造成的,1px左右的差距倒影响不大,具体使用哪几个方法获取px,看自己喜好了。