Android屏幕适配及DisplayMetrics解析

一、基本概念

dip       : Density independent pixels ,设备无关像素。
dp        :就是dip
px        : 像素
dpi       :dots per inch , 直接来说就是一英寸多少个像素点。常见取值 120,160,240。我一般称作像素密度,简称密度
density   : 直接翻译的话貌似叫 密度。常见取值 1.5 , 1.0 。和标准dpi的比例(160px/inc)
分辨率     : 横纵2个方向的像素点的数量,常见取值 480X800 ,320X480
屏幕尺寸: 屏幕对角线的长度。电脑电视同理。屏幕大小是手机对角线的物理尺寸,以英寸(inch)为单位。比如某某手机为“5寸大屏手机”,就是指对角线的尺寸,5寸×2.54厘米/寸=12.7厘米。
屏幕比例的问题。因为只确定了对角线长,2边长度还不一定。所以有了4:3、16:9这种,这样就可以算出屏幕边长了。

1、屏幕尺寸
屏幕尺寸指屏幕的对角线的长度,单位是英寸(in),1英寸=2.54厘米

2、px
是英文单词pixel的缩写,意为像素,屏幕上的点。我们通常所说的分辨率如480X800就是指的像素,一般以纵向像素*横向像素。

3、dpi
dpi是Dots Per Inch的缩写, 或PPI,pixels per inch。密度,每英寸点数,即每英寸包含像素个数。

从英文顾名思义,就是每英寸的像素点数,数值越高当然显示越细腻。假如我们知道一部手机的分辨率是1080×1920,屏幕大小是5英寸,你能否算出此屏幕的密度呢?哈哈,中学的勾股定理派上用场啦!通过宽1080和高1920,根据勾股定理,我们得出对角线的像素数大约是2203,那么用2203除以5就是此屏幕的密度了,计算结果是440。440dpi的屏幕已经相当细腻了。
在这里插入图片描述

4、density
屏幕密度,density和dpi的关系为 density = dpi/160

4、dp和dip
设备独立像素,device independent pixels的缩写,Android特有的单位,在屏幕密度dpi = 160屏幕上,1dp = 1px。dp和density的关系为 1dp = density px,dip值 =(dpi值/160)* pixel值

5、sp
和dp很类似,一般用来设置字体大小,和dp的区别是它可以根据用户的字体大小偏好来缩放。

6、 dp与px的转换

在安卓中,系统密度为160dpi的中密度手机屏幕为基准屏幕,即320×480的手机屏幕。在这个屏幕中,1dp=1px。

100dp在320×480(mdpi,160dpi)中是100px。那么100dp在480×800(hdpi,240dpi)的手机上是多少px呢?我们知道100dp在两个手机上看起来差不多大,根据160与240的比例关系,我们可以知道,在480×800中,100dp实际覆盖了150px。因此,如果你为mdpi手机提供了一张100px的图片,这张图片在hdpi手机上就会拉伸至150px,但是他们都是100dp。

中密度和高密度的缩放比例似乎可以不通过160dpi和240dpi计算,而通过320px和480px也可以算出。但是按照宽度计算缩放比例不适用于超高密度xhdpi和超超高密度xxhdpi了。即720×1280中1dp是多少px呢?如果用720/320,你会得出1dp=2.25px,实际这样算出来是不对的。dp与px的换算要以系统密度为准,720×1280的系统密度为320,320×480的系统密度为160,320/160=2,那么在720×1280中,1dp=2px。同理,在1080×1920中,1dp=3px。

大家可以记住下面这个比例,dp与px的换算就十分easy啦!

ldpi:mdpi:hdpi:xhdpi:xxhdpi=3:4:6:8:12,我们发现,相隔数字之间还是2倍的关系。计算的时候,以mdpi为基准。比如在720×1280(xhdpi)中,1dp等于多少px呢?mdpi是4,xhdpi是8,2倍的关系,即1dp=2px。反着计算更重要,比如你用PhotoShop在720×1280的画布中制作了界面效果图,两个元素的间距是20px,那要标注多少dp呢?2倍的关系,那就是10dp!

