Android开发屏幕适配

重要概念

屏幕尺寸

屏幕尺寸指屏幕的对角线的长度,单位是英寸,1英寸=2.54厘米,比如常见的屏幕尺寸有2.4、2.8、3.5、3.7、4.2、5.0、5.5、6.0等;

屏幕分辨率(Resolution)

屏幕分辨率是指在横纵向上的像素点数,单位是px,1px=1个像素点。一般以纵向像素*横向像素,如1920*1080,1280*720,整个屏幕的像素数目,为了表示方便一般用屏幕的像素宽度(水平像素数目)乘以像素高度表示,形如1280x720,反之分辨率为1280x720的屏幕,像素宽度不一定为1280。在Android开发中,我们经常讲到的分辨率为输出分辨率,也可以成为打印分辨率,与之相对的就是显示分辨率。

DPI(dot per inch)

DPI是指每英寸上的像素点数,单位是dpi,即“dot per inch”的缩写。dpi=(√(横向分辨率^2+纵向分辨率^2))/屏幕尺寸),屏幕像素密度与屏幕尺寸和屏幕分辨率有关,在单一变化条件下,屏幕尺寸越小、分辨率越高,像素密度越大,反之越小,此单位可以反映输出或者显示的清晰度,通常,我们都使用dpi来作为扫描器和打印机的解析度单位,数值越高表示解析度越高。

PPI(pixels per inch)

ppi跟dpi一样,所表示的是每英寸所拥有的像素(pixel)数目,对于手机屏幕来说,屏幕尺寸是固定的,分辨率一般是不可以调节的。所以ppi是一个定值。此值越高显示越细腻。计算了一下小米手机屏幕的PPI,4.0英寸、分辨率854X480,PI(DPI)=√(854^2+480^2)/4=244.912……≈245.

屏幕密度(densityDpi)

屏幕密度(densityDpi)表示单位(英寸)面积内的像素个数,通常用dpi为单位,即每英寸多少个像素点,即是android系统为不同屏幕尺寸,分辨率提供统一单位(dip)的一个可变参数。该值是设备自己决定的,一般不可变,一般由手机厂商或Android TV厂商在生产时就已确定的,目前该值对应的density(density=densityDpi/160)有:0.75(densityDpi=120),1.0(densityDpi=160),1.33(densityDpi=213),1.5(densityDpi=240),2.0(densityDpi=320),3.0(densityDpi=480)(PS:所列数值仅限于本作者目前开发所见),densityDpi的改变不会改变手机的分辨率,但它的改变会影响显示效果。有的android软件说是改变分辨率其实是改变densityDpi大小。density、densityDpi的换算关系如下:density=densityDpi/160;

屏幕密度(Denstity)

谷歌定义 densityDpi=160为基准。density=densityDpi/标准dpi(160),dp(dip,即设备无关像素)与px(像素)之间的转换则由density值决定。px、dip(dp)、density、densityDpi有如下的换算关系:dip=px/density,px=dp*density,px=(desityDpi/160)*dip,dip=px/(densityDpi/160);
像素密度(density)实际上是以单位英寸160个像素作为参考标准,主要密度有0.75,1,1.5和2,当密度为2时就表示1英寸有320个像素,Android中通过代码可以获取到屏幕的像素值和密度,根据这些值就可以反向算出屏幕的物理尺寸 :
屏幕尺寸=屏幕对角线的像素值/(密度*160)= [(长的平方+宽的平方)开根号] / (密度*160)

dp、dip、dpi、sp、px、pt

px(pixel)我们应该是比较熟悉的,前面的分辨率就是用的像素为单位,大多数情况下,比如UI设计、Android原生API都会以px作为统一的计量单位,像是获取屏幕宽高等。
对于计算机的屏幕设备而言,像素(Pixel)或者说px是一个最基本的单位,就是一个点。其它所有的单位,都和像素成一个固定的比例换算关系。所有的长度单位基于屏幕进行显示的时候,都统一先换算成为像素的多少,然后进行显示。所以,就计算机的屏幕而言,相对长度和绝对长度没有本质差别。任何单位其实都是像素,差别只是比例不同。
如果把讨论扩展到其它输出设备,比如打印机,基本的长度单位可能不是像素,而是其它的和生活中的度量单位一致的单位了。

