首页 > 代码库 > <html>

<html>

转载请标明出处:
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=http://www.mamicode.com/"{@docRoot}guide/topics/resources/providing-resources.html#ScreenSizeQualifier">large>能够看到是推断的屏幕高度和宽度的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!技术分享 技术分享


结语:

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

技术分享

技术分享


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

<link rel="stylesheet" href="http://static.blog.csdn.net/public/res-min/markdown_views.css?

v=1.0" />

文章列表

 
 
<script type="text/javascript"> function btndigga() { $(".tracking-ad[data-mod=‘popu_222‘] a").click(); } function btnburya() { $(".tracking-ad[data-mod=‘popu_223‘] a").click(); } $(function () { if ($(".markdown_views").length > 0) { if($("#fromjs").length==0) { $(‘pre.prettyprint code‘).each(function () { var lines = $(this).text().split(‘\n‘).length; var $numbering = $(‘
    ‘).addClass(‘pre-numbering‘).hide(); $(this).addClass(‘has-numbering‘).parent().append($numbering); for (i = 1; i <= lines; i++) { $numbering.append($(‘
  • ‘).text(i)); }; $numbering.fadeIn(1700); }); } } else { $("#article_content pre").each(function () { var $this = $(this); if ($this.attr("class") != undefined) { if ($this.attr("class").indexOf("brush:") != -1) { var lang = $this.attr("class").split(‘;‘)[0].split(‘:‘)[1]; $this.attr(‘name‘, ‘code‘); $this.attr(‘class‘, lang); } if ($this.attr("class")) { $this.attr(‘name‘, ‘code‘); } } }); $(‘#article_content textarea[name=code]‘).each(function () { var $this = $(this); if ($this.attr("class").indexOf(":") != -1) { $this.attr("class", $this.attr("class").split(‘:‘)[0]); } }); dp.SyntaxHighlighter.HighlightAll(‘code‘); $(‘.highlighter‘).addClass(‘dp-highlighter‘); if (!window.clipboardData) { setTimeout("setCopyBtn()", 500); } function __get_code_toolbar(snippet_id) { return $("技术分享" + "技术分享"); } $("[code_snippet_id]").each(function () { __s_id = $(this).attr("code_snippet_id"); if (__s_id != null && __s_id != "" && __s_id != 0 && parseInt(__s_id) > 70020) { __code_tool = __get_code_toolbar(__s_id); $(this).prev().find(".tools").append(__code_tool); } }); $(".bar").show(); } }); </script>
    <script>window._bd_share_config = { "common": { "bdSnsKey": {}, "bdText": "", "bdMini": "1", "bdMiniList": false, "bdPic": "", "bdStyle": "0", "bdSize": "16" }, "share": {} }; with (document) 0[(getElementsByTagName(‘head‘)[0] || body).appendChild(createElement(‘script‘)).src = http://www.mamicode.com/‘http://bdimg.share.baidu.com/static/api/js/share.js?v=89860593.js?

    cdnversion=‘ + ~(-new Date() / 36e5)];</script>

    Android 手把手教你开发轮转大图控件
    Android setContentView源代码解析
    <script type="text/javascript"> /*博客内容页下方Banner2-728*90。创建于2014-7-3*/ var cpro_id = "u1607657"; </script> <script type="text/javascript" src="http://cpro.baidustatic.com/cpro/ui/c.js"></script>

    发表评论

    技术分享
    发表评论  
    HTML/XMLobjective-cDelphiRubyPHPC#C++JavaScriptVisual BasicPythonJavaCSSSQL其他
     
技术分享

xuehuayous

等级:技术分享

排名:千里之外

加关注 发私信
我的GitHub

    欢迎Follow、Fork、Star

文章分类
  • Java0基础与加强(13)
  • 开源框架分析(10)
  • 设计模式(0)
  • 原生控件分析(2)
  • 算法(1)
  • 个人开源框架(2)
  • Android(12)
阅读排行
  • Android 拍照、选择图片并裁剪(23300)
  • Android UltimateRecyclerView优雅地使用RecyclerView(16242)
  • Android PullToRefresh 分析之四、扩展RecyclerView(10139)
  • Android RecyclerView加入头部和尾部(8805)
  • Android 上传图片到JavaWebserver(7615)
  • Java 算法 JSON Bean相互转化及JSON生成实体类(5696)
  • Android PullToRefresh 分析之中的一个、初识PullToRefresh(4981)
  • Android PullToRefresh 全然解析(4687)
  • Android PullToRefresh 分析之五、扩展刷新载入样式(4387)
  • Android 打造底部导航控件(4338)
文章存档
  • 2017年02月(1)
  • 2017年01月(1)
  • 2016年10月(1)
  • 2016年09月(1)
  • 2016年06月(1)
  • 2016年05月(2)
  • 2016年04月(1)
  • 2016年03月(1)
  • 2016年02月(2)
  • 2016年01月(4)
  • 2015年12月(6)
  • 2015年04月(2)
  • 2014年12月(4)
  • 2014年10月(1)
  • 2014年04月(1)
  • 2014年03月(13)
评论排行
  • Android 拍照、选择图片并裁剪(33)
  • Android PullToRefresh 分析之四、扩展RecyclerView(15)
  • Android RecyclerView加入头部和尾部(12)
  • Android PullToRefresh 分析之五、扩展刷新载入样式(10)
  • Android PullToRefresh 全然解析(9)
  • Android UltimateRecyclerView优雅地使用RecyclerView(6)
  • Android 关于屏幕的一些事儿(3)
  • Android 上传图片到JavaWebserver(3)
  • Android 手把手教你开发轮转大图控件(2)
  • Android LayoutInflater源代码解析(1)
最新评论
  • Android UltimateRecyclerView优雅地使用RecyclerView

    imilyyan回复:

    请问下在CoordinatorLayout的布局下,加入了toolbar和UltimateRecyc...

  • Android PullToRefresh 分析之五、扩展刷新载入样式

    Jack_lyj回复:

    新手请教:LoadingLayoutBase在哪能看到都做了什么啊,急急急,谢谢

  • Android PullToRefresh 分析之五、扩展刷新载入样式

    Jack_lyj回复:

    新手请教:LoadingLayoutBase在哪能看到都做了什么啊,谢谢

  • Android 拍照、选择图片并裁剪

    zb412646700回复:

    主要是引用uCrop实现图片剪辑,不是必需搞注解。抽象。接口这些东西。

    。不但显示不出自己的水平,反而...

  • Android 拍照、选择图片并裁剪

    zb412646700回复:

    主要是引用uCrop实现图片剪辑,不是必需搞注解,抽象,接口这些东西。

    。。

    不但显示不出自己的水平,反而...

  • Android 拍照、选择图片并裁剪

    u012534883回复:

    博主你好,代码我已经执行了,如今我发现不管设置withAspectRatio多少,裁剪框始终是一样宽...

  • Android 拍照、选择图片并裁剪

    u012534883回复:

    博主你好。代码我已经执行了,如今我发现不管设置withAspectRatio多少,裁剪框始终是一样宽...

  • Android 拍照、选择图片并裁剪

    u012534883回复:

    博主你好。代码我已经执行了,如今我发现不管设置withAspectRatio多少,裁剪框始终是一样宽...

  • Android 拍照、选择图片并裁剪

    jifashi4回复:

    没有jar包吗?

  • Android PullToRefresh 分析之四、扩展RecyclerView

    u014187519回复:

    recycleView没有setAdapter方法和setLayoutManager是什么情况啊?

技术分享
  • 我的GitHub
  • 文章分类
  • 阅读排行
  • 文章存档
  • 评论排行
  • 最新评论
<script type="text/javascript" src="http://static.blog.csdn.net/skin/skin2-template/js/apps/accordion.js"></script> <script type="text/javascript" src="http://static.blog.csdn.net/skin/skin2-template/js/apps/jquery.mousewheel.js"></script> <script type="text/javascript" src="http://static.blog.csdn.net/skin/skin2-template/js/apps/jquery.rollbar.min.js"></script> <script type="text/javascript" src="http://static.blog.csdn.net/skin/skin2-template/js/apps/transition.js"></script> <script id="noticeScript" type="text/javascript" charset="utf-8" btnId="header_notice_num" wrapId="note1" count="5" subCount="5" src="http://c.csdnimg.cn/public/common/toolbar/js/notify.js"></script> <script id="csdn-toolbar-id" btnId="header_notice_num" wrapId="note1" count="5" subCount="5" type="text/javascript" src="http://c.csdnimg.cn/public/common/toolbar/js/toolbar.js"></script>
<style type="text/css"> #popup_mask { position: absolute; width: 100%; height: 100%; background: #000; z-index: 9999; left: 0px; top: 0px; opacity: 0.3; filter: alpha(opacity=30); display: none; } .skin_list dl dd div ol { margin-left:15px; } .skin_list dl dd div ol li { display:list-item;list-style-type:decimal;margin-left:15px; } </style>
<script id="adJs52b5334"></script> <script>document.getElementById("adJs52b5334").src = "http://ads.csdn.net/js/opt/52b5334.js?t=" + Math.random();</script>
<link rel="stylesheet" href="http://static.blog.csdn.net/css/blog_code.css" /> <script type="text/javascript" src="http://static.blog.csdn.net/scripts/saveToCode.js"></script> <script type="text/javascript" src="http://csdnimg.cn/rabbit/tracking-ad/main.js?

75eacd8></script> <script language="javascript" type="text/javascript" src="http://ads.csdn.net/js/async_new.js"></script> <script type="text/javascript"> var fromjs=$("#fromjs"); if(fromjs.length>0) { $("#fromjs .markdown_views pre").addClass("prettyprint"); prettyPrint(); $(‘pre.prettyprint code‘).each(function () { var lines = $(this).text().split(‘\n‘).length; var $numbering = $(‘