Android 自适应不同分辨率屏幕

时光怂恿深爱的人放手 提交于 2019-11-30 16:27:18

前几天,面试的时候,有问道关于如何自适应不同分辨率屏幕的问题。当时也是一知半解,今天索性看了很多资料,好好的总结了下。

    首先解释几个基本的概念:

    in:表示英寸,是屏幕的物理尺寸。每英寸等于2.54厘米。例如,形容手机屏幕大小,经常说,3.2(英)寸、3.5(英)寸、4(英)寸就是指这个单位。这些尺寸是屏幕的对角线长度。如果手机的屏幕是3.2英寸,表示手机的屏幕(可视区域)对角线长度是3.2*2.54 = 8.128厘米。

   px:表示屏幕实际的象素。例如,320*480的屏幕在横向有320个象素,在纵向有480个象素。

   dip或dp(与密度无关的像素):这个和设备硬件有关,为了支持WVGA、HVGA和QVGA推荐使用这个。一种基于屏幕密度的抽象单位。设置一些view的宽高可以用这个,一般情况下,在不同分辨率,都不会有缩放的感觉。如果用px的话,320px占满HVGA的宽度,到WVGA上就只能占一半不到的屏幕了,那一定不是你想要的。

   sp: 除了与密度无关外,还与scale无关 主要处理字体的大小。 

   density:屏幕密度,每英寸有多少个像素显示点,与分辨率是两个概念

   


VGA:480*640

Low:120 Medium:240 High:480

方便记忆的法子:QVGA即"Quarter VGA"。顾名思义即VGA的四分之一尺寸

HVGA 即“Half VGA”

WVGA即VGA的另一种形式,比VGA分辨率高,别名 : Wide VGA, ,其分辩率为800×480象素。是扩大了VGA(640×480)的分辨率。应用于PDA和手机等,因为很多网页的宽度都是800,所以WVGA的屏幕会更加适和于浏览网页,可以说是未来手持设备的分辨率的大趋势

 

换算公式:px=(density/160)dp其中density一般为3个常用固定值240/160/120

    比如如果在屏幕为320*480设置一个view的大小为320px,则这个view充满整个width,如果设备屏幕为480*800,则这个view只是充满width的320/480=2/3。显然不符合自适应不同的分辨率要求。如果设置view的大小为320dp,则根据px = (density/160)dp,480*800的屏幕密度为240,可知px=1.5dp。所以真实像素大小为320*1.5=480

    然后我在模拟器上测试了下,但发现一个问题,打印出来的并非是模拟器的真实分辨率,又查了下,成功解决

    在一个Activity的onCreate方法中,写入如下代码:

       DisplayMetrics metric = new DisplayMetrics();

       getWindowManager().getDefaultDisplay().getMetrics(metric);

       int width = metric.widthPixels;    // 屏幕宽度(像素)

       int height = metric.heightPixels;  // 屏幕高度(像素)

       float density = metric.density;     // 屏幕密度(0.75 / 1.0 / 1.5)

       int densityDpi = metric.densityDpi; // 屏幕密度DPI(120 / 160 / 240)

    但是,需要注意的是,在一个低密度的小屏手机上,仅靠上面的代码是不能获取正确的尺寸的。比如说,一部240x320像素的低密度手机,如果运行上述代码,获取到的屏幕尺寸是320x427。因此,研究之后发现,若没有设定多分辨率支持的话,Android系统会将240x320的低密度(120)尺寸转换为中等密度(160)对应的尺寸,这样的话就大大影响了程序的编码。所以,需要在工程的AndroidManifest.xml文件中,<manifest>中加入supports-screens节点,具体的内容如下:

       <supports-screens

           android:smallScreens="true"

           android:normalScreens="true"

           android:largeScreens="true"

           android:resizeable="true"

           android:anyDensity="true" />

   这样的话,当前的Android程序就支持了多种分辨率,那么就可以得到正确的物理尺寸了。

   如此测试通过没问题!

  另一方面:默认情况下面系统会自动调整和缩放位图,但是难免拉伸位图 

   apk的资源包中,当屏幕density=240时使用hdpi标签的资源
  当屏幕density=160时,使用mdpi标签的资源
  当屏幕density=120时,使用ldpi标签的资源。
  不加任何标签的资源是各种分辨率情况下共用的

  1在XML布局,使用wrap_content和fill_parent来填充整个父窗口;

  2使用FrameLayout的,而不是AbsoluteLayout,减少界面布局对屏幕大小的依赖;

  3 NEVER usehard-coding for pixel value, use dip (density independent pixel)(不要硬编码像素值px,而是使用独立像素值的dip)

  4根据density和resolution 为不同的设备准备合适的图片资源。