dip和dp是一个意思,都是Device Independent Pixels(Density Independent Pixels)的缩写,即设备独立像素,也可以成为屏幕无关像素,表示实际像素大小与具体屏幕密度无关,上面我们说过,dpi是屏幕像素密度,假如一英寸里面有160个像素,这个屏幕的像素密度就是160dpi,那么在这种情况下,dp和px如何换算呢?在Android中,规定以160dpi为基准,1dip=1px,如果密度是320dpi,则1dip=2px,以此类推。
那么既然dpi是自适应屏幕密度的,与px之间又是如何换算呢:
120dpi(ldpi低密度屏)   1dp = 0.75px
(由于像素点是物理点,所以用2个像素点来显示3个dp的内容)
160dpi(mdpi中密度屏)   1dp = 1px
213dpi(tvdpi电视密度屏)  1dp = 1.33px
240dpi(hdpi高密度屏)   1dp = 1.5px
320dpi(xhdpi极高密度屏)  1dp = 2px
假如同样都是画一条320px的线,在480*800分辨率手机上显示为2/3屏幕宽度,在320*480的手机上则占满了全屏,如果使用dp为单位,在这两种分辨率下,160dp都显示为屏幕一般的长度。这也是为什么在Android开发中,写布局的时候要尽量使用dp而不是px的原因。

而sp,即scale-independent pixels(放大独立像素),也可以成为刻度无关像素,主要用于字体显示。与dp类似,但是可以根据文字大小首选项进行放缩,是设置字体大小的御用单位。
由上述分析结果可知,控件使用dp,文字使用sp即可满足自适应的需求。

pt:point,是一个标准的长度单位,1pt=1/72英寸,用于印刷业。

mdpi、hdpi、xdpi、xxdpi

其实之前还有个ldpi,但是随着移动设备配置的不断升级,这个像素密度的设备已经很罕见了,所在现在适配时不需考虑。

mdpi、hdpi、xdpi、xxdpi用来修饰Android中的drawable文件夹及values文件夹,用来区分不同像素密度下的图片和dimen值。

那么如何区分呢?Google官方指定按照下列标准进行区分:
这里写图片描述

屏幕大小自适应

界面布局方面

需要根据物理尺寸的大小准备5套布局,layout(放一些通用布局xml文件,比如界面中顶部和底部的布局,不会随着屏幕大小变化,类似windos窗口的title bar),layout-small(屏幕尺寸小于3英寸左右的布局),layout-normal(屏幕尺寸小于4.5英寸左右),layout-large(4英寸-7英寸之间),layout-xlarge(7-10英寸之间)

图片资源方面

需要根据dpi值准备5套图片资源,drawable,drawalbe-ldpi,drawable-mdpi,drawable-hdpi,drawable-xhdpi
Android有个自动匹配机制去选择对应的布局和图片资源
手机的分辨率 像素 密度和 屏幕尺寸是我们经常听到的字眼,它们之间的关系如下:
分辨率是指屏幕上有横竖各有多少个容器点,每个容器点是用于容纳一个像素的。
像素严格来说是指用于图片上的,图片上的像素点越多,图片就越显的清晰,当然也会越大。
手机可以有相同的分辨率,但屏幕尺寸可以不相同,例如3.7英寸的屏幕可以是320*480的分辨率,5.1英寸的屏幕也可以是320*480的分辨率,那这2个屏幕在显示时有什么区别呢?
举个例子:如果A手机屏幕宽度是5CM,B手机宽度是10Cm,宽度分辨率都是320,那么显示同一张图片时,B手机上的图片就会显的大一点,并且没有A手机上的清晰。为什么呢,这就是我们常见的密度(DPI)概念,即每英寸多少个点。本身一行5CM有320个点,DPI大约是150,现在是一行10CM有320个点,密度大约是75。密度大为减少,所以B手机显示的图片会变大,不清晰。就像我们使用Android Screen Monitor在电脑上显示手机屏幕一样,总感觉在电脑上显示时有点大,这就是因为你电脑的分辨率密度没有手机的分辨率密度大导致的,可以把电脑的分辨率调大一点让电脑屏幕密度和手机密度一致。
电脑显示器的解析度约为72dpi,这个数值其实是这样计算出来的:以一部15寸的电脑显示器为例,可视面积的水平长度大约为11.2寸,如果显示模式是800×600,那么解析度就是800/11.2=71.4。如果是17寸电脑显示器,以1024×768的显示模式来看,解析度就变成1024/12.8=80了。
最近做项目发现,同样的分辨率2台机器,某个界面的样式就是不一样,经过多次测试发现,原来第一台pad(lenovo A1_07)的密度为1.5,转换成dpi是240,属于高密度;另一台pad的密度为1,属于中等密度,转换成dpi就是160,应该属于中等密度
这就发现问题了, 解决方法:
应该在values文件夹下面分别放置values-hdpi, values-mdpi分别对应的样式,就OK了

