Px、Dp和Sp是什么?
其实它们就是个度量单位,
Px表示像素点, 1px = 1像素
Dp和Sp,都表示和屏幕密度无关,不过后者只适用于字体大小。
举个例子,假设:
获取layout的宽度,返回的int数据代表像素值:
int layout_width = contxt.getResource().getDimension(R.dimen.layout_width);
对应dimens.xml文件中的数据为:
42dp
跟着源码来看是如何获取dp对应的像素值:
/**
* Retrieve a dimensional for a particular resource ID. Unit
* conversions are based on the current {@link DisplayMetrics} associated
* with the resources.
*
* @param id The desired resource identifier, as generated by the aapt
* tool. This integer encodes the package, type, and resource
* entry. The value 0 is an invalid identifier.
*
* @return Resource dimension value multiplied by the appropriate
* metric.
*
* @throws NotFoundException Throws NotFoundException if the given ID does not exist.
*
* @see #getDimensionPixelOffset
* @see #getDimensionPixelSize
*/
public float getDimension(int id) throws NotFoundException {
synchronized (mAccessLock) {
TypedValue value = mTmpValue;
if (value == null) {
mTmpValue = value = new TypedValue();
}
getValue(id, value, true);
if (value.type == TypedValue.TYPE_DIMENSION) {
return TypedValue.complexToDimension(value.data, mMetrics);
}
throw new NotFoundException(
"Resource ID #0x" + Integer.toHexString(id) + " type #0x"
+ Integer.toHexString(value.type) + " is not valid");
}
}
根据英文注释,这个方法,根据id值,返回对应的dimens数据。其中TypeValue是资源数据容器,主要用于Resource类中,主要成员变量有type、string、data、density等等。
在getValue方法中,由id值来初始化value,这是个jni方法,具体涉及到aapt的资源打包,此处不深入了。有兴趣的话,可以研究下老罗的这篇文章:
Android应用程序资源的编译和打包过程分析
getValue完事之后,对value的type进行判断,只有Dimension类型的数据才能调用下一步的方法,毕竟也不能保证误操作getDimension()方法中会不会传进一个字符id。
complexToDimension方法最后会调用到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;
}
其中,DisplayMetrics也是一个重要的数据容器,记录了重要的显示数据,比如屏幕宽度、高度、密度等。unit数据是从先前的TypeValue的data数据中获取,对了,再重申一遍,我们要返回的是像素值:
1.如果unit是PX,直接返回数值;
2.如果是DP,则将数值乘以屏幕像素密度。dp也叫dip(density independent pixel)直译为密度无关的像素。我们猜测如果使用了这个单位,在不同屏幕密度的设备上显示的长度就会是相同的。1dp在mdpi设备下等于1px,在hdpi设备下等于1.5px,同样的在xxhdpi设备下1dp=3px。因此推断出,metrics.density = px值/dp值。
不过设备的密度值是死的,它是一个比例因子,density = 像素值/160, mdpi的像素值为160dpi,因此它的density = 1,所以在mdpi下1dp = 1 px。
因此上述例子中,在mdpi下最终获取的int layout_width = 42,hdpi下是63,以此类推。
3.如果是SP,这个单位和字体大小有关,scaledDensity是系统字体比例因子,在魅族手机的简易模式中,该数值大于1,因此所有应用的字体都看起来很大。
如果你不希望字体大小跟着scaledDensity走怎么办呢?
可以试着setTextSize传入:
TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX,size, metrics),
unit改为PX就好了,跟着像素走,缺点也是显而易见的,那就是不同屏幕密度,大小也会不一样,只能算一个规避简易模式这个大坑的不得已的处理手段。
剩下的几个单位,PT/IN/ MM,基本用不到,暂时不追踪,等用到的时候再追。哈哈哈。