Android的碎片化已经被喷了好多年,随着国内手机厂商的崛起,碎片化也越来越严重,根据OpenSignal的最新调查,2014年市面上有18796种不同的Android设备,作为开发者,一个无法回避的难题就是需要适配各种各样奇奇怪怪的机型。
设备机型不同必然也会导致屏幕大小和分辨率(Resolution)的不同,但是无论分辨率有多大,屏幕有多大,我们手指触控范围的大小不会发生变化,所以最优的适配方式应该是指定大小的控件在所有的设备上的显示都一样。
Android的官方文档对此也有明确的说明
When adding support for multiple screens, applications do not work directly with resolution; applications should be concerned only with screen size and density, as specified by the generalized size and density groups.
所以,适配应该与分辨率无关,只与屏幕大小和屏幕密度相关,以下是与单位相关的术语:
(1) Screen size 屏幕的尺寸,即对角线长度(单位inch-英寸)
(2) Resolution 分辨率,即屏幕的总像素点数(width * height)
(3) Screen Density屏幕密度,即每单位英寸包含的像素点数(dots/inches)
(4)Density-independent pixel (dp或dip) 密度无关像素,或者说是与屏幕密度无关的像素。标准是160dip,即1dp对应1个pixel,计算公式如:px = dp * (dpi / 160),屏幕密度越大,1dp对应的像素点越多。
第一、屏幕尺寸
比如常见的屏幕尺寸有3.5、3.7、4.2、5.0、5.5、6.0等。
第二、屏幕分辨率
屏幕上显示的像素个数,单位尺寸内像素点越多,显示的图像就越清楚。单位是px,1px=1个像素点。
分辨率720*1280表示手机水平方向的像素为720,垂直方向为1280.
市场上主流分辨率有:480*800、 720*1280、 1080*1920(其他的早该淘汰了,忽略不计)。
第三、屏幕密度
表示屏幕每英寸(inch)的物理长度内包含的像素点数(dots),即屏幕像素密度。 单位是dpi( Dots Per Inch)
DPI(Dots Per Inch)是印刷行业中用来度量空间点密度用的,这个值是打印机每英寸可以喷的墨汁点数。计算机显示设备从打印机中借鉴了DPI的概念,由于计算机显示设备中的原子单位不是墨汁点而是像素,所以就创造了PPI(Pixels Per Inch),这个值是屏幕每英寸的像素数量,即像素密度(Screen density)。由于各种原因,目前PPI(主要是iOS)和DPI(主要在Android中)都会用在计算机显示设备的参数描述中,不过二者的意思是一样的,都是代表像素密度。
Android设备用DPI来表示屏幕密度(Density),屏幕密度大就表示一个Inch包含的Dot比较多。160DPI的屏幕就表示一个Inch包含160个Dot,320DPI的屏幕表示一个Inch有320个Dot,所以说Dot的大小是不固定的。高DPI屏幕显示的元素会比较精细(看起来会比较小),低DPI屏幕显示的元素相对来说就比粗糙(看起来会比较大)。
通常我们说一个设备是多少寸时,指的是屏幕对角线(Diagonal)长度是多少inch,所以用对角线的像素值(px)除以对角线长度(inch),就可以计算出PPI。
PPI 计算公式
为了简化适配工作,Android根据屏幕大小(Inch)和屏幕密度(DPI)对设备做了如下划分:
你需要把对应dpi的资源放到对应的目录就可以了,Android会根据dpi自动选择资源,目录规则如下:
- drawable-mdpi/asset.png
- drawable-hdpi/asset.png
- drawable-xhdpi/asset.png
- ...
可以看出Android中mdpi与iOS中的1x multiplier所代表的PPI是一样的,xhdpi与iOS的2x multiplier所代表的PPI一样,如图:
第四、Andriod中的单位DP与SP
4.1 DP
既然有那么多不同分辨率、不同大小的屏幕,使用PX必然会导致适配困难,为了进一步简化适配工作,Android为我们提供了一个虚拟的像素单位 - DP 或者 DIP (Density-Independent pixel),当然也可以理解为 Device-Independent Pixel,即与设备屏幕密度无关的像素。为什么说是虚拟呢,因为它的大小不是一个物理(Phisical)值,而是由操作系统根据屏幕大小和密度动态渲染出来的。
PX跟DP之间的换算关系很简单:
px = dp * (dpi / 160)
举例来说,小米Pad的屏幕密度为326dpi,如果需要显示的图片大小为20dp,那么就需要提供一个 20*(326/160)=40px
的图片才能达到最佳显示效果,如果还要适配一个163dpi的屏幕,那么还需要再提供一个20*(163/160)=20px
的图片。
那么一个20dp的图片,在不同设备上的显示效果如何呢?我们以iPad为例来说明。
iPad 屏幕参数
iPad2 和 iPad Retina的物理尺寸都是 9.7 inch,不同的是分辨率和PPI,一个是1024x768 / 132ppi,另一个是2048x1536 / 264ppi,分别计算一下20dp对应多少inch
ipad2 = 20 * (132 / 160) * (7.9 / (math.sqrt(1024 * 1024 + 768 * 768)))
ipad_retina = 20 * (264 / 160) * (7.9 / (math.sqrt(2048 * 2048 + 1536 * 1536)))
计算结果都是0.1018359375,这就是dp的功能,它能保证在所有的设备上显示的大小都一样。
4.2 SP
SP 全称是 Scale-independent Pixels,用于字体大小,其概念与DP是一致的,也是为了保持设备无关。因为Android用户可以根据喜好来调整字体大小,所以要使用sp来表示字体大小。
第五、Andriod Studio中的资源
res目录下存放项目中的所有动画、图片、布局、字符串等资源。通常图标放在mipmap目录下,图片放在drawable目录下,布局放在layout目录下,字符串放在values目录下, 下面是详细描述。
5.1 动画资源(Animation Resources)
定义预先确定的动画资源。
Tween动画保存在res/anim/
目录下,通过R.anim
类来访问.
Frame动画保存在res/drawable/
目录下,通过 R.drawable
类来访问.
备注:动画分为两种,一种是Tween动画、还有一种是Frame动画。Tween动画,这种实现方式可以使视图组件移动、放大、缩小以及产生透明度的变化;Frame动画,传统的动画方法,通过顺序的播放排列好的图片来实现,类似电影。
5.2 颜色状态列表资源
定义随着View状态而改变的颜色资源。
保存在 res/color/
目录下,通过 R.color
类来访问.
5.3 icon资源
定义应用程序使用的图标资源。
保存在res/mipmap目录下,通过R.mipmap类来访问.
5.4 Drawable资源
定义bitmap图像或通过XML来定义的图像资源.
保存在res/drawable/
目录下,通过R.drawable
类来访问.
5.5 布局资源
定义应用程序UI的布局。
保存在res/layout/
目录下,通过R.layout
类来访问.
5.6 菜单资源
定义应用程序菜单的内容。
保存在res/menu/
目录下,通过R.menu
类来访问.
5.7 String资源
定义串、串数组、和数量字符串(zero, one, two, few, many, other)plurals (并且包含串的格式化以及风格).
保存在res/values/
目录下,通过R.string
, R.array
, 以及R.plurals
类来访问.
5.8 样式(Style)资源
定义UI元素的外表与格式(look and format).
保存在res/values/
目录下,通过R.style
类来访问.
5.9 更多的资源类型
颜色、维度、ID、整数值、整数数组、TypedArray的值.
保存在res/values/
目录下,但每一种是通过不同的 R
子类(诸如 R.bool
, R.color, R.dimen
, R.id, R.integer
,