在这里插入图片描述

问题:
1、为什么我们需要推荐使用dp而不推荐使用px
不使用px的原因是由于不同的手机分辨率是不同的,相同的像素对应的尺寸是不同的。

推荐使用dp的原因是我们可以通过dp设置指定的尺寸,这个不受像素的影响,是像素无关的。从上面可以看出dp就是等于屏幕密度,从而可以得到1dp对应设备的像素,这样我们就可以设置真实的尺寸大小,这个大小跟像素是无关的。就是说dp能够让同一数值在不同的分辨率展示出大致相同的尺寸大小。

2、为什么dp还是不能解决屏幕适配的问题
dp可以用来设置指定尺寸,这个不受像素的影响,这样对不同的手机我们可以指定相同的尺寸,但还有一个问题就是市面上的手机尺寸并不是相同的,常见的屏幕尺寸有2.4、2.8、3.5、3.7、4.2、5.0、5.5、6.0等,对于这么多尺寸,我们在为布局设置宽高的时候,的确可以使用dp来给定一个固定的大小,但是我们却不知道用户手机的尺寸是多少,如果设置尺寸太大,可能屏幕容纳不小,如果设置的尺寸太小,屏幕可能留出许多的空白区域。

二、Android Drawable适配
1、普通drawable
新建一个Android项目后应该可以看到很多drawable文件夹,分别对应不同的dpi
drawable-ldpi (dpi=120, density=0.75)
drawable-mdpi (dpi=160, density=1)
drawable-hdpi (dpi=240, density=1.5)
drawable-xhdpi (dpi=320, density=2)
drawable-xxhdpi (dpi=480, density=3)
对于五种主流的像素密度(MDPI、HDPI、XHDPI、XXHDPI 和 XXXHDPI)应按照 2:3:4:6:8 的比例进行缩放。

我们一般的做法是以高分辨率作为设计大小,然后按照倍数对应缩小到小分辨率的图片。因为小分辨率在生成高分辨率图片的时候,会出现像素丢失。

2、自动拉伸位图

如果在可以更改尺寸的组件上使用了简单的图片,您很快就会发现显示效果多少有些不太理想,因为系统会在运行时平均地拉伸或收缩您的图片。解决方法为使用自动拉伸位图,这是一种格式特殊的 PNG 文件,其中会指明可以拉伸以及不可以拉伸的区域,这个就是NinePatch图片。

.9的制作,实际上就是在原图片上添加1px的边界,然后按照我们的需求,把对应的位置设置成黑色线,系统就会根据我们的实际需求进行拉伸。

三、布局的适配
对于不同的屏幕,如果差别太大,我们就需要使用不同的布局文件,我们可以通过使用配置限定符,在运行时根据当前的设备配置自动选择合适的资源了,例如根据各种屏幕尺寸选择不同的布局。

根据物理尺寸的大小准备5套布局:
layout(放一些通用布局xml文件,比如界面顶部和底部的布局,不会随着屏幕大小变化,类似windos窗口的title bar)

layout-small(屏幕尺寸小于3英寸左右的布局)

layout-normal(屏幕尺寸小于4.5英寸左右)

layout-large(4英寸-7英寸之间)

layout-xlarge(7-10英寸之间)

四、百分比布局
这是最理想的一种布局方案,使用百分比就可以完全不用担心适配的问题。android-percent-support这个库
用法可以看下面博客:Android 百分比布局库(percent-support-lib) 解析与扩展;Android 增强版百分比布局库 为了适配而扩展
.
另外,还有另外一种方案,根据一个基准,比如480*320的分辨率为基准,对各种不同手机的像素按照这个标准将垂直像素和水平像素分别均分成480份和320份。这样就可以得到每一份的像素,我们在设置的时候,直接使用份数就可以了,这也相当于是百分比,具体参照下面文章:Android 屏幕适配方案

