Android布局中长度单位的深入研究

要想使自己的布局在不同设备达到精准空置,理清理顺Android布局长度单位之间关系很有必要,否则你也许会经常挠头为什么显示出来的布局不是自己定义的效果呢,有些东西,虽然基础,但是弄个透彻也需要花些功夫,废话不多说,下面开始。

AD:2013大数据全球技术峰会课程PPT下载


    要想使自己的布局在不同设备达到精准空置,理清理顺Android布局长度单位之间关系很有必要,否则你也许会经常挠头为什么显示出来的布局不是自己定义的效果呢,有些东西,虽然基础,但是弄个透彻也需要花些功夫,废话不多说,下面开始。

    1.先了解一下Android有支持哪些长度单位

    px: pixels(像素). 不同设备显示效果相同,比如我们800*480的屏幕宽度就是 800px

    dip: device independent pixels(设备独立像素). 不同设备有不同的显示效果,这个和设备硬件有关,通常屏幕大时,density就大,屏幕小时,density就小

    屏幕实际分辨率为240px*400px时,densityDpi=120

    屏幕实际分辨率为320px*533px,densityDpi=160

    屏幕实际分辨率为480px*800px,densityDpi=240

    而dip与px之间的换算关系是:

    pixs =dips * (densityDpi/160),也就是说当densityDpi=160时,1dip=1px

    sp: scaled pixels(放大像素),sp的大小取决于系统metrics.scaledDensity值大小

    pt: point,是一个标准的长度单位,1pt=1/72英寸,用于印刷业(基本用不到)

    pt与px的换算关系:pixs = pt*xdpi * (1.0f/72);xdpi表示1英寸像素个数

    in(英寸)长度单位(基本用不到)

    in与px的换算关系:pixs = in*xdpi

    mm(毫米)长度单位(基本用不到)

    mm与px的换算关系:pixs = mm * xdpi * (1.0f/25.4f)

    2.系统获取长度单位

    看了上面具体长度单位的含义你会产生一个疑问,不同单位换算取决于系统的一些属性,比如densityDpi的值,xdpi的值,那么系统这些值在哪里获取了,直接看测试用例:

      
      
    1. public void testgetdisplay(){
    2. WindowManager wm = (WindowManager) this.getInstrumentation().getContext().getSystemService(Context.WINDOW_SERVICE);
    3. DisplayMetrics mDisplayMetrics = new DisplayMetrics();
    4. wm.getDefaultDisplay().getMetrics(mDisplayMetrics);
    5. System.out.println("display.height="+wm.getDefaultDisplay().getHeight());
    6. System.out.println("display.width="+wm.getDefaultDisplay().getWidth());
    7. System.out.println("densityDpi="+mDisplayMetrics.densityDpi);
    8. System.out.println("xdpi="+mDisplayMetrics.xdpi);
    9. System.out.println("density="+mDisplayMetrics.density);
    10. }

    3.densityDpi与drawable-(hdpi,mdpi,ldpi)之间的关系

    系统drawable有hdpi,mdpi,ldpi三个文件夹下面存放不同尺寸的图片,使用哪个文件下的文件,与系统densityDpi值是有关系的。

    densityDpi=120:ldpidensityDpi=160:mdpidensityDpi=240:hdpi

    前面我又说过densityDpi取决于显示屏,这样你就了解了为什么不同显示屏WVGA,HVGA,QVGA会采用不同drawable-(hdpi,mdpi,ldpi)图片。

    分辨率为240px*400px,densityDpi=120-->QVGA:ldpi

    分辨率为320px*533px,densityDpi=160 -->HVGA:mdpi

    分辨率为480px*800px,densityDpi=240 -->WVGA:WVGA

    4.深入了解代码

    尽管了解上面这些理论值,但是有时候发现设置了不同长度单位,可显示出来的效果却出人预想,我曾经就碰到过这种挠头的问题,为解决这个问题,只有深入代码,一探究竟了。

    在深入代码前我们首先要搞清楚一个问题,那就是代码中所有长度值的单位都是px,手上没有现成的例子就以现在我研究的/Launcher2/res/layout-land/workspace_screen.xml为例,看一个自定义属性:

    launcher:cellWidth="105pt"

    该属性自定义了一个桌面快捷图标的宽度,若读者自己测试,自己写个测试view,设置属性:

    android:layout_width="800px"

    是一样的。

    当view被创建的时候,xml中的属性值存在参数AttributeSet attrs中:

      
      
    1. public CellLayout(Context context, AttributeSet attrs, int defStyle)

    继续看该构造函数的实现代码:

      
      
    1. public CellLayout(Context context, AttributeSet attrs, int defStyle) {
    2. super(context, attrs, defStyle);
    3. //获取自定义属性组CellLayout中的所有自定义属性,关于自定义属性,这里不作展开说明
    4. TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CellLayout, defStyle, 0);
    5. //获取属性cellWidth的值,长度单位将转换为px
    6. mCellWidth = a.getDimensionPixelSize(R.styleable.CellLayout_cellWidth, 10);
    7. 。。。
    8. }

    实现长度单位换算的关键代码就在a.getDimensionPixelSize(R.styleable.CellLayout_cellWidth, 10),直接深入到关键代码:

      
      
    1. public int getDimensionPixelSize(int index, int defValue)
    2. public static int complexToDimensionPixelSize(int data,DisplayMetrics metrics)
    3. public static float applyDimension(int unit, float value,DisplayMetrics metrics){
    4. switch (unit) {
    5. case COMPLEX_UNIT_PX:
    6. return value;
    7. case COMPLEX_UNIT_DIP:
    8. return value * metrics.density;
    9. case COMPLEX_UNIT_SP:
    10. return value * metrics.scaledDensity;
    11. case COMPLEX_UNIT_PT:
    12. return value * metrics.xdpi * (1.0f/72);
    13. case COMPLEX_UNIT_IN:
    14. return value * metrics.xdpi;
    15. case COMPLEX_UNIT_MM:
    16. return value * metrics.xdpi * (1.0f/25.4f);
    17. }
    18. return 0;
    19. }

    unit就是指单位类型,这个怎么来的我没有,但我想它肯定是在解析xml是根据不同单位转换的。

     

    图像显示所需的概念:

    像素:一幅图片的显示就是由许多显示着不同颜色的小方格组成的,这样的小方格就被称为像素,是构成图片的最小单位.但是这个像素的具体大小是多少呢?这需要取决于显示这张图片的具体的物理设备显示一个图片像素点的荧光点的大小.图片文件只是记录着它自身有多少个像素点,每个像素点显示什么颜色,至于它自身物理尺寸有多大,它自身也无法得知,例如一张480*800像素的图片在电脑显示器上显示明显要比在手机屏幕上显示大很多,而这张图片本身并没有改变.只是手机的屏比电脑显示器的屏要精细许多,也就是每一个物理像素点要小许多,密度也就大许多.

    分辨率:分为“图像分辨率”与“物理显示分辨率”。它们都是水平像素点数与垂直像素点数的乘积,也就是像素总和数。图像分辨率是指图片文件记录着自身所有的像素数。物理显示分辨率是指物理显示屏水平与垂直能显示的像素数的乘积.有的人说分辨率越高,显示的图片就越清晰,这点是针对物理尺寸相同的情况而言,因为一样的分辨率有可能尺寸可能不一样大,这样光就分辨率来比较清晰度没有可比性.

    密度:就是物理设备上单位尺寸里的像素数,当然是密度越大图片显示就越清晰了.

    Android系统的长度单位:

    px:像素,终端上的一个物理像素点,例如,480*800的屏幕在横向有320个象素,在纵向有480个象素。

    dip(dp):与终端上的物理像素点无关,是一种基于屏幕密度抽象单位,被称作“设备独立像素”,会随着屏幕的密度进行自动的大小调整.

    sp:比例像素,主要处理字体大小,可以根据用户的字体大小首选项进行缩放.

    in:英寸,标准长度单位

    mm:毫米,标准长度单位

    pt:磅,标准长度单位,1/72英寸

    Android屏幕适配:

    为什么要适配:Android终端的物理尺寸,分辨率的类别众多,可能你为一种终端设计了一套UI符合要求,但是在另一类大小的物理终端上显示就完全不是你想要的。

    解决办法:针对这种情况,Android提供了一套标准,它将屏幕分为三类,所以当你新建一个Android工程后,工程自动为你创建三个存放不同分辨率,不同密度下的UI的文件夹,如下图:

    drawable

    三个文件夹下的UI对应不同分辨率及密度的屏幕,对应关系如下:

     

            屏幕类型     分辨率       密度     尺寸
    drawable-hdpi        WVGA    480*800       240     大
    drawable-ldpi        QVGA    240*320       120     小
    drawable-mdpi        HVGA    320*480       160     中

    这三个文件夹只是告诉Android你设计的UI是针对哪种屏幕分辨率及密度进行设计的,Android终端会在打开应用的时候自动根据终端类型去匹配与文件夹里提供的UI相近分辨率及密度的图片。

    假设终端屏是WVGA类型的,也就是密度为240,那么程序打开时,会去提取drawable-hdpi文件夹下图片,然后用一个物理像素去显示一个图片像素,因为你把图片是放在drawable-hdpi文件夹下的,也就是告诉Android你设计的UI是针对480*800,密度为240而言的,那么它不会对图片进行放大或缩小操作,正好用一个物理像素去显示一个图片像素。

    当图片的密度与屏幕密度相同,则不进行缩放.

    当图片的密度与屏幕密度不同,则进行缩放。

    实验1:

    针对480*800的屏我设计了一张图片(分辨率是480*100),分别在不同物理分辨率终端下进行显示,看看Android会不会显示不正常。图片如下:

    density

    布局文件:

    <?xml version="1.0" encoding="utf-8"?>
     2<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     3     android:orientation="vertical"
     4     android:layout_width="fill_parent"
     5     android:layout_height="fill_parent"
     6     android:gravity="center"
     7     >
     8      
     9     <ImageView
    10         android:layout_width="wrap_content"
    11         android:layout_height="wrap_content"
    12         android:src="@drawable/density480800"/>
    13      
    14 </LinearLayout>

    假设:这张图片的宽是480像素,高为100像素,我将它放在drawable-hdpi文件夹下,就是告诉Android我的设计意图是针对480*800的Android终端而言的,那么它会在480*800的终端上以一个物理像素去显示一个图片像素,如果将它放在分辨率为240*320的终端上进行显示,如果Android不对显示进行处理的话,还是以一个物理像素去显示一个图片像素,终端必将显示不下这张图片,因为图片像素超过了物理像素个数。

    注意:我这里只设计了一张图片,并放置于drawable-hdpi文件夹下,并没有在其它文件夹下放置相应的图片,当Android进行匹配的时候也就只能匹配这张图了。

    结果:

    WVGA屏:对比原图,正好显示下,大小相同

    image

    HVGA屏:对比原图,能够显示下,对图片进行了缩小操作.

    image

    QVGA屏:对比原图,能够显示下,对图片进行了缩小操作

    image

    实验2:

    针对240*320的屏我设计了一张图片(分辨率为:240*100),分别在不同物理分辨率终端下进行显示,看看Android会不会显示不正常。图片如下:

    density-240-320

    布局文件:

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:gravity="center"
        >
         
        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/density240320"/>
         
    </LinearLayout>

    这张图就是专门为240*320的屏设计的,设计意图就是图片撑满屏幕宽度,所以设置成wrap_content就够了,没有考虑其它屏的情况

    假设:这张图片的宽是240像素,高为100像素,我将它放在drawable-ldpi文件夹下,就是告诉Android我的设计意图是针对240*320密度为120的Android终端而言的,那么它会在240*320的终端上以一个物理像素去显示一个图片像素,如果将它放在分辨率为480*800的终端上进行显示,如果Android不对显示进行处理的话,还是以一个物理像素去显示一个图片像素,图片必将偏离了设计意图(设计本想这张图片能够充满终端的屏幕),因为图片像素小于物理像素个数。

    QVGA屏:对比原图,正好撑满屏宽

    image

    HVGA屏:对比原图,虽然设置成了wrap_content,但是Android还是对其进行了放大操作

    image

    WVGA:同上

    image

    总结:

    1:只要对一种屏幕设计一套UI,Android总能通过放大或者缩小来适应屏幕来保证设计意图,这个前提是布局里的单位是dip

    2:虽然Android总能够很聪明地来保证你的设计意图,但是实际工作中,最好还是要多设计几套UI,因为像实验2里,放大后的图片明显要模糊了许多

    3:如果考虑设计的工作量,只想设计一套UI,那最好也是针对大分辨率的终端来设计,这样图片缩小比图片放大的清晰度要好。

    实验3:

    上面的图片都是从drawable文件夹里获取的,所以Android知道将图片当成什么密度来处理(放大或缩小),如果图片是从data/data目录或者sd卡上获取的,Android会默认将图片当作160的基准密度来处理,将实验二的图片放入data/data/cn.com/目录下,并获取设置到ImageView上,我们的本意是图片充满终端宽度,结果会是什么样呢?结果如下:

    复制代码
     1 public class Test4Activity extends Activity {
     2     private ImageView iv;
     3     @Override
     4     public void onCreate(Bundle savedInstanceState) {
     5         super.onCreate(savedInstanceState);
     6         setContentView(R.layout.main);
     7          
     8         iv = (ImageView)findViewById(R.id.image);
     9          
    10         iv.setImageDrawable(new BitmapDrawable(BitmapFactory.decodeFile("data/data/cn.com/density-240-320.png")));
    11          
    12 }

    HVGA屏下:

    image

    结果:图片并没有如愿地充满屏宽,原因就是Android将图片的密度当作了160基准密度来处理,而终端屏密度又正好是160,密度相同图片并不会进行缩放操作,所以终端会以一个物理像素来显示一个图片像素。所以宽240像素的图片当然无法充满宽320像素的屏。

    处理:希望从非drawable目录下获取的图片,Android也能帮助我们进行智能地缩放,那就要告诉Android这张图片采用什么密度来处理。具体如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    public class Test4Activity extends Activity {
        private ImageView iv;
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.main);
            
            iv = (ImageView)findViewById(R.id.image);
            
            BitmapFactory.Options options = new BitmapFactory.Options(); 
        //设置工厂以120的密度来读取图片文件
             options.inDensity = 120;
            BitmapDrawable image = new BitmapDrawable(BitmapFactory.decodeFile("data/data/cn.com/density-240-320.png",options));
            //设置图片在终端中以终端本身的密度来显示图片
             image.setTargetDensity(getApplicationContext().getResources().getDisplayMetrics());
            iv.setImageDrawable(image);
    }

      

    最终效果:

    HVGA屏下:

    image

    结束:Android中长度单位最好使用与物理像素无关的逻辑像素dip,这样Android就能帮助我们自动适应不同的屏幕,保持设计意图。

    Android中图片大小的转换是基于密度,与屏幕分辨率和尺寸大小无关.

     

     

    写在前面的话:作为一个android开发入门小生,往往关注的只是android组件怎么使用,功能如何实现,而且最想的是在自己的手机上跑起来自己设计、开发的应用向周围的人显摆(其实笔者是在说自己)。所以我们往往忽略了我们的应用在别人的手机上实际的效果。所以理解如何编写一个屏幕适配性高的应用,是从一个小作坊式应用开发者向真正商业化应用软件开发者晋升所必须具备的素质。

    参考文档是最好的老师,所以为了深入理解android平台屏幕适配方案,我们必须从android关于这一块的参考文档入手。

    概述——android应用如进行屏幕尺寸和分辨率适配

    本章内容参考自android开发文档” SupportingMultiple Screens”

    一、屏幕特征介绍。

             1、屏幕尺寸(Screen size)

    卖场中商家向客户介绍设备屏幕大小的时候一般用的都是“寸(inch)”这个单位,

    android平台上将“寸(inch)”描述为”Screen size”。Android系统根据Screen size概念性地分类为Small、normal、large、xlarge几个级别。

             2、屏幕密度 (Screen density)

             当然作为一个标准的it人士,当然不会被单纯的Screensize大小忽悠住。我们往往关心的是屏幕的分辨率(Resolution),亦即真正决定了屏幕显示画面细腻程度的参数。苹果newPad即是凭借其大力宣扬的视网膜屏着实又火了一把,其关键所在就是苹果在newPad的9.7inch屏幕规格上使用的是高达2048x1536分辨率的屏幕,从画质细腻程度上说足比同样为9.7inch大小的iPad2的1024x768分辨率的屏幕高了4倍。

             我们一般以density(dpi)来描述一个屏幕的像素密度,在Android系统中根据dpi大小概念性的将屏幕像素密度指标分为ldpi、mdpi、hdpi、xhdpi几类。基本划分了屏幕像素密度的大小。

             如果读者注意到了,以上对size和density的分类都是概念性的分类,这里的意思就是并不是完全绝对的划分。之所以这么划分,是因为市面上现有手机中,往往很容易发现两台手机在屏幕尺寸,宽高比上有非常细微的差别;类似的也会有两台手机之间的像素密度有非常细微的差别(不同厂家生产的屏)。为了使产品的UI设计能够尽量忽视size和density的这些细微的差距,google做出了这样的“大概”的分类。

             从图1中可以看到屏幕尺寸和分辨率在android平台上的划分,每个相邻分类之间都是有重叠区域的。基于此,UI设计师们只需要考虑应用在不同generalized size/density上的适配性就行了。大大减少了产品的设计成本。

    图1、屏幕尺寸以及像素密度分类 1

            

             3、Density-independent pixel(dip)

             由于上面两点屏幕特性的存在,我们在考虑屏幕适配方案的时候往往因为需要在实际尺寸(size)和像素密度(density)两个维度的方向考虑从而导致异常头疼。基于此点,google设计了一个单位用于在进行应用的UI设计时能够只在一个维度上考虑。他就是dip。

             dip这个单位其实与像素类似,都可以理解为组成画面的最小颗粒度的单位。就是这个单位使我们彻底摆脱了在设计界面时在考虑屏幕分辨率大小的同时还需要考虑屏幕dpi的繁琐思路。之所以这么说是因为android手机通过在不同dpi的手机上,自动转换一个单位dip所代表的像素值,即dipi越大的手机,一个dip所代表的dx数量越多。如下图。


             在此我们先不介绍dip的具体用法,只提供手机屏幕分辨率与dip值的换算方式作为参考:

    dx = dip (dpi / 160)

             注:第一台安卓手机the T-Mobile G1 的dpi即为160。


     

    之前我们谈到在根据不同分辨率设计ui的时候往往还需要考虑到不同设备的dpi,这是什么原因呢。下面有一个简单的例子。

    如果一个应用没有考虑dpi直接在界面上放上了一个32*32像素(px)单位大小的按钮,那么在同样是3.7寸,但是像素分别为480*320、600*480、800*600的三台手机上展示效果将是怎样的呢。大家思考一会过后,可以看到下图的展示效果。


    图2、同为32px*32px的按钮在三台物理尺寸相同、分辨率不同的手机上的展示效

    我们从图2中清晰可见,从视觉上来看,你的整体布局明显跟设计不符了。这是因为由于dpi的增大,一个px对应在设备上的物理长度减小,导致最终展现的界面组件大小越小。

    什么样的屏幕适配方案可以解决这个问题?

    用dip作为界面组件的尺寸标注单位。

    如果我们将控件的尺寸定义为30dip*32dip,那么这个界面最终在屏幕上的显示效果将是如图3所示:


    图3、32dip*32dip的图片在不同机器界面上的展示效果

             有此可见通过dip单位的引入,我们在进行UI设计的时候就只需要考虑不同屏幕物理大小情况下的界面布局了(屏幕适配的真正目的也正是如此)。

             正因为有了这样的便利,为了标准化UI设计的目标,google对screen size的真正分类方法其实有确切的分类标准:

    1)  xlarge,≥960dip x 720dip

    2)  large, 大于等于640dip x 480dip

    3)  normal 大雨等于 470dip x 320dip

    4)  small 大于等于 426dip x 320 dip

    5)  

    二、屏幕适配的基本过程。

    在android平台上进行分辨率适配,google提供了一套流程共开发者参考:

    1、  声明你的应用兼容的屏幕尺寸。

    通过在manifest文件中包含<support-screens>段来声明你的应用兼容的屏幕尺寸。更多内容可见相关开发文档

    这一步一个显著的作用是让设备判断是否支持用屏幕兼容性模式(ScreenCompatibilityMode)来运行你的应用。以及可以

    2、  为不同的屏幕尺寸提供不同的布局。

    这一步的作用很容易理解,同一个应用,在10.1寸的平板电脑上或者7寸的大屏幕手机上,相较于3.0寸的小屏幕手机,往往需要展示更多的信息,或者更易于浏览和操作的界面排版方式。而更小屏幕的手机上需要更精干的界面。

    3、  为不同的dpi提供不同的图片资源。

    回到上一节我们的例子。如图3,如果我们使用dip标记一个组件的大小,那么系统绘制图片资源时,会将以像素尺寸度量大小的图片资源做缩放操作来最终适应屏幕中组件的大小。这样当缩放比例到达一定程度时,会出现图片资源显示效果模糊、边沿产生毛刺、圆角变形等特点。为了尽量保证手机上最终展示效果符合UI设计的效果,建议针对不同dpi准备不同的图片资源。

    如果你为不同dpi设备准备了不同的图片资源,android参考文档中建议以3:4:6:8的比例来依次做ldpi,mdpi,hdpi和xhdpi上的资源。笔者认为google这么做是考虑到了不同dpi之间dip单位代表的px值。如果读者想要了解跟多图标资源制作的信息可参考相关开发文档“IconDesign Guidelines”。

    除了给不同dpi准备不同的图片资源以外,android中还有一种方法可以避免由于过度缩放导致的图片失真的结果。那就是使用.9.png格式的图片,.9.png格式的图片简单来说就是一种可以自定义横向、纵向拉伸区域的图片,从而在对图片进行缩放操作的时候,android系统不会将它整体的缩放,而是将定义的拉伸区域横向,纵向拉伸。相关.9.png格式的介绍读者可自行参考某度、某歌和android开发文档。值得一提的是.9.png格式的图片有两种生成方式,即可以在应用运行过程中在代码中通过加载原图片资源同时增加拉伸的相关信息来构造.9.png格式的图片;也可以使用jdk或者android sdk中的工具先生成.9.png格式的图片,再拷贝到工程目录的对应资源文件夹下。

    三、android系统如何加载不同的资源文件。

    Android系统资源文件具有一个特性,该特性支持我们提供可更改的资源(provide alternative resources)。该特性的主要目的就是为了使应用兼容不同设备的不同配置信息,显然,不同屏幕也算在内。

    具体做法是:

    1、在资源文件夹中新建以<resources_name>-<config_qualifier>格式命名的资源文件夹。

    其中resources_name代表的是android资源文件夹所定义的layout/drawable/values等类型文件夹;config-qualifier代表的是配置限定符,表示该文件夹中的资源文件,允许加载的配置要求,如某写string信息需要在英文系统中使用你可以新建res/values-en/strings.xml文件,这样系统就会在英文系统中,优先使用values-en下的资源。

    2、将不同配置下使用的资源文件放在不同的资源文件夹中。

    在使用这个特性的时候应该注意以下事项:

    1、不要随意删除没有增加config-qualifier的文件夹。在android资源文件夹中,这样的文件夹即是默认文件夹,当添加了config_qualfier的文件夹中没有能够匹配当前设备配置的限定符时,将会从默认德尔文件夹中加载资源。所以如果当系统需要从默认文件夹中加载资源而又找不到资源的时候应用就会崩溃(当然如果你目的就是如此,那么大可这样做)。

    2、设备的一些配置是会不断变化的(例如横竖屏切换),所以如果你增加了配置限定符的限定符在加载使用后,设备配置发生了变化,理应当根据新的变化从新加载。关于如何动态获取设备配置的变化,读者可自行参考开发文档“Handing Runtime Changes”。

    3、如果有相同资源想在不同配置下使用,请不好很落伍的将资源拷贝然后放在增加了不同限定符的文件夹下。这不仅会显得你很不fashion,也会浪费存储空间。Android提供了方法可在不同文件夹下通过xml配置文件指定到默认文件夹中的一个资源文件,所以你只需要一份资源文件放在默认文件夹中再在不同限定符的文件夹中通过编写xml配置文件来指定你用的是哪个资源文件。

    4、如果想用好该特性,请读者仔细阅读相关开发文档“Providing Resources”。避免各种不fashion的做法。

     

     

     

    图像显示所需的概念:

    像素:一幅图片的显示就是由许多显示着不同颜色的小方格组成的,这样的小方格就被称为像素,是构成图片的最小单位.但是这个像素的具体大小是多少呢?这需要取决于显示这张图片的具体的物理设备显示一个图片像素点的荧光点的大小.图片文件只是记录着它自身有多少个像素点,每个像素点显示什么颜色,至于它自身物理尺寸有多大,它自身也无法得知,例如一张480*800像素的图片在电脑显示器上显示明显要比在手机屏幕上显示大很多,而这张图片本身并没有改变.只是手机的屏比电脑显示器的屏要精细许多,也就是每一个物理像素点要小许多,密度也就大许多.

    分辨率:分为“图像分辨率”与“物理显示分辨率”。它们都是水平像素点数与垂直像素点数的乘积,也就是像素总和数。图像分辨率是指图片文件记录着自身所有的像素数。物理显示分辨率是指物理显示屏水平与垂直能显示的像素数的乘积.有的人说分辨率越高,显示的图片就越清晰,这点是针对物理尺寸相同的情况而言,因为一样的分辨率有可能尺寸可能不一样大,这样光就分辨率来比较清晰度没有可比性.

    密度:就是物理设备上单位尺寸里的像素数,当然是密度越大图片显示就越清晰了.

    Android系统的长度单位:

    px:像素,终端上的一个物理像素点,例如,480*800的屏幕在横向有320个象素,在纵向有480个象素。

    dip(dp):与终端上的物理像素点无关,是一种基于屏幕密度抽象单位,被称作“设备独立像素”,会随着屏幕的密度进行自动的大小调整.

    sp:比例像素,主要处理字体大小,可以根据用户的字体大小首选项进行缩放.

    in:英寸,标准长度单位

    mm:毫米,标准长度单位

    pt:磅,标准长度单位,1/72英寸

    Android屏幕适配:

    为什么要适配:Android终端的物理尺寸,分辨率的类别众多,可能你为一种终端设计了一套UI符合要求,但是在另一类大小的物理终端上显示就完全不是你想要的。

    解决办法:针对这种情况,Android提供了一套标准,它将屏幕分为三类,所以当你新建一个Android工程后,工程自动为你创建三个存放不同分辨率,不同密度下的UI的文件夹,如下图:

    drawable

    三个文件夹下的UI对应不同分辨率及密度的屏幕,对应关系如下:

     

            屏幕类型     分辨率       密度     尺寸
    drawable-hdpi        WVGA    480*800       240     大
    drawable-ldpi        QVGA    240*320       120     小
    drawable-mdpi        HVGA    320*480       160     中

    这三个文件夹只是告诉Android你设计的UI是针对哪种屏幕分辨率及密度进行设计的,Android终端会在打开应用的时候自动根据终端类型去匹配与文件夹里提供的UI相近分辨率及密度的图片。

    假设终端屏是WVGA类型的,也就是密度为240,那么程序打开时,会去提取drawable-hdpi文件夹下图片,然后用一个物理像素去显示一个图片像素,因为你把图片是放在drawable-hdpi文件夹下的,也就是告诉Android你设计的UI是针对480*800,密度为240而言的,那么它不会对图片进行放大或缩小操作,正好用一个物理像素去显示一个图片像素。

    当图片的密度与屏幕密度相同,则不进行缩放.

    当图片的密度与屏幕密度不同,则进行缩放。

    实验1:

    针对480*800的屏我设计了一张图片(分辨率是480*100),分别在不同物理分辨率终端下进行显示,看看Android会不会显示不正常。图片如下:

    density

    布局文件:

    复制代码
    <?xml version="1.0" encoding="utf-8"?>
     2<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     3     android:orientation="vertical"
     4     android:layout_width="fill_parent"
     5     android:layout_height="fill_parent"
     6     android:gravity="center"
     7     >
     8      
     9     <ImageView
    10         android:layout_width="wrap_content"
    11         android:layout_height="wrap_content"
    12         android:src="@drawable/density480800"/>
    13      
    14 </LinearLayout>
    复制代码

    假设:这张图片的宽是480像素,高为100像素,我将它放在drawable-hdpi文件夹下,就是告诉Android我的设计意图是针对480*800的Android终端而言的,那么它会在480*800的终端上以一个物理像素去显示一个图片像素,如果将它放在分辨率为240*320的终端上进行显示,如果Android不对显示进行处理的话,还是以一个物理像素去显示一个图片像素,终端必将显示不下这张图片,因为图片像素超过了物理像素个数。

    注意:我这里只设计了一张图片,并放置于drawable-hdpi文件夹下,并没有在其它文件夹下放置相应的图片,当Android进行匹配的时候也就只能匹配这张图了。

    结果:

    WVGA屏:对比原图,正好显示下,大小相同

    image

    HVGA屏:对比原图,能够显示下,对图片进行了缩小操作.

    image

    QVGA屏:对比原图,能够显示下,对图片进行了缩小操作

    image

    实验2:

    针对240*320的屏我设计了一张图片(分辨率为:240*100),分别在不同物理分辨率终端下进行显示,看看Android会不会显示不正常。图片如下:

    density-240-320

    布局文件:

    复制代码
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:gravity="center"
        >
         
        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/density240320"/>
         
    </LinearLayout>
    复制代码

    这张图就是专门为240*320的屏设计的,设计意图就是图片撑满屏幕宽度,所以设置成wrap_content就够了,没有考虑其它屏的情况

    假设:这张图片的宽是240像素,高为100像素,我将它放在drawable-ldpi文件夹下,就是告诉Android我的设计意图是针对240*320密度为120的Android终端而言的,那么它会在240*320的终端上以一个物理像素去显示一个图片像素,如果将它放在分辨率为480*800的终端上进行显示,如果Android不对显示进行处理的话,还是以一个物理像素去显示一个图片像素,图片必将偏离了设计意图(设计本想这张图片能够充满终端的屏幕),因为图片像素小于物理像素个数。

    QVGA屏:对比原图,正好撑满屏宽

    image

    HVGA屏:对比原图,虽然设置成了wrap_content,但是Android还是对其进行了放大操作

    image

    WVGA:同上

    image

    总结:

    1:只要对一种屏幕设计一套UI,Android总能通过放大或者缩小来适应屏幕来保证设计意图,这个前提是布局里的单位是dip

    2:虽然Android总能够很聪明地来保证你的设计意图,但是实际工作中,最好还是要多设计几套UI,因为像实验2里,放大后的图片明显要模糊了许多

    3:如果考虑设计的工作量,只想设计一套UI,那最好也是针对大分辨率的终端来设计,这样图片缩小比图片放大的清晰度要好。

    实验3:

    上面的图片都是从drawable文件夹里获取的,所以Android知道将图片当成什么密度来处理(放大或缩小),如果图片是从data/data目录或者sd卡上获取的,Android会默认将图片当作160的基准密度来处理,将实验二的图片放入data/data/cn.com/目录下,并获取设置到ImageView上,我们的本意是图片充满终端宽度,结果会是什么样呢?结果如下:

    复制代码
     1 public class Test4Activity extends Activity {
     2     private ImageView iv;
     3     @Override
     4     public void onCreate(Bundle savedInstanceState) {
     5         super.onCreate(savedInstanceState);
     6         setContentView(R.layout.main);
     7          
     8         iv = (ImageView)findViewById(R.id.image);
     9          
    10         iv.setImageDrawable(new BitmapDrawable(BitmapFactory.decodeFile("data/data/cn.com/density-240-320.png")));
    11          
    12 }
    复制代码

    HVGA屏下:

    image

    结果:图片并没有如愿地充满屏宽,原因就是Android将图片的密度当作了160基准密度来处理,而终端屏密度又正好是160,密度相同图片并不会进行缩放操作,所以终端会以一个物理像素来显示一个图片像素。所以宽240像素的图片当然无法充满宽320像素的屏。

    处理:希望从非drawable目录下获取的图片,Android也能帮助我们进行智能地缩放,那就要告诉Android这张图片采用什么密度来处理。具体如下:

    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    public class Test4Activity extends Activity {
        private ImageView iv;
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super .onCreate(savedInstanceState);
            setContentView(R.layout.main);
            
            iv = (ImageView)findViewById(R.id.image);
            
            BitmapFactory.Options options = new BitmapFactory.Options();  
        //设置工厂以120的密度来读取图片文件
             options.inDensity = 120 ;
            BitmapDrawable image = new BitmapDrawable(BitmapFactory.decodeFile( "data/data/cn.com/density-240-320.png" ,options));
            //设置图片在终端中以终端本身的密度来显示图片
             image.setTargetDensity(getApplicationContext().getResources().getDisplayMetrics());
            iv.setImageDrawable(image);
    }

      

    最终效果:

    HVGA屏下:

    image

    结束:Android中长度单位最好使用与物理像素无关的逻辑像素dip,这样Android就能帮助我们自动适应不同的屏幕,保持设计意图。

    Android中图片大小的转换是基于密度,与屏幕分辨率和尺寸大小无关.

     

    • 0
      点赞
    • 1
      收藏
      觉得还不错? 一键收藏
    • 0
      评论
    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

    当前余额3.43前往充值 >
    需支付:10.00
    成就一亿技术人!
    领取后你会自动成为博主和红包主的粉丝 规则
    hope_wisdom
    发出的红包
    实付
    使用余额支付
    点击重新获取
    扫码支付
    钱包余额 0

    抵扣说明:

    1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
    2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

    余额充值