今天碰巧看见了这个函数,是将传进来的值根据不同选项转化为最底层的像素,然后传出。
那么不妨来自己来探索下安卓里px、dip(dp)、sp、pt、in、mm到底是什么吧
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;
}
介绍
网上关于px、dip(dp)、sp、pt、in、mm的介绍已经有很多了,这里我就不多阐述了,下面就简单贴下谷歌对这些关键字的说明
/** {@link #TYPE_DIMENSION} complex unit: Value is raw pixels. */
public static final int COMPLEX_UNIT_PX = 0;//原始像素
/** {@link #TYPE_DIMENSION} complex unit: Value is Device Independent Pixels. */
public static final int COMPLEX_UNIT_DIP = 1;//与设备无关的像素
/** {@link #TYPE_DIMENSION} complex unit: Value is a scaled pixel. */
public static final int COMPLEX_UNIT_SP = 2;//缩放的像素
/** {@link #TYPE_DIMENSION} complex unit: Value is in points. */
public static final int COMPLEX_UNIT_PT = 3;//点数
/** {@link #TYPE_DIMENSION} complex unit: Value is in inches. */
public static final int COMPLEX_UNIT_IN = 4;//英寸
/** {@link #TYPE_DIMENSION} complex unit: Value is in millimeters. */
public static final int COMPLEX_UNIT_MM = 5;//毫米
同时,这个函数里需要传进来一个DisplayMetrics对象,那么这个DisplayMetrics对象是什么么?
下面,还是来看下谷歌的大爷们对它的定义吧
/**
* A structure describing general information about a display, such as its
* size, density, and font scaling.
* <p>To access the DisplayMetrics members, initialize an object like this:</p>
* <pre> DisplayMetrics metrics = new DisplayMetrics();
* getWindowManager().getDefaultDisplay().getMetrics(metrics);</pre>
*/
public class DisplayMetrics {
大致意思是一个描述手机显示屏信息的一个实体类,那里面保存的都是写啥信息呢?我们拿手机的分辨率数据来同获取到的DisplayMetrics实例来进行对比一番
可以很清晰的看见DisplayMetrics对象里的高度和宽度像素值就等于手机屏幕的真实尺寸
那么回到上面的转换函数,里面用了DisplayMetrics实例的三个值:density、scaledDensity、xdpi
这三个又是啥子呢?
相互之间的转换
老样子,看下谷歌大爷们是怎么定义这三个值得,以及值是怎么来的
density、scaledDensity、xdpi的定义
density
/**
* The logical density of the display. This is a scaling factor for the
* Density Independent Pixel unit, where one DIP is one pixel on an
* approximately 160 dpi screen (for example a 240x320, 1.5"x2" screen),
* providing the baseline of the system's display. Thus on a 160dpi screen
* this density value will be 1; on a 120 dpi screen it would be .75; etc.
*
* <p>This value does not exactly follow the real screen size (as given by
* {@link #xdpi} and {@link #ydpi}, but rather is used to scale the size of
* the overall UI in steps based on gross changes in the display dpi. For
* example, a 240x320 screen will have a density of 1 even if its width is
* 1.8", 1.3", etc. However, if the screen resolution is increased to
* 320x480 but the screen size remained 1.5"x2" then the density would be
* increased (probably to 1.5).
*
* @see #DENSITY_DEFAULT
*/
public float density;
大致意思呢就是,这个值是一个缩放比例,在160dpi的屏幕上等于1,在120pdi的屏幕上就等于0.75,当然,这个值是谷歌定的,见下表
ldpi | 120dpi | 0.75 |
mdpi | 160dpi | 1 |
hdpi | 240dpi | 1.5 |
xhpi | 320dpi | 2 |
那么dpi又是什么呢?
dpi也是一种单位,代表一英寸(等于2.54厘米)里有多少像素点,如上面的举例240X320分辨率是1.5X2尺寸的屏幕,所以160dpi=240/1.5=320/2=X或Y轴的像素值/屏幕比
同时下面的注释也提到了xdpi和ydpi就是X轴和Y轴的dpi,当然,这两个的值都是一样的,对于上面的例子来讲都是160dpi
那么不妨来看下这次测试的红米note2的数据吧,从上图可知,数据为:
xdpi=ydpi=403(有些许小数点后的差异,暂时未深入了解为什么),那么我们用分辨率除以它们得到的scren比例值是:(1920/403):(1080/430)=4.764:2.680,即
X轴为4.764英寸,Y轴为2.680英寸,而红米note2某宝上是5.5英寸,而5.5≈根号下(X轴英寸的平方+Y轴英寸的平方),那么你应该想到了什么5.5英寸指的是什么
好吧,回到正题,看下一个变量
scaledDensity
/**
* A scaling factor for fonts displayed on the display. This is the same
* as {@link #density}, except that it may be adjusted in smaller
* increments at runtime based on a user preference for the font size.
*/
public float scaledDensity;
也是一个缩放系数,和density有关,能更好的调节用户对字体大小的偏好
xdpi
同上
变量的值是怎么来的?
好吧,一开始我们就是获取了一个DisplayMetrics对象然后使用里面的值,但是里面的值是怎么赋值的呢?
下面是流程:
Context c = getContext();
r.getDisplayMetrics()
那么我们进r.getDisplayMetrics()里面去看
public DisplayMetrics getDisplayMetrics() {
return mResourcesImpl.getDisplayMetrics();
}
原来是用另一个对象的函数啊,那再进去看看
DisplayMetrics getDisplayMetrics() {
if (DEBUG_CONFIG) Slog.v(TAG, "Returning DisplayMetrics: " + mMetrics.widthPixels
+ "x" + mMetrics.heightPixels + " " + mMetrics.density);
return mMetrics;
}
返回的是一个当前类里面的变量值,让我们找找我们看看这个mMetrics是在哪里初始化的
public ResourcesImpl(@NonNull AssetManager assets, @Nullable DisplayMetrics metrics,
@Nullable Configuration config, @NonNull DisplayAdjustments displayAdjustments){}
哦,是在构造函数里,那看来我们的返回上一个类里面,看看mResourcesImpl这个对象是在哪里被初始化
private Resources() {
this(null);
final DisplayMetrics metrics = new DisplayMetrics();
metrics.setToDefaults();
final Configuration config = new Configuration();
config.setToDefaults();
mResourcesImpl = new ResourcesImpl(AssetManager.getSystem(), metrics, config,
new DisplayAdjustments());
}
找到了,在这里,这里new了一个实例,并调用了setToDefaults()方法,那我们看看DisplayMetrics类的构造方法和setToDefaults()方法里有没有对类里面的变量赋值的操作吧
public DisplayMetrics() {
}
public void setToDefaults() {
widthPixels = 0;
heightPixels = 0;
density = DENSITY_DEVICE / (float) DENSITY_DEFAULT; //看这行
densityDpi = DENSITY_DEVICE;
scaledDensity = density; //看这行
xdpi = DENSITY_DEVICE; //看这行
ydpi = DENSITY_DEVICE;
noncompatWidthPixels = widthPixels;
noncompatHeightPixels = heightPixels;
noncompatDensity = density;
noncompatDensityDpi = densityDpi;
noncompatScaledDensity = scaledDensity;
noncompatXdpi = xdpi;
noncompatYdpi = ydpi;
}
哇,看到了,是在setToDefaults()方法方法里进行了赋值操作
我们可以看到我们需要的density、scaledDensity、xdpi和DENSITY_DEVICE 、DENSITY_DEFAULT有关系
DENSITY_DEVICE、DENSITY_DEFAULT是什么
/**
* The device's current density.
* <p>
* This value reflects any changes made to the device density. To obtain
* the device's stable density, use {@link #DENSITY_DEVICE_STABLE}.
*
* @hide This value should not be used.
* @deprecated Use {@link #DENSITY_DEVICE_STABLE} to obtain the stable
* device density or {@link #densityDpi} to obtain the current
* density for a specific display.
*/
@Deprecated
public static int DENSITY_DEVICE = getDeviceDensity();
private static int getDeviceDensity() {
// qemu.sf.lcd_density can be used to override ro.sf.lcd_density
// when running in the emulator, allowing for dynamic configurations.
// The reason for this is that ro.sf.lcd_density is write-once and is
// set by the init process when it parses build.prop before anything else.
return SystemProperties.getInt("qemu.sf.lcd_density",
SystemProperties.getInt("ro.sf.lcd_density", DENSITY_DEFAULT));
}
DENSITY_DEVICE是当前设配的密度,该值由getDeviceDensity()方法读取手机上的配置文件得来
public static final int DENSITY_DEFAULT = DENSITY_MEDIUM;
而DENSITY_MEDIUM则是
public static final int DENSITY_MEDIUM = 160;
看到这里我想你应该知道了,为什么160dpi下的density=1了(此处应有表情包,可惜太麻烦了,懒得扔上来)
好吧,到这里就结束了
哎,你还没说怎么变换?上面不是有公式么,自己换算去