实践过程:
由于Android设备中获取的密度density本身是个约等于的数值,比如计算出密度的准确值density=1.575,实际在代码中读到的density=1.5, 需要实现一个简单的计算屏幕尺寸的应用在不同设备上验证物理尺寸准确程度,具体代码如下:

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)
double diagonalPixels = Math.sqrt(Math.pow(width, 2)+Math.pow(height, 2)) ;
double screenSize = diagonalPixels/(160*density) ;

有可能上面的计算屏幕的尺寸不是太准确
一些有用的代码:

mport android.content.Context;
import android.util.DisplayMetrics;
/**
 * 计算公式 pixels = dips * (density / 160)
 * 
 * @version 1.0.1 2010-12-11
 * 
 * @author
 */
public class DensityUtil {

    private static final String TAG = DensityUtil.class.getSimpleName();

    // 当前屏幕的densityDpi
    private static float dmDensityDpi = 0.0f;
    private static DisplayMetrics dm;
    private static float scale = 0.0f;
    /**
     * 
     * 根据构造函数获得当前手机的屏幕系数
     * 
     * */
    public DensityUtil(Context context) {
        // 获取当前屏幕
        dm = new DisplayMetrics();
        dm = context.getApplicationContext().getResources().getDisplayMetrics();
        // 设置DensityDpi
        setDmDensityDpi(dm.densityDpi);
        // 密度因子
        scale = getDmDensityDpi() / 160;
        Logger.i(TAG, toString());
    }
    /**
     * 当前屏幕的density因子
     * 
     * @param DmDensity
     * @retrun DmDensity Getter
     * */
    public static float getDmDensityDpi() {
        return dmDensityDpi;
    }
    /**
     * 当前屏幕的density因子
     * 
     * @param DmDensity
     * @retrun DmDensity Setter
     * */
    public static void setDmDensityDpi(float dmDensityDpi) {
        DensityUtil.dmDensityDpi = dmDensityDpi;
    }
    /**
     * 密度转换像素
     * */
    public static int dip2px(float dipValue) {

        return (int) (dipValue * scale + 0.5f);
    }
    /**
     * 像素转换密度
     * */
    public int px2dip(float pxValue) {
        return (int) (pxValue / scale + 0.5f);
    }
    @Override
    public String toString() {
        return " dmDensityDpi:" + dmDensityDpi;
    }
}

AndroidManifest.xml设置

android:anyDensity=”true”时,应用程序安装在不同密度的终端上时,程序会分别加载xxhdpi、xhdpi、hdpi、mdpi、ldpi文件夹中的资源。
相反,如果设为false,即使在文件夹下拥有相同资源,应用不会自动地去相应文件夹下寻找资源:
1) 如果drawable-hdpi、drawable-mdpi、drawable-ldpi三个文件夹中有同一张图片资源的不同密度表示,那么系统会去加载drawable_mdpi文件夹中的资源;
2) 如果drawable-hpdi中有高密度图片,其它两个文件夹中没有对应图片资源,那么系统会去加载drawable-hdpi中的资源,其他同理;
3) 如果drawable-hdpi,drawable-mdpi中有图片资源,drawable-ldpi中没有,系统会加载drawable-mdpi中的资源,其他同理,使用最接近的密度级别。

横屏竖屏目录区分

drawable

a) drawable-hdpi该图片既适用于横屏,也适用于竖屏;
b) drawable-land-hdpi,当屏幕为横屏,且为高密度时,加载此文件夹的资源;
c) drawable-port-hdpi,当屏幕为竖屏,且为高密度时,加载此文件夹中的资源。其他同理。

layout

在res目录下建立layout-port和layout-land两个目录,里面分别放置竖屏和横屏两种布局文件,以适应对横屏竖屏自动切换。

多屏适配原则

1) 在layout文件中设置控件尺寸时应采用fill_parent、wrap_content、match_parent和dp;
具体来说,设置view的属性android:layout_width和android:layout_height的值时,wrap_content,match_parent或dp比px更好,文字大小应该使用sp来定义。
2) 在程序的代码中不要出现具体的像素值,在dimens.xml中定义;
为了使代码简单,android内部使用pix为单位表示控件的尺寸,但这是基于当前屏幕基础上的。为了适应多种屏幕,android建议开发者不要使用具体的像素来表示控件尺寸。
3) 不使用AbsoluteLayout(android1.5已废弃) ,可以使用RelativeLayout替代;
4) 对不同的屏幕提供合适大小的图片。
不同大小屏幕用不同大小的图片,low:medium:high:extra-high图片大小的比例为3:4:6:8;举例来说,对于中等密度(medium)的屏幕你的图片像素大小为48×48,那么低密度(low)屏幕的图片大小应为36×36,高(high)的为72×72,extra-high为96×96。

