文章目录
- 一、概念
- 1.1 屏幕大小
- 1.2 屏幕分辨率
- 1.3 屏幕像素密度
- 1.4 实际密度和系统密度
- 1.5 dp
- 二、从系统中获取相关参数和转换
- 2.1 DisplayMetrics
- 2.2 TypedValue
经常被 Android 设备纷繁的屏幕搞得是晕头转向,下面就梳理一下 Android 中的相关概念。
一、概念
1.1 屏幕大小
屏幕大小是手机对角线的物理尺寸,单位是:英寸(inch),1 inch =2.54 cm(厘米) 。例如现在手机大多都是 “6.0寸” 以上的屏幕,这个就是对角线的尺寸,6.0(inch) X 2.54(cm)/ 1 (inch)= 15.24(cm)
1.2 屏幕分辨率
分辨率就是手机屏幕的像素点数,一般描述为:纵像素 * 横像素,如 2240 * 1080,就是在纵方向上有2240个像素点,在横方向上有1080个像素点。
1.3 屏幕像素密度
屏幕像素密度(dpi,dots per inch;或 PPI ,pixels per inch),就是每英寸的像素点,数值越高显示越细腻。假如一部手机的分辨率是 1080*1920 ,屏幕大小是5英寸,那么此屏幕的密度是多少呢?通过宽1080和高1920,根据勾股定理,得出对角线的像素大约是2203,那么用2203/5=440,440dpi就是屏幕的像素屏幕密度。
1.4 实际密度和系统密度
这里其实将的还是上面的 屏幕像素密度 ,不过是换了一种说法。
实际密度:就是我们自己算出来的密度,这个密度代表了屏幕真实的细腻程度,如上述例子中的440dpi 就是实际密度,说明这块屏幕每寸有440个像素。5英寸1080*1920 的屏幕密度是440,而相同分辨率的4.5英寸屏幕是490。如此看来,屏幕密度会出现很多数值,呈现严重的碎片化。而密度有时 Android 屏幕将界面进行显示的依据,那么 Android 是如何适配这么多屏幕的呢?其实,每部手机屏幕都有一个初始固定密度,这些数值是120、160、240、320、480,我们权且称为“系统密度”。大家发现规律没有?相隔数值之间是2倍的关系。一般情况下,240*320的屏幕是低密度120dpi,即 ldpi ;320*480的屏幕是中密度160dpi,即 mdpi ;480*800的屏幕是高密度240dpi,即 hdpi ;720*1280的屏幕是超高密度320dpi,即xhdpi ;1080*1920的屏幕密度是超超高密度480dpi,即 xxhdpi;
在具体的 Android 项目下,这些表示不同屏幕密度(不同分辨率)用来修饰 Android 中 drawable 文件夹以及 value 文件夹,用来区分不同像素密度下的图片和 dimen 值,所以在不同 drawable 文件夹下图片的 densityDpi 的值也是不同的 。这会导致用 BitmapFactory 的 decodeResource创建 Bitmap 时,其加载到内存的大小不一样。(详见 深入理解Android Bitmap的各种操作)
Google官方指定按照下列标准进行区分:
名称 | 像素密度范围 |
mdpi | 120dpi ~ 160dpi |
hdpi | 160dpi ~ 240dpi |
xhdpi | 240dpi ~ 320dpi |
xxhdpi | 320dpi ~ 480dpi |
xxxhdpi | 480dpi ~ 640dpi |
1.5 dp
dp 也可以写成dip(density independent pixel),即密度无关的像素。比如,一张宽和高均为100dp的图片在 320 *480 和480*800的手机上,“看起来”是一样大,实际上,它们的像素值并不一样。dp正是这样一个尺寸,不管这个屏幕的密度是多少,屏幕上相同dp的大小的元素看起来始终差不多大。另外,文字尺寸使用 sp,即 scale independent pixel 的缩写,与 dp 类似,是设置字体大小的御用单位。在 Android 中,系统密度为160dpi的中密度手机屏幕为基准屏幕,即320*480的手机屏幕。在这个屏幕中:1dp = 1px。
100dp在320*480(mdpi,160dpi)中是100px。那么100dp在480*800(hdpi,240dpi)的手机上,根据160与240的比例关系,100dp实际覆盖了150px。因此,如果你为mdpi手机上提供了一张100px的图片,这张图片在hdpi手机上就会拉伸至150px,但是他们都是100dp。要注意一点的就是:dp和px的换算要以系统密度为准,720*1280的系统密度为320,320*480的系统密度为160,320/160=2,那么在720*1280中,1dp = 2px。同理,在1080*1920中,1dp = 3px。有一个dp和px换算的比例公式:ldpi : mdpi :hdpi:xdpi:xxdpi = 3:4:6:8:12,我们发现相隔数字之间还是2倍的关系。计算的时候,以 mdpi 为基准。如果以 mdpi 为基准比例为:
dpi : mdpi :hdpi:xdpi:xxdpi = 0.75:1:1.5:2:3
二、从系统中获取相关参数和转换
2.1 DisplayMetrics
这个类是 Android 提供的记述屏幕有关信息的一种类,诸如其尺寸、屏幕像素密度等。
获取这个对象的方法:
//第一种
DisplayMetrics metrics = new DisplayMetrics();
activity.getWindowManager().getDefaultDisplay().getMetrics(metrics);
//第二种
DisplayMetrics metrics=activity.getResources().getDisplayMetrics();
//第三种
Resources.getSystem().getDisplayMetrics();
这个类中有几个比较重要的属性:
/**
* The absolute width of the available display size in pixels.
* 设备的绝对宽度,单位是px
*/
public int widthPixels;
/**
* The absolute height of the available display size in pixels.
* 设备的绝对高度,单位是px
*/
public int heightPixels;
/**
* 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
*简单来说,可以理解为 density 的数值是 1dp = density px 即 density = dpi/160,也就是在我们介绍从低到高系统密度的比值:
* 0.75 : 1 : 1.5 : 2 : 3
*/
public float density;
/**
* The screen density expressed as dots-per-inch. May be either
* {@link #DENSITY_LOW}, {@link #DENSITY_MEDIUM}, or {@link #DENSITY_HIGH}.
* 就是上面的:屏幕像素密度(dpi)
*/
public int densityDpi;
/**
* 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.
* 字体显示的缩放因子,跟上面的 density 是一样的
*/
public float scaledDensity;
/**
* The exact physical pixels per inch of the screen in the X dimension.
* 水平方向上的dpi
*/
public float xdpi;
/**
* The exact physical pixels per inch of the screen in the Y dimension.
* 竖直方向上的dpi
*/
public float ydpi;
在 DisplayMetrics 中,density 和 densityDpi 这个两个的线性关系:
density | 1 | 1.5 | 2 | 3 | 3.5 | 4 |
densityDpi | 160 | 240 | 320 | 480 | 560 | 640 |
2.2 TypedValue
TypedValue 是 Android 提供单位转换的类,这个类是工具类,作为一个动态容器,它存放一些数据值,这些值主要是 resource 中的值。TypedValue 类把对应的值转化为实际屏幕上的点值,也就是像素值,总体来说作用:把 Android 系统中的非标准度量尺寸转变为标准度量尺寸(Android 系统中的标准尺寸是 px,即像素)。非标准单位:dp 、in、mm、pt、sp
典型应用为:
/**
* Converts an unpacked complex data value holding a dimension to its final floating
* point value. The two parameters <var>unit</var> and <var>value</var>
* are as in {@link #TYPE_DIMENSION}.
*
* @param unit The unit to convert from.
* @param value The value to apply the unit to.
* @param metrics Current display metrics to use in the conversion --
* supplies display density and scaling information.
*
* @return The complex floating point value multiplied by the appropriate
* metrics depending on its unit.
*/
TypedValue.applyDimension(int unit, float value, DisplayMetrics metrics)
其中,第一个参数 unit 是你需要转换的单位,第二个参数 value 是你需要得到的单位数值,第三个参数 metrics 封装了屏幕区域各种属性。
例如:我要得到一个 20dp 在当前设备上对应的 px 值:
float px_value = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,20,context.getResources().getDisplayMetrics());
还有其他类型:
TypedValue.applyDimension()方法的功能就是把非标准尺寸转换成标准尺寸:
dp->px: TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 20, context.getResources().getDisplayMetrics());
in->px: TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_IN, 20, context.getResources().getDisplayMetrics());
mm->px: TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_MM, 20, context.getResources().getDisplayMetrics());
pt->px: TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PT, 20, context.getResources().getDisplayMetrics());
sp->px: TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 20, context.getResources().getDisplayMetrics());
不积跬步无以至千里,不积小流无以成江海。让我们默默前行,不蹉跎岁月,不辜负自己。突然想到家园的歌词:冰雪早已覆盖我的足迹,远方的炊烟摇曳温暖的召唤,风儿无法吹断我回望的视线,家园好像永远征途漫漫。是啊,我们人生也是征途漫漫。