Android 终极屏幕适配方案

Android 屏幕适配问题的由来

我们都知道 Android 碎片化问题令人痛心疾首,而造成的屏幕差异正式碎片化的问题中心。
屏幕的尺寸从3英寸到10英寸,分辨率从320到1920应有尽有,这对我们ui适配问题造成很大的困难。对于屏幕碎片化问题,Android 官方推荐使用dp作为尺寸单位来适配ui,因此我们很有必要清楚px,dp,dpi,ppi,density这些概念。

定义 概念 转化
px 像素点,比如手机分辨率320 x 480表示宽有320像素,高有480像素 px = density * dp
ppi 像素密度,每英寸包含的像素数目,这是屏幕物理参数,例如mate 20 pro 的ppi是538 ppi约等于ddpi
dpi 像素密度,跟ppi不同的是,dpi可能被人为调整,例如几部相同分辨率不同尺寸的手机ppi是430,440,450,android会dpi指定为480 dpi 约等于ppi
dp density-independent pixels,基于屏幕物理分辨率一个抽象单位,用来说明与密度无关的尺寸 px = dp *(dpi / 160)
density 密度,屏幕每平方英寸还有的像素点数量 density = dpi/160)

一般来说,通过dp和自适应布局可以基本解决屏幕碎片化问题,这也是Android 推荐使用的屏幕兼容性方案,但是它也会存在两个比较大的问题:

  • 不一致性。因为dpi与ppi的差异实际的差异性,导致在相同分辨率的手机上,控件实际大小会有所不同。
  • 效率。设计师的设计稿都是由px为单位的,开发人员为了适配需要换算成dp。

除了dp适配之外,今天我要讲的是通过修改系统density来适配。

在编写xml文件时候,无论我们给控件的宽高用dp还是px,还是pt,最终android会把它转换成px显示。通过一下源码可以得知。

   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;
    }

所以可以发现当我们用dp时候,系统都会讲我们dp 值 乘以metrics.density换算的px值显示到手机上,sp的话则是乘以metrics.scaledDensity,默认情况下,metrics.scaledDensity = metrics.density。所以我们只要通过修改系统的metrics.density 就能达到适配的效果。
那么怎么修改呢?我们可以让ui出图时候定一个参考的宽度的值,比如width = 320dp;然后我们可以根据当前手机的displayMetrics.widthPixels / width算出density从而替换系统的density。话不多说上代码~

public class Density {

    private static final float  WIDTH = 320;//参考设备的宽

    private static float appDensity;//表示屏幕密度
    private static float appScaleDensity; //字体缩放比例,默认appDensity

    public static void setDensity(final Application application, Activity activity){
        //获取当前app的屏幕显示信息
        DisplayMetrics displayMetrics = application.getResources().getDisplayMetrics();
        if (appDensity == 0){
            //初始化赋值操作
            appDensity = displayMetrics.density;
            appScaleDensity = displayMetrics.scaledDensity;

        //计算目标值density, scaleDensity, densityDpi
        float targetDensity = displayMetrics.widthPixels / WIDTH; // 1080 / 360 = 3.0
        float targetScaleDensity = targetDensity * (appScaleDensity / appDensity);
        int targetDensityDpi = (int) (targetDensity * 160);

        //替换Activity的density, scaleDensity, densityDpi
        DisplayMetrics dm = activity.getResources().getDisplayMetrics();
        dm.density = targetDensity;
        dm.scaledDensity = targetScaleDensity;
        dm.densityDpi = targetDensityDpi;
    }

}

根据当前手机宽的像素值/参考的宽度值 算出目标density,然后替换掉系统的即可。
然后mainActivity 里调用Density.setDensity(getApplication(),this);

你可能感兴趣的:(Android 终极屏幕适配方案)