具体方案:

1.根据不同屏幕尺寸,提供不同布局

    为了保证你的位图是最好看的,默认情况下面,android会自动调整应用程序的布局,但是大多数情况下面,根据广义尺寸,小,正常,大,更大去增加不同的布局资源。比如,如果需要对大小为large的屏幕提供支持,需要在res目录下新建一个文件夹layout-large/并提供layout。当然,也可以在res目录下建立layout-portlayout-land两个目录,里面分别放置竖屏和横屏两种布局文件,以适应对横屏竖屏自动切换。

2.提供不同的屏幕密度和不同的位图drawables

   根据广义密度,低,中型, 高,特高去添加不同的位图资源。比如,如需对密度为low的屏幕提供合适的图片,需新建文件夹drawable-ldpi/。应尽量使用点9格式的图片,图片大小的确定:low:medium:high:extra high比例为3:4:6:8。举例来说,对于中等密度(medium)的屏幕你的图片像素大小为48×48,那么低密度(low)屏幕的图片大小应为36×36,高(high)的为72×72extra high96×96

3关于系统怎么动态的寻找替代资源?

   1.系统根据当前的屏幕大小和密度,然后动态的采用程序中提供特定的资源。

   2.如果没有匹配的资源,系统会使用默认的资源进行缩放从而符合当前屏幕的资源,默认的资源是那些没有标签配置限定符。

   关于系统的资源配置的目录Android系统支持多配置资源文件,我们可以追加新的资源目录到你的Android项目中。命名规范:资源名字-限制符



最后:

在xml布局文件中,我们既可以设置px,也可以设置dp(或者dip)。一般情况下,我们都会选择使用dp,这样可以保证不同屏幕分辨率的机器上布局一致。但是在代码中,如何处理呢?很多控件的方法中都只提供了设置px的方法,例如setPadding,并没有提供设置dp的方法。这个时候,如果需要设置dp的话,就要将dp转换成px了。

以下是一个应用类,方便进行px和dp之间的转换。


[java]  view plain copy
<EMBED id=ZeroClipboardMovie_1 name=ZeroClipboardMovie_1 type=application/x-shockwave-flash align=middle pluginspage=http://www.macromedia.com/go/getflashplayer height=18 width=18 src=http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf wmode="transparent" flashvars="id=1&width=18&height=18" allowfullscreen="false" allowscriptaccess="always" bgcolor="#ffffff" quality="best" menu="false" loop="false">
  1. import android.content.Context;  
  2.   
  3. public class DensityUtil {  
  4.   
  5.     /** 
  6.      * 根据手机的分辨率从 dp 的单位 转成为 px(像素) 
  7.      */  
  8.     public static int dip2px(Context context, float dpValue) {  
  9.         final float scale = context.getResources().getDisplayMetrics().density;  
  10.         return (int) (dpValue * scale + 0.5f);  
  11.     }  
  12.   
  13.     /** 
  14.      * 根据手机的分辨率从 px(像素) 的单位 转成为 dp 
  15.      */  
  16.     public static int px2dip(Context context, float pxValue) {  
  17.         final float scale = context.getResources().getDisplayMetrics().density;  
  18.         return (int) (pxValue / scale + 0.5f);  
  19.     }  
  20. }  
[java]  view plain copy
<EMBED id=ZeroClipboardMovie_2 name=ZeroClipboardMovie_2 type=application/x-shockwave-flash align=middle pluginspage=http://www.macromedia.com/go/getflashplayer height=18 width=18 src=http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf wmode="transparent" flashvars="id=2&width=18&height=18" allowfullscreen="false" allowscriptaccess="always" bgcolor="#ffffff" quality="best" menu="false" loop="false">
  1. import android.content.Context;  
  2.   
  3. public class DensityUtil {  
  4.   
  5.     /** 
  6.      * 根据手机的分辨率从 dp 的单位 转成为 px(像素) 
  7.      */  
  8.     public static int dip2px(Context context, float dpValue) {  
  9.         final float scale = context.getResources().getDisplayMetrics().density;  
  10.         return (int) (dpValue * scale + 0.5f);  
  11.     }  
  12.   
  13.     /** 
  14.      * 根据手机的分辨率从 px(像素) 的单位 转成为 dp 
  15.      */  
  16.     public static int px2dip(Context context, float pxValue) {  
  17.         final float scale = context.getResources().getDisplayMetrics().density;  
  18.         return (int) (pxValue / scale + 0.5f);  
  19.     }  
  20. } 
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!