转载地址:http://blog.csdn.net/dai_zhenliang/article/details/38387971
在dimens.xml定义如下变量:
Android代码如下:
在各种屏幕密度不一样的手机上运行结果如下:
数值为16,测试各种单位、各种获取方法取得的数值
屏幕densityDpi:160
屏幕scale:1.0
屏幕fontScale:1.0
getDimension(dp) = 16.0
getDimensionPixelOffset(dp) = 16
getDimensionPixelSize(dp) = 16
getDimension(sp) = 16.0
getDimensionPixelOffset(sp) = 16
getDimensionPixelSize(sp) = 16
---------------------------------------------------
屏幕densityDpi:240
屏幕scale:1.5
屏幕fontScale:1.5
getDimension(dp) = 24.0
getDimensionPixelOffset(dp) = 24
getDimensionPixelSize(dp) = 24
getDimension(sp) = 24.0
getDimensionPixelOffset(sp) = 24
getDimensionPixelSize(sp) = 24
---------------------------------------------------
屏幕densityDpi:320
屏幕scale:2.0
屏幕fontScale:2.0
getDimension(dp) = 32.0
getDimensionPixelOffset(dp) = 32
getDimensionPixelSize(dp) = 32
getDimension(sp) = 32.0
getDimensionPixelOffset(sp) = 32
getDimensionPixelSize(sp) = 32
---------------------------------------------------
屏幕densityDpi:480
屏幕scale:3.0
屏幕fontScale:3.0
getDimension(dp) = 48.0
getDimensionPixelOffset(dp) = 48
getDimensionPixelSize(dp) = 48
getDimension(sp) = 48.0
getDimensionPixelOffset(sp) = 48
getDimensionPixelSize(sp) = 48
结论:getDimension()、getDimensionPixelOffset()、getDimensionPixelSize()这3个方法
相同点:1、它们都是把dimens.xml中的数值乘以屏幕scale。
2、dimens.xml中的单位写dp或sp感觉上没有区别,但如果你是用在设置字体大小的话还是建议用sp单位的。
3、这3个方法返回的值都是px,也就是说它们把dp或sp转换为了px。
不同点:getDimension()返回的是float,精度更高,另外两个方法返回的是int,应该会四舍五入,那问题就来了,这两个返回int的方法有什么区别呢?试验如下:
把dimens.xml中的数值改为:3.5,运行结果如下:
数值为3.5,测试各种单位、各种获取方法取得的数值
屏幕densityDpi:160
屏幕scale:1.0 3.5 x1.0 = 3.5
屏幕fontScale1.0
getDimension(dp) = 3.5
getDimensionPixelOffset(dp) = 3 直接删除小数
getDimensionPixelSize(dp) = 4 四舍五入
getDimension(sp) = 3.5
getDimensionPixelOffset(sp) = 3
getDimensionPixelSize(sp) = 4
-----------------------------------------------------------------------------------
屏幕densityDpi:240
屏幕scale:1.5 3.5 x 1.5 = 5.25
屏幕fontScale1.5
getDimension(dp) = 5.25
getDimensionPixelOffset(dp) = 5 直接删除小数
getDimensionPixelSize(dp) = 5 四舍五入
getDimension(sp) = 5.25
getDimensionPixelOffset(sp) = 5
getDimensionPixelSize(sp) = 5
-----------------------------------------------------------------------------------
屏幕densityDpi:320
屏幕scale:2.0 3.5 x 2.0 = 7.0
屏幕fontScale2.0
getDimension(dp) = 7.0
getDimensionPixelOffset(dp) = 7 直接删除小数
getDimensionPixelSize(dp) = 7 四舍五入
getDimension(sp) = 7.0
getDimensionPixelOffset(sp) = 7
getDimensionPixelSize(sp) = 7
-----------------------------------------------------------------------------------
屏幕densityDpi:480
屏幕scale:3.0
屏幕fontScale3.0
getDimension(dp) = 10.5
getDimensionPixelOffset(dp) = 10 直接删除小数
getDimensionPixelSize(dp) = 11 四舍五入
getDimension(sp) = 10.5
getDimensionPixelOffset(sp) = 10
getDimensionPixelSize(sp) = 11
通过上面的试验,结论是:getDimensionPixelOffset()是直接把小数删除,而getDimensionPixelSize()则会进行四舍五入。使用场景:如果是设置调用setTextSize(float)方法,则使用getDimension()方法即可,这个是精度最准的,而如果你要使用这些值的地方只接受int类型,则可以使用getDimensionPixelOffset()或getDimensionPixelSize(),根据你是需要直接删除小数,还是要四舍五入来选择一个即可。
注:这里有一个错误:“调用setTextSize(float)方法,则使用getDimension()方法”,这是错误的,因为setTextSize(float)方法默认单位就是sp,内部会自动乘以scale来转换为px(像素),所以当在代码中设置setTextSize(float)时,直接把数值写在这个方法参数里即可,如果调用getDimension的话,则这个size值会乘两次scale,因此得到的值就是变大了。最后结论就是getDimension()、getDimensionPixelOffset()、getDimensionPixelSize()这3个方法只适用于获取dimens.xml中定义的dp的值转换为等价的px值(像素值),对于sp的值这3个方法无用武之地。
android并没有在java代码中直接获取xml中定义的dp\sp的值的API,只有getDimension()、getDimensionPixelOffset()和getDimensionPixelSize()三个函数获取绝对尺寸,关于这三个函数的区别可以参见我的另一个帖子getDimension()、getDimensionPixelOffset()和getDimensionPixelSize()区别详解 。
为什么不提供获取dp/sp值的函数?可能是因为google认为没有必要,API里的setWidth、setHeight等函数输入参数都是像素值。但如果实在想得到xml中咱们自己写的dp或sp的值(例如想在日志里输出dp\sp什么的),有两种方法可以实现:
第一种方法就是将像素值转化为dp\sp,这类代码网上已经满天飞了,这里为了阅读本帖的朋友们方便,我还是把这个代码贴出来(请见本帖结尾)
上述方法本身没什么问题,但如果对于代码洁癖或“吹毛求疵”的人来说,有个烦人的地方是将像素转化为sp\dp都是不同的函数接口,例如px2dip(),px2sp(),其实android还支持英寸、毫米和磅,但这些都不常用,但如果做全了,还应该有px2in(), px2mm(), px2pt()这些函数,那么我在代码中想获取我在xml中定义的尺寸时,还需要在这么多个函数里面选,好麻烦啊(程序员都是懒惰的),有没有简单直接的调用方法呢? 答案是有的,这就是本文阐述的方法:
其实在研究getDimension()、getDimensionPixelOffset()和getDimensionPixelSize()这三个函数源码时,会发现这三个函数的实现都是先获取在xml中定义的值,然后再根据是dp/sp/in/mm/pt转化成相应的像素值,所以,我们只需要把android的部分代码抽出来即可,封装成一个小工具类如下:
[mw_shl_code=java,true]public class ResourceUtils {
private static TypedValue mTmpValue = new TypedValue();
private ResourceUtils(){}
public static int getXmlDef(Context context, int id){
synchronized (mTmpValue) {
TypedValue value = mTmpValue;
context.getResources().getValue(id, value, true);
return (int)TypedValue.complexToFloat(value.data);
}
}
}[/mw_shl_code]
然后在代码里我们就可以直接这么写了
[mw_shl_code=java,true]ResourceUtils.getXmlDef(context, R.dimen.test_dp)[/mw_shl_code]搞定,事情变的简单了~ 根据id不同,如果id对应的是dp的,那函数返回的就是dp值,如果id对应的是sp的,那函数返回的就是sp值,一个函数入口就行了~
这里有个android的小的优化点值得学习,就是mTmpValue临时变量的设计。为了不在每一个getResources().getXXX()函数里都new TypedValue(),使用一个mTmpValue临时变量进行缓存重复使用,并加上同步保护,减少了内存占用,可见google为了优化内存的用心~
顺带贴上第一种方法的代码,其实这段代码网上满天飞了
[mw_shl_code=java,true]public class DisplayUtil {
private DisplayUtil(){}
/**
* 将px值转换为dip或dp值,保证尺寸大小不变
* @param context
* @param pxValue
* @return
*/
public static int px2dip(Context context, float pxValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (pxValue / scale + 0.5f);
}
/**
* 将dip或dp值转换为px值,保证尺寸大小不变
* @param dipValue
* @param scale
* @return
*/
public static int dip2px(Context context, float dipValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dipValue * scale + 0.5f);
}
/**
* 将px值转换为sp值,保证文字大小不变
* @param pxValue
* @param fontScale
* @return
*/
public static int px2sp(Context context, float pxValue) {
final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
return (int) (pxValue / fontScale + 0.5f);
}
/**
* 将px值转换为sp值,保证文字大小不变
* @param spValue
* @param fontScale
* @return
*/
public static int sp2px(Context context, float spValue) {
final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
return (int) (spValue * fontScale + 0.5f);
}
}