9-patch图片

在android中,不仅可以使用.png、.jpg、.gif的普通图片作为图片资源,而且可以将扩展名为.9.png的9-patch图片作为图片资源。android的SDK目录tools下的draw9patch工具可以生成一个可以伸缩的标准png图像,该图像的扩展名为.9.png。9-patch图片通常用作背景,与普通图片不同的是,使用9-patch图片作为屏幕或者按钮背景时,当屏幕尺寸或按钮大小改变时,图片可自动缩放,达到不失真效果。
9-patch PNG图片,在原生PNG图片四周空出一个像素间隔,用来标识PNG图片中哪些部分可以拉伸、哪些不可以拉伸、背景上的边框位置等。

不同layout

怎样才能让Application自动适应不同的屏幕呢?
其实很简单,只需要在res目录下创建不同的layout文件夹,比如layout-640x360、layout-800x480等,所有的layout文件在编译之后都会写入R.java里,而系统会根据屏幕的大小自己选择合适的layout进行使用。

Android应用资源加载机制

UI界面在不同平台的适配受屏幕尺寸和屏幕密度影响,Android适配机制就是在资源后面添加对这两种因素的限定,通过不同的限定区分不同的平台资源,Android在使用资源的时候会优先选择满足本平台限定的资源,再找最接近条件的,再找默认(即不加限定),通过选择适合当前平台的资源来完成不同平台的适配。
屏幕尺寸分为:small,normal,large,xlarge分别表示小,中,大,超大屏;
屏幕密度分为:ldpi,mdpi,hdpi,xhdpi,它们的标准值分别是:120dpi,160dpi,240dpi,320dpi;
以上划分均表示的是一个范围:

  • 当屏幕密度densityDpi<=120时,使用ldpi标签的资源;(density<=0.75),对应的图片资源文件夹为(drawable-ldpi),布局资源文件夹为(layout-ldpi),value资源文件夹(values-ldpi);
  • 当屏幕密度120~densityDpi<=160时,使用mdpi标签的资源;(0.75~density<=1.0),对应的图片资源文件夹为(drawable-mdpi),布局资源文件夹为(layout-mdpi),value资源文件夹(values-mdpi);
  • 当屏幕密度160~densityDpi<=240时,使用hdpi标签的资源;(1.0~density<=1.5),对应的图片资源文件夹为(drawable-hdpi),布局资源文件夹为(layout-hdpi),value资源文件夹(values-hdpi);
  • 当屏幕密度240~densityDpi<=320时,使用xhdpi标签的资源;(1.5~density<=2.0),对应的图片资源文件夹为(drawable-xhdpi),布局资源文件夹为(layout-xhdpi),value资源文件夹(values-xhdpi);
  • 当屏幕密度320~densityDpi<=480时,使用xxhdpi标签的资源;(2.0~density<=3.0),对应的图片资源文件夹为(drawable-xxhdpi),布局资源文件夹为(layout-xxhdpi),value资源文件夹(values-xxhdpi);
  • 不加任何标签的资源是各种分辨率情况下共用的,如果对应的densityDPi不存在对应的资源文件夹,Android应用会从高到低依次查找响应资源进行匹配,直至匹配成功为止;

另外

也可在Android工程中建立诸如drawable-1920x1080,layout-1920x1080,values-1920x1080,drawable-1080x720,layout-1080x720,values-1080x720这样的文件夹,系统会优先匹配符合该分辨率下的响应文件夹下的资源,如果匹配失败,再从高到低匹配响应资源;所有的l资源文件在编译之后都会写入R.java里,而系统会根据屏幕的大小自己选择合适的资源进行使用。

注1:分辨率限定符的匹配是向下匹配,如果没有values-land-mdpi-1920x1080,无法适配到values-land-mdpi-1920x1080,那这样就可能适配到下一级,比如values-land-mdpi-1280x720,但是现在的Android TV的屏幕密度都在1~2之间,如果不做好精确地适配,会造成界面显示上的瑕疵。

  注2:由于分辨率限定符的匹配是向下匹配,所以如果有非主流mdpi屏幕不能精确适配到上述指定值时,values-mdpi至少可以保证app运行时不至于崩溃,同理values可以保证ldpi屏幕的Android TV不会因生成view而又取不到相应值而崩溃。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值