五、注意点
1、使用wrap_content、match_parent、weight

weight的计算方法:设置宽度(android:layout_width) + 剩余宽度的占位比

2、使用相对布局,禁用绝对布局

3、使用限定符

4、使用自动拉伸位图

六、DisplayMetrics解析
有了上面的概念和知识,下面我们可以可来讲讲DisplayMetrics了。

1、获取屏幕分辨率信息的三种方法:

DisplayMetrics metrics = new DisplayMetrics();

Display display = activity.getWindowManager().getDefaultDisplay();
display.getMetrics(metrics);

DisplayMetrics metrics=activity.getResources().getDisplayMetrics(); 

Resources.getSystem().getDisplayMetrics();

在android里面,获取一个窗口的metrics,里面有这么几个值
 
 metrics.density;
 metrics.densityDpi;

densityDpi : 就是我们常说的dpi。

density : 其实是 DPI / (160像素/英寸) 后得到的值。是不是有点奇怪,因为我带了单位。。。这个涉及到后面一个比较重要的东西,后面再说。

从上面就看得出了,DPI本身的单位也是 像素/英寸,所以density其实是没单位的,他就是一个比例值。

而dpi的单位是 像素/英寸,比较符合物理上面的密度定义,密度不都是单位度量的值么,所以我更喜欢把dpi叫像素密度,简称密度,density还是就叫density。

2、上面所有的单位到px的转换

android.util.TypedValue类提供了一个函数,支持把所有的单位换算到px,实现代码如下:

public static float applyDimension(int unit, float value,  DisplayMetrics metrics)  
{  
	switch (unit) { 
		//px:pixel 
		case COMPLEX_UNIT_PX:  
    		return value;  
		//dp(dip)
		case COMPLEX_UNIT_DIP:  
    		return value * metrics.density; 
		//sp  
		case COMPLEX_UNIT_SP:  
    		return value * metrics.scaledDensity;  
		//pt : 1/72英寸
		case COMPLEX_UNIT_PT:  
    		return value * metrics.xdpi * (1.0f/72); 
		//in: inch 英寸 
		case COMPLEX_UNIT_IN:  
    		return value * metrics.xdpi;  
	 	//mm : 毫米  1英寸=25.4毫米
		case COMPLEX_UNIT_MM:  
    		return value * metrics.xdpi * (1.0f/25.4f);  
 	}  
 	return 0;  
} 

从上面我们可以看到dp、sp、pt、in、mm转换为px的方法。

在上面用到了DisplayMetrics对象,在这个对象中有哪些内容,我们来看看这个类。

//设备的绝对宽度,单位是px
public int widthPixels;

//设备的绝对高度,单位是px
public int heightPixels;

//屏幕密度,它的计算方法在上面的概念中已经给出了
public float density;

//dpi 上面的概念已经给出,单位尺寸的像素点
public int densityDpi;

//字体显示的缩放因子,跟上面的density是一样
public float scaledDensity;

//水平方向的dpi
public float xdpi;

//竖直方向的dpi
public float ydpi;

3、运用

在TextView里面有个setTextSize方法,它可以指定设置字体的单位。

mTestText1 = (TextView) findViewById(R.id.test1);
mTestText1.setTextSize(TypeValue.COMPLEX_UNIT_SP, 18);

上面就是将TextView对象的字体设置为18sp.

我们来看看它内部的实现源码。

public void setTextSize(float size) {
    setTextSize(TypedValue.COMPLEX_UNIT_SP, size);
}

public void setTextSize(int unit, float size) {
Context c = getContext();
Resources r;

if (c == null)
    r = Resources.getSystem();
else
    r = c.getResources();

setRawTextSize(TypedValue.applyDimension(
    unit, size, r.getDisplayMetrics()));
}

从上面可以看到,它默认单位就是TypedValue.COMPLEX_UNIT_SP,最终调用的其实就是上面我们讲到的TypedValue.applyDimension方法,其实它就是把sp转成了px.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值