Android(安卓)关于屏幕的一些事儿
16lz
2022-07-09
转载请标明出处:
http://blog.csdn.net/xuehuayous/article/details/50596463;
本文出自:【Kevin.zhou的博客】
前言: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.javaAdroid-Utils是我想把之前的工具类整理下,还在完善中,大家有好的想法可以给我留言,共同进步! 另外,欢迎 star or f**k me on github!结语:
其实我们在开发中还比较在意的一个问题是:
就是我要写多少才他么是真正的占屏幕宽度的一半!!! 后续打算做个工具来处理这些烦人的东西。 要过年了还他么感冒了,加上一些乱七八糟的事情,最近好不爽。牢骚发完了,该干嘛干嘛去!
更多相关文章
- 【阿里云镜像】切换阿里巴巴开源镜像站镜像——Debian镜像
- Android屏幕分辨率正确获取及PX,DPI,DP,SP等的对应关系
- Android系统配置数据库注释(settings.db)
- android studio Could not find com.android.support.constraint
- android ndk编译x264开源(用于android的ffmpeg中进行软编码)
- android 获取唯一标识
- android拍照与读取相册
- Android(安卓)热点开关状态的判断和获取热点ssid
- Android软键盘适配问题