Android屏幕适配总结

前言

很久以前就想写一篇跟屏幕适配相关的文章一直没有动笔,此次下决心抽周末的时间结合我在实际项目中所遇到的问题写一篇博客。html

Android屏幕组成

Android手机屏幕是由不少的像素点(pixels)组成的,从左到右称为x轴,从上到下为y轴。屏幕的分辨率就是屏幕上x轴上的像素点格式乘以y轴上的像素点个数。320*480的屏幕就是有320个像素点在x轴上,有480个像素点在y轴上。以下图所示:java

在这里插入图片描述

Android屏幕大小

什么是屏幕的大小? 对于手机,平板,电脑,或者电视,一般用屏幕的对角线的长度单位为英尺(inches,1 英尺=2.54厘米)来表示屏幕的大小。 若是知道屏幕的宽度和高度就能够计算出屏幕的大小。以下图所示: android

在这里插入图片描述
屏幕的宽为1.8,高度为2.6能够计算出屏幕的大小为3.2。

屏幕密度(Screen density)

屏幕密度指的是单位面积上的像素点的个数。像素点(pixel)屏幕上最小的显示区域,不一样分辨率的手机上像素点的大小不同,一样尺寸的屏幕像素点越大屏幕上的像素点总数越少,分辨率越低,像素点越小,屏幕上像素点个数越多,分辨率越高。以下图所示: bash

在这里插入图片描述
一样大小的屏幕上一样大小的a,左边的像素点大,因此a所包含的像素点就少,分辨率就低,右边像素点小,a包含的像素点数量多因此分辨率更高。下面的图更加清晰的表现了像素点大小和分辨率的关系。
在这里插入图片描述
在面积1inch*1inch的屏幕上像素点越大像素点的总数越少,所以分辨率越低。在Android中用dpi来表示像素点的密度,单位面积上像素点越多屏幕分辩率越高因此dpi称为了衡量一个屏幕好坏的标准之一。

DPI

DPI(Dots Per Inch),每一英尺上像素点的个数,dpi是用于衡量屏幕分辨率的尺度,dpi越大屏幕分辨率越高,DPI计算公式:app

在这里插入图片描述
其中x是屏幕x轴的像素点数,y是屏幕y轴的像素点数单位是pixels,s是屏幕的大小单位是pixels,s的计算在屏幕大小里面已经给出了计算方法:
在这里插入图片描述
其中a,b分别指屏幕物理上的宽和高,单位是inch(英尺),下面看下dpi计算的实例:

240x320, 1.5"x2" 
复制代码

上面是一个240320也就是说x轴像素点240pixels,y轴像素点320pixels,屏幕尺寸1.52,物理尺寸长2英尺,宽1.5英尺。 先计算屏幕的大小 ide

在这里插入图片描述
再由上面的dpi公式计算dpi
在这里插入图片描述
上面计算获得的值是:160dpi,也就是说

240x320, 1.5"x2" 
复制代码

的dpi是160.wordpress

基本单位

一、像素(px)布局

含义:一般所说的像素,就是CCD/CMOS上光电感应元件的数量,一个感光元件通过感光,光电信号转换,A/D转换等步骤之后,在输出的照片上就造成一个点,咱们若是把影像放大数倍,会发现这些连续色调实际上是由许多色彩相近的小方点所组成,这些小方点就是构成影像的最小单位“像素”(Pixel)。简而言之,像素就是手机屏幕的最小构成单元。 单位:px(pixel),1px = 1像素点 通常状况下UI设计师的设计图会以px做为统一的计量单位。字体

二、分辨率ui

含义:手机在横向、纵向上的像素点数总和 通常描述成 宽*高 ,即横向像素点个数 * 纵向像素点个数(如1080 x 1920)。 单位:px(pixel),1px = 1像素点

三、屏幕尺寸(in)

含义:手机对角线的物理尺寸 单位 英寸(inch),一英寸大约2.54cm 常见的尺寸有4.7寸、5寸、5.5寸、6寸

四、屏幕像素密度(dpi)

含义:每英寸的像素点数。 例如每英寸内有160个像素点,则其像素密度为160dpi。 单位:dpi(dots per inch) 计算公式: 像素密度 = 像素 / 尺寸 (dpi = px / in) 标准屏幕像素密度(mdpi): 每英寸长度上还有160个像素点(160dpi),即称为标准屏幕像素密度(mdpi)。

dp与px的换算方法

px=density*dp 所以dp与px的换算方法以下:

px = dp * (dpi / 160)
复制代码

分辨率和屏幕密度以及像素大小之间的关系以下图:

在这里插入图片描述

在Android中直接使用px做单位的问题

