Px、Dp和Sp是什么?
其實它們就是個度量單位,
Px表示像素點, 1px = 1像素
Dp和Sp,都表示和屏幕密度無關,不過后者只適用于字體大小。
舉個例子,假設:
獲取layout的寬度,返回的int數據代表像素值:
int layout_width = contxt.getResource().getDimension(R.dimen.layout_width);
對應dimens.xml文件中的數據為:
<dimen name="layout_width">42dp</dimen>
跟著源碼來看是如何獲取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的資源打包,此處不深入了。有興趣的話,可以研究下老羅的這篇文章:
? 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,基本用不到,暫時不追蹤,等用到的時候再追。哈哈哈。