Android UI适配相关知识

版权声明: https://blog.csdn.net/ZHENZHEN9310/article/details/86476897

在Android开发中,由于Android碎片化严重,屏幕分辨率千奇百怪,而想要在各种分辨率的设备上显示基本一致的效果,适配成本越来越高。

先来介绍下基本概念

1、分辨率:

分辨率就是手机屏幕的像素点数,“宽×高”,安卓手机屏幕常见的分辨率有480×800、720×1280、1080×1920等。

2、屏幕大小:

屏幕大小是手机对角线的物理尺寸,以英寸(inch)为单位。比如5寸手机,对角线的尺寸:5寸×2.54厘米/寸=12.7厘米。

3、px(Pixel):

设备真实像素,同一像素值在不同分辨率的手机上展示不同,分辨率越大,展示区域越小

4、dpi(Dots Per Inch):

每英寸面积内的像素点数,计算方式如下。

比如我的手机,华为mate9: dpi = √﹙1080 * 1080 + 1920 * 1920﹚/ 5.9 = 373

比如 Google Pixel2 : dpi = √﹙1080 * 1080 + 1920 * 1920﹚/ 5.0 = 440

4.5寸手机:dpi = √﹙1080 * 1080 + 1920 * 1920﹚/ 4.5 = 440

5、density:
屏幕密度,density = dpi/160

6、dp(Device Independent Pixels):

设备独立像素,每英寸160px的设备上,1dp = 1px;以此类推,每英寸320px的设备上,1dp = 2px。

dp = px / density

dp = px * (160 / dpi) => 1dp = 2px * ( 160 / 320)

7、dp和px转换关系

  • px = density * dp;
  • density = dpi / 160;
  • px = (dpi / 160) * dp;

系统屏幕密度:

  • ldpi文件夹下对应的密度为120dpi,对应的分辨率为240*320
  • mdpi文件夹下对应的密度为160dpi,对应的分辨率为320*480
  • hdpi文件夹下对应的密度为240dpi,对应的分辨率为480*800
  • xhdpi文件夹下对应的密度为320dpi,对应的分辨率为720*1280
  • xxhdpi文件夹下对应的密度为480dpi,对应的分辨率为1080*1920

通过dp加上自适应布局和weight比例布局可以基本解决不同手机上适配的问题,这基本是最原始的Android适配方案。

我原以为这样就够了,但是这只能保证我们写出来的界面适配绝大部分手机,部分手机仍然需要单独适配,为什么dp只解决了90%的适配问题?因为并不是所有的1080P的手机dpi都是160的整数倍,比如Google 的Pixel2(1920 * 1080)的dpi是420,也就是说,在Pixel2中,1dp=2.625px,这样会导致相同分辨率的手机中,一个100dp * 100dp的控件,在一般的1080P手机上(dpi是480)展示是300px,而Pixel 2 中只有262.5px,这样控件的实际大小会有所不同。

图一是1080P、480dpi的手机,图二是1080P、420dpi的手机,设置图片宽度为360dp

图一占据px: 360dp * ( 480dpi / 160) = 1080 px 刚好满屏

图二占据px: 360dp * ( 420dpi / 160) = 945 px 未满屏

通过DisplayMetrix来转换dp、px

/**
     * dp转换成px
     * px = density * dp 
     */
    private int dp2px(Context context,float dpValue){
        float scale=context.getResources().getDisplayMetrics().density;
        return (int)(dpValue*scale+0.5f);
    }

    /**
     * px转换成dp
     */
    private int px2dp(Context context,float pxValue){
        float scale=context.getResources().getDisplayMetrics().density;
        return (int)(pxValue/scale+0.5f);
    }
    /**
     * sp转换成px
     */
    private int sp2px(Context context,float spValue){
        float fontScale=context.getResources().getDisplayMetrics().scaledDensity;
        return (int) (spValue*fontScale+0.5f);
    }
    /**
     * px转换成sp
     */
    private int px2sp(Context context,float pxValue){
        float fontScale=context.getResources().getDisplayMetrics().scaledDensity;
        return (int) (pxValue/fontScale+0.5f);
    }

利用TypeValue来转换dp、px

原理同上,也是通过DisplayMetrix.density 来进行dp转换, 通过DisplyMetrix.scaledDensity来进行sp转换

    private int dp2px(Context context,int dpValue){
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpValue,
                    context.getResources().getDisplayMetrics());
    }
    private int sp2px(Context context,int spValue){
        return (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, spValue,     
                    context.getResources().getDisplayMetrics());
    }

TypedValue#applyDimension()

 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; //同上,px = density * dp
        case COMPLEX_UNIT_SP:
            return value * metrics.scaledDensity; //文字的sp
        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;
    }

获取手机屏幕大小

华为Mate 9的屏幕尺寸是5.9英寸,分辨率是1920×1080像素。

经过计算并不是160的整数倍,373/160 = 2.3 , 通过系统调用发现density = 3.0,不知道为啥? 也许是手机厂商设置的480?。

至于1920 和1808是手机的虚拟导航键的问题,展示了虚拟导航键,size就变化了。

Log.e(tag, "getWindowManager().getDefaultDisplay().getRealMetrics(display)/n")
        val display = DisplayMetrics()
        windowManager.defaultDisplay.getRealMetrics(display)
        Log.d(tag, display.toString())

        Log.e(tag, "getWindowManager().getDefaultDisplay().getMetrics(display)/n")
        windowManager.defaultDisplay.getMetrics(display)
        Log.d(tag, display.toString())


        Log.e(tag, "getWindowManager().getDefaultDisplay().getRealSize(size)")
        val size = Point()
        windowManager.defaultDisplay.getRealSize(size)
        Log.d(tag, size.toString())

        Log.e(tag, "getWindowManager().getDefaultDisplay().getSize(size)")
        windowManager.defaultDisplay.getSize(size)
        Log.d(tag, size.toString())

        Log.e(tag, "getResources().getDisplayMetrics()")
        val metrix = resources.displayMetrics
        Log.d(tag, metrix.toString())

猜你喜欢

转载自blog.csdn.net/ZHENZHEN9310/article/details/86476897
今日推荐