zoukankan      html  css  js  c++  java
  • <html>

    前言:android数以千计的分辨率令我们开发人员头痛不已。那么我们能不能解开它的面纱来赞赏一下它的本质呢?下面就带大家进行这么一场美妙的观光。


    一、概述


        Android的屏幕适配是一个比較受关注的问题,再加上UI、UE通常是依照IOS出一套然后Android也是对比着去做,给美工小妹妹想要讲清楚dp是一件比登天还难的事情。先来了解下这几个点位之间的关系。


    二、 PX、PT、PPI、DPI、DP


    术语
    说明
    备注
    PX
    (pixel),像素。屏幕上显示数据的最主要的点

    PT
    (point), 点。印刷行业经常使用单位
    1pt=1/72英寸
    PPI
    (pixel per inch)。每英寸像素数

    DPI
    (dot per inch),每英寸点数

    DP
    dip(Density-independent pixel), 设备独立像素
    1dp=160dpi时1px长度

        当中px, pt, dp为长度单位,ppi和dpi为密度单位。安卓端屏幕大小各不同样,依据其像素密度,分为下面几种规格:


        dp为安卓开发时的长度单位。依据不同的屏幕分辨率,与px有不同的相应关系。


    三、获取屏幕宽高


        获取屏幕的宽高是我们开发中经常遇到的问题,并且相信大家都已经很熟悉。最经常使用的为下面两种:
    public static int getScreenHeight1(Activity activity) {
        return activity.getWindowManager().getDefaultDisplay().getHeight();
    }
    public static int getScreenHeight2(Activity activity) {
        DisplayMetrics displayMetrics = new DisplayMetrics();
        activity.getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
        return displayMetrics.heightPixels;
    }
        事实上以上两种方式是一样的,仅仅只是另外一种是把信息封装到 DesplayMetrics中。再从DesplayMetrics得到数据。
    在 Android 3.2(Api 13) 之后又提供了例如以下的一个方法,将数据封装到Point中。然后返回宽度高度信息。

    @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2)
    public static int getScreenHeight3(Activity activity) {
        Point point = new Point();
        activity.getWindowManager().getDefaultDisplay().getSize(point);
        return point.y;
    }
        在 Android 4.2(Api17) 之后提供了例如以下方法,与第三种类似也是将数据封装到Point中。然后返回款高度信息。

    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
    public static int getScreenHeight4(Activity activity) {
        Point realSize = new Point();
        activity.getWindowManager().getDefaultDisplay().getRealSize(realSize);
        return realSize.y;
    }

        那么我们执行一把,看下获取的详细信息。下面为MI3和海尔平板(S1001LS)获取的数据:




        在MI3上四个方法获取到的数据都为1920px,可是在海尔平板(S1001LS)上面前三个为1848px。最后一个为1920px。出现了分歧,我们看官方说分辨率是多少:



        我擦,吓我一跳,原来我天天扔的开机发这么叼,三千多呢。也验证了他高度1920px。那么看开我们写的四种获取屏幕高度的方法前三种都是有问题的。根本不是获取到的屏幕高度。

    事实上是因为包不包括底部的导航栏的原因。


        因为getRealSize()这种方法是是在 Android 4.2(Api17) 之后提供的,那是不是意味着之前的版本号我们就不能得到确切的屏幕分辨率呢?我们到源代码来看下:


        通过查找发现。在Android 4.0(Api14)就提供了getRealSize()这种方法,仅仅只是是系统隐藏了,我们不能直接去调用。那么能不能通过反射的方式来使用呢?

    public static int getScreenHeight5(Activity activity) {
        Point realSize = new Point();
        Display display = activity.getWindowManager().getDefaultDisplay();
        try {
            Display.class.getMethod("getRealSize", Point.class).invoke(display, realSize);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return realSize.y;
    }

    和我们预期的一样在Android 4.0也获取到了数据。

    事实上系统在Android 3.2(Api13)開始增加了例如以下方法,仅仅只是一直是隐藏的API:
    /**
     * Gets the raw width of the display, in pixels.
     * <p>
     * The size is adjusted based on the current rotation of the display.
     * </p>
     * @hide
     */
    public int getRawHeight() {
    int h = getRawHeightNative();
        if (DEBUG_DISPLAY_SIZE) Slog.v(
                TAG, "Returning raw display height: " + h);
        return h;
    }
    private native int getRawWidthNative();
    当然我们能够通过反射的方式来调用它:

    public static int getScreenHeight6(Activity activity) {
        int heightPixels = 0;
        Display display = activity.getWindowManager().getDefaultDisplay();
        try {
            heightPixels = (Integer) Display.class.getMethod("getRawHeight").invoke(display);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return heightPixels;
    }

    Android 版本号
    版本
    getRawHeight()
    getRealSize(Point p)
    Android 3.2
    13
    包括(隐藏)

    Android 4.0,4.0.1,4.0.2
    14
    包括(隐藏)
    包括(隐藏)
    Android 4.0.3,4.0.4
    15
    包括(隐藏)
    包括(隐藏)
    Android 4.1,4.1.1
    16
    包括(隐藏)
    包括(隐藏)
    Android 4.2,4.2.2
    17
    包括(隐藏)
    包括
    ... ...
    ... ...
    ... ...
    ... ...

    综上,我们能够得到一个比較完整的获取系统屏幕高度的方法:

    public int getRealHeight(Activity activity) {
        int heightPixels = 0;
        Display display = activity.getWindowManager().getDefaultDisplay();
        final int VERSION = Build.VERSION.SDK_INT;
    
        if(VERSION < 13) {
            display.getHeight();
        }else if (VERSION == 13) {
            try {
                heightPixels = (Integer) Display.class.getMethod("getRawHeight").invoke(display);
            } catch (Exception e) {
            }
        } else if (VERSION >= 14 && VERSION < 17) {
            Point realSize = new Point();
            try {
                Display.class.getMethod("getRealSize", Point.class).invoke(display, realSize);
                heightPixels = realSize.y;
            } catch (Exception e) {
            }
        } else {
            Point realSize = new Point();
            display.getRealSize(realSize);
            heightPixels = realSize.y;
        }
        return heightPixels;
    }
    简化一下就是:

    public int getRealHeight(Activity activity) {
        int heightPixels = 0;
        Display display = activity.getWindowManager().getDefaultDisplay();
        final int VERSION = Build.VERSION.SDK_INT;
    
        if(VERSION < 13) {
            display.getHeight();
        }else if (VERSION == 13) {
            try {
                heightPixels = (Integer) Display.class.getMethod("getRawHeight").invoke(display);
            } catch (Exception e) {
            }
        } else {
            Point realSize = new Point();
            try {
                Display.class.getMethod("getRealSize", Point.class).invoke(display, realSize);
                heightPixels = realSize.y;
            } catch (Exception e) {
            }
        }
        return heightPixels;
    }

    四、获取屏幕尺寸


        以上我们为什么费了那么大劲非要搞到屏幕的真实高度呢?我们又没有办法控制下方的导航栏。得到真实高度也没什么卵用。事实上我们获取真实的屏幕高度是为了计算屏幕的高度。那么屏幕的物理尺寸怎么去计算呢?

    1. 屏幕物理尺寸是屏幕对角线的长度。单位英寸。
    2. 屏幕的像素点密度(ppi) ≠ dp;
    3. 屏幕物理宽度 = width / xppi;
    4. 屏幕物理高度 = height / yppi;

    我们发现 DisplayMetrics 中有例如以下两个变量:

    /**
     * The exact physical pixels per inch of the screen in the X dimension.
     */
    public float xdpi;
    /**
     * The exact physical pixels per inch of the screen in the Y dimension.
     */
    public float ydpi;
    没错,这就是我们要找的在宽度和高度上的ppi(每英寸内的像素点数目)。

    /**
     * 获取屏幕宽度ppi
     *
     * @param activity
     * @return 屏幕宽度ppi
     */
    public static float getWidthPpi(Activity activity) {
        DisplayMetrics displayMetrics = new DisplayMetrics();
        activity.getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
        return displayMetrics.xdpi;
    }
    /**
     * 获取屏幕高度ppi
     *
     * @param activity
     * @return 屏幕高度ppi
     */
    public static float getHeightPpi(Activity activity) {
        DisplayMetrics displayMetrics = new DisplayMetrics();
        activity.getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
        return displayMetrics.ydpi;
    }
    那么获取宽高的物理尺寸也就简单了:

    /**
     * 获取屏幕宽度物理尺寸
     *
     * @param activity
     * @return
     */
    public static float getWidthInch(Activity activity) {
        int realWidth = getRealWidth(activity);
        return (float)realWidth / getWidthPpi(activity);
    }
    /**
     * 获取屏幕高度物理尺寸
     *
     * @param activity
     * @return
     */
    public static float getHeightInch(Activity activity) {
        int realHeight = getRealHeight(activity);
        return (float)realHeight / getHeightPpi(activity);
    }
    依据勾股定理。宽度和高度的尺寸都知道了,对角线的长度就是长度平方加上高度平方再开平方:

    /**
     * 获取屏幕物理尺寸
     *
     * @param activity
     * @return 屏幕物理尺寸
     */
    public static float getScreenInch(Activity activity) {
        return (float)Math.sqrt(Math.pow(getWidthInch(activity), 2) + Math.pow(getHeightInch(activity), 2));
    }

    五、获取长宽DP

        
        知道DP和PPI的关系,以及屏幕的长宽值之后想要获取屏幕长度和宽度上的总dp就比較简单了。

    首先通过代码得到dp和ppi(dpi)的相应关系:

    /**
     * 获取屏幕密度
     *
     * @return
     */
    public static float getScreenDensity(Activity activity) {
        DisplayMetrics displayMetrics = new DisplayMetrics();
        activity.getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
        return displayMetrics.density;
    }
    然后用屏幕宽高除以 屏幕密度就是宽高上的dp数目:

    /**
     * 获取屏幕可操作区域宽度dp数目
     *
     * @param activity
     * @return
     */
    public static float getScreenWidthDp(Activity activity) {
        return getScreenWidth(activity) / getScreenDensity(activity);
    }
    /**
     * 获取屏幕高度可操作区域dp数目
     *
     * @param activity
     * @return
     */
    public static float getScreenHeightDp(Activity activity) {
        return getScreenHeight(activity) / getScreenDensity(activity);
    }
    /**
     * 获取屏幕真实宽度dp数目
     *
     * @param activity
     * @return
     */
    public static float getRealWidthDp(Activity activity) {
        return getRealWidth(activity) / getScreenDensity(activity);
    }
    /**
     * 获取屏幕真实高度dp数目
     *
     * @param activity
     * @return
     */
    public static float getRealHeightDp(Activity activity) {
        return getRealHeight(activity) / getScreenDensity(activity);
    }

    六、推断是手机还是平板


        有时候在一套代码跑在手机和平板上,所以就要依据是平板还是pad来设置不同的布局。当然手机和平板公用一套代码显然不是一个好的方案。也确实能减小项目的开发维护成本。
    下面为几种方案:
    1. 推断设备是否具备通话功能
    2. 推断设备是否大于6英寸
    3. 推断设备是否为大尺寸

    第一种方案不太有效,由于如今好多平板也是能够打电话的。推断设备是否大于6英寸是一个经常使用的做法:

    /**
     * 推断屏幕是否大于6英寸
     *
     * @param activity
     * @return
     */
    public static boolean isMoreThan6Inch(Activity activity) {
        return getScreenInch(activity) >= 6.0;
    }
    第三种推断是否为大尺寸设备:
    /**
     * 推断设备是否为大尺寸屏幕
     *
     * @param context
     * @return
     */
    public static boolean isScreenSizeLarge(Context context) {
        return (context.getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_LARGE;
    }
    我们开看下SCREENLAYOUT_SIZE_LARGE 是怎么定义的:

    /** Constant for {@link #screenLayout}: a {@link #SCREENLAYOUT_SIZE_MASK}
     * value indicating the screen is at least approximately 480x640 dp units,
     * corresponds to the
     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#ScreenSizeQualifier">large</a>
     * resource qualifier.
     * See <a href="{@docRoot}guide/practices/screens_support.html">Supporting
     * Multiple Screens</a> for more information. */
    public static final int SCREENLAYOUT_SIZE_LARGE = 0x03;
    能够看到是推断的屏幕高度和宽度的dp数目。他要求最小为 480X640 dp,我认为最好这两个都满足才是平板:

    /**
     * 推断设备是否为平板
     *
     * @param activity
     * @return true 平板;
     *          false 手机;
     */
    public static boolean isTablet(Activity activity) {
        return isMoreThan6Inch(activity) && isScreenSizeLarge(activity);
    }

    七、源代码及演示样例

       
    给大家提供一个github的地址: Android-Utils 中的 ScreenUtil.java Adroid-Utils是我想把之前的工具类整理下,还在完好中,大家有好的想法能够给我留言。共同进步!
        另外,欢迎 star or f**k me on github! 


    结语:

        
        事实上我们在开发中还比較在意的一个问题是:




        就是我要写多少才他么是真正的占屏幕宽度的一半。!。兴许打算做个工具来处理这些烦人的东西。
    要过春节了还他么感冒了,加上一些乱七八糟的事情,近期好不爽。牢骚发完了,该干嘛干嘛去!

img

xuehuayous

等级:

排名:千里之外

我的GitHub
文章分类
阅读排行
文章存档
评论排行
最新评论
查看全文
  • 相关阅读:
    chrome 浏览器与chromedriver 对应关系以及 对应的驱动下载
    pip 安装six 报错 ModuleNotFoundError: No module named 'pip._internal.cli.main'
    django 连接远程mysql 报错 django.db.utils.OperationalError: (1045, "Access denied for user 'root'@'localhost' (using password: NO)")
    MySQL8.0允许外部访问
    Linux 安装mysql
    php导出导入excel插件
    解决php导出csv文件utf8中文乱码问题
    Docker 安装mysql5.6
    centos7 设置静态IP
    U盘制作centos7系统并安装
  • 原文地址:https://www.cnblogs.com/blfbuaa/p/7052540.html
  • Copyright © 2011-2022 走看看