文章目录

  • 一、概念
  • 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就是屏幕的像素屏幕密度。

android 消息屏障 安卓屏幕信息_Displaymetrics

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 项目下,这些表示不同屏幕密度(不同分辨率)用来修饰 Androiddrawable 文件夹以及 value 文件夹,用来区分不同像素密度下的图片和 dimen 值,所以在不同 drawable 文件夹下图片的 densityDpi 的值也是不同的 。这会导致用 BitmapFactorydecodeResource创建 Bitmap 时,其加载到内存的大小不一样。(详见 深入理解Android Bitmap的各种操作)
Google官方指定按照下列标准进行区分:

名称

像素密度范围

mdpi

120dpi ~ 160dpi

hdpi

160dpi ~ 240dpi

xhdpi

240dpi ~ 320dpi

xxhdpi

320dpi ~ 480dpi

xxxhdpi

480dpi ~ 640dpi

1.5 dp

dp 也可以写成dipdensity 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

android 消息屏障 安卓屏幕信息_android 消息屏障_02

二、从系统中获取相关参数和转换

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 中,densitydensityDpi 这个两个的线性关系:

density

1

1.5

2

3

3.5

4

densityDpi

160

240

320

480

560

640

2.2 TypedValue

TypedValueAndroid 提供单位转换的类,这个类是工具类,作为一个动态容器,它存放一些数据值,这些值主要是 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());

不积跬步无以至千里,不积小流无以成江海。让我们默默前行,不蹉跎岁月,不辜负自己。突然想到家园的歌词:冰雪早已覆盖我的足迹,远方的炊烟摇曳温暖的召唤,风儿无法吹断我回望的视线,家园好像永远征途漫漫。是啊,我们人生也是征途漫漫。