若是在布局中直接使用px来作单位会有什么问题,以下图,在图中a的宽度为2px,高度为2px,在左图中因为像素点比右图像素点大,所以做图中的a明显比右图中的a大。

在这里插入图片描述
要使在不一样分辨率的屏幕上显示图片的大小同样,那么确定不能用px来作单位,若是使用dp来作单位呢,左右图都是2dp*2dp,此时左右图大小相等,由于1dp在不一样的屏幕上大小相同,而1px在不一样的屏幕上大小不一样。

下面来看下在Android中为何要在不一样的分辨率的目录下放大小不一样的图:好比在drawable,drawable-hdpi,drawable-xhdpi,drawable-xxhdpi,drawable-xxxhdpi目录下图片大小不同:

在这里插入图片描述
为何在MDPI中图像的大小是XXHDPI中图像大小的1/4呢?原理是这样的,在160dpi中1dp=1px1,而在640dpi中1dp=4px2,也就是说1px1=4px2,也就是说160dpi中一个像素大小是640dpi中一个像素大小的4倍,因此MDPI中的图像看起来比XXXHDPI的图像小4倍,可是因为MDPI中每一个像素比XXXHDPI大4倍,因此显示在屏幕上后大小是同样的。

注意在Android中全部的尺寸单位最后都是转化为px后再显示的,由于屏幕显示的基本单位就是px

源码中dp转换px的公式

private float dipToPx(float dip) {
        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip,
                getResources().getDisplayMetrics());
    }
复制代码
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和dp的转换与metrics.density相关,下面看一下源码里面对metrics.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 */
复制代码

上面的总结一下就是:标准状况下240x320, 1.5"x2"的屏幕上density=1,就是说物理尺寸时1.52英寸的状况下,可是当240320屏幕物理尺寸不是1.52的时候此时density仍是等于1。由dpi和density的计算公式可知这时的density是有问题的。由于此时240320因为物理尺寸不是1.5*2,算出来的dpi不等于160。

public static final int DENSITY_DEFAULT = 160;
density =  DENSITY_DEVICE / (float) DENSITY_DEFAULT;
 
复制代码

若是density计算有问题那么dp转换为px就会有问题,因此在有些手机上有时就会出现很奇怪的适配问题,好比字体显示偏小,布局偏小等等,解决这个问题可使用下面方法:即提供一个计算density的方法不用系统的density计算致使的问题。

public class ScreenUtils {
    //Negotiate with the designer to define a design dimension. Here is 1920*1080 resolution set.
    private static final float widthdp = 360f;
    private static final float heightdp = 640f;

    //Recording system settings
    private static float systemDensity = 0;
    private static float systemScaledDensity = 0;


    public void setCustomDensity(@NonNull final Activity activity) {
        DisplayMetrics displayMetrics = activity.getApplication().getResources().getDisplayMetrics();
        if (systemDensity == 0) {
            //Initialization
            systemDensity = displayMetrics.density;
            systemScaledDensity = displayMetrics.scaledDensity;
            //Add a listener. If the user changes the font of the system, the system will return the listener.
            activity.getApplication().registerComponentCallbacks(new ComponentCallbacks() {
                @Override
                public void onConfigurationChanged(Configuration newConfig) {
                    if (newConfig != null && newConfig.fontScale > 0) {
                        systemScaledDensity = activity.getApplication().getResources().getDisplayMetrics().scaledDensity;
                    }
                }

                @Override
                public void onLowMemory() {

                }
            });
        }
        //Target value calculation => PX = DP * density
        final float targetDensity = displayMetrics.widthPixels / widthdp;
        final float targetScaledDensity = targetDensity * (systemScaledDensity / systemDensity);
        final int targetDensityDpi = (int) (160 * targetDensity);
        //Set the calculated value
        displayMetrics.density=targetDensity;
        displayMetrics.scaledDensity=targetScaledDensity;
        displayMetrics.densityDpi=targetDensityDpi;
        //Set the value of activity
        final DisplayMetrics activityDisplayMetrics =activity .getResources().getDisplayMetrics();
        activityDisplayMetrics.density = targetDensity;
        activityDisplayMetrics.scaledDensity = targetScaledDensity;
        activityDisplayMetrics.densityDpi = targetDensityDpi;
    }

}
复制代码

参考文献

一、 stackoverflow.com/questions/2…

二、 developer.android.com/guide/pract…

三、 developer.android.com/training/mu…

四、 developer.android.com/guide/pract…

五、 laaptu.wordpress.com/tag/android…

六、 www.codexiu.cn/android/blo…

七、 stackoverflow.com/questions/2…

八、tekeye.uk/android/and…

九、programmer.help/blogs/shari…

相关文章
相关标签/搜索