随着硬件技术的发展,移动端手机的屏幕分辨率越来越多,为了处理屏幕碎片化问题和方便移动端界面的开发,当前两个主流移动端操作系统(Android,IOS)都抽象出了一套屏幕适配方案,本文章主要讲解两套适配方案的原理。
移动开发中的一些概念
屏幕尺寸相关的单位和术语:
- pt(point):磅,一个标准的物理绝对长度单位,大小为1/72英寸。主要用于印刷与平面设计。1inch = 2.54cm=72pt。
- px(pixel):物理像素,组成屏幕图像的最小点,一个相对大小单位。同尺寸屏幕中像素越小越多,代表分辨率越高,反之越低。
- 屏幕尺寸(Screen Size):屏幕物理尺寸,指屏幕对角线长度,而不是屏幕宽度或高度。
- 分辨率(Resolution):屏幕拥有像素的总数以及屏幕水平(宽)和垂直(高)方向的像素数量。
- ppi(pixels per inch):屏幕密度,指每英寸所拥有的像素数/每英寸像素。ppi数值越高,即代表显示屏能够以越高的密度显示图像。显示的密度越高,拟真度就越高。当你拿的手机距离你约25-30厘米,如果手机ppi达到300以上,你的眼睛将无法分辨出像素点。这就是苹果提出的视网膜屏幕。
- dpi(dots per inch):印刷计数单位,指每英寸所能印刷出来的网点数。衡量打印机打印精度的主要参数之一,值越大,表明打印机的打印精度越高。电脑图片为72dpi 。但印刷图片dpi参数最好是300dpi以上。如果冲洗5x7英寸的照片,dpi是300,照片需达到(5x300)x(7x300)= 1500 x 2100像素。photoshop中的分辨率设置指的其实就是dpi
换算公式:
1pt = (ppi / 72) x px
在物理屏上:ppi=dpi
PPI = √(X^2+Y^2)/ Z
安卓开发中的适配方案
为了方便程安卓序员的开发,安卓操作系统将手机屏幕分成了不同的等级。
Andorid手机相关的单位和标准:
- dip(Device Independent Pixels):设备独立像素单位。 Android开发虚拟单位。多用于Android示例计数单位。
- dp:android开发中的布局长度单位,和ios中的pt概念一样,是一个虚拟单位
- sp:和dp一样,只不过是用来设定字体大小的,sp设定字体大小可以随用户的缩放而改变大小,在每英寸160点的显示器上,当文字大小为100%时,1sp = 1px。
转换公式:
dp = (ppi/160) x px
dip = dp
从上图中可以看出,逻辑密度和真实密度是有一定的差距的,设备的真实密度通过某个范围归类到某一个逻辑密度,从而划分成不同的密度等级。
不同等级密度中,1dp所代表的理想物理尺寸是一致的,例如上图的Mdpi中:1dp = 320/160/320 = 1/160inch,XHdpi中:1dp = 720/320/360 = 1/160inch。
由于设备的真实密度与逻辑有一定的差距,所以1dp多代表的物理尺寸会有稍微的区别,不过大体上接近某一个值,公式为:1dp = 设备分辨率/真实密度/逻辑分辨率。
在相同密度等级的设备,虽然1dp所代表的物理像素的个数相等,但由于真实密度有区别,所以相等的像素个数所占据的物理尺寸是有差别的。
IOS开发中的适配方案
IOS的适配方案和安卓的类似,都是通过虚拟单位结合转换比去实现。
IOS手机相关的单位和标准:
- pt:ios中的虚拟单位,类似安卓中的dp
从上图中可以看出,ios中的转换比是以ppi=163的密度来计算的,在ios中,如果屏幕最终渲染的大小和屏幕分辨率的大小一致,那么1pt所代表的物理尺寸=1/163inch。在上图中除了6+,其他设备的1dp均等于1/163英寸。
6+手机因为特殊的PPI,如果按照正常的计算方式,其比例因素将不等于整数,为了方便移动端开发,6+手机采用了虚拟放大技术,将屏幕分辨率虚拟成(1242x2208),使其设备像素比刚好等于3。在最终的渲染中,通过缩小虚拟分辨率(1242x2208)使屏幕的最终渲染结果等于物理分辨率(1080x1920)。
在6+中,1dp不在等于1/163英寸,而是 = 1080/401/414英寸,略大于1/163。
WEB开发中的适配方案
在web网页开发中,也有专门用于屏幕适配的viewport标签。css中的px单位也是一个虚拟单位,和物理像素单位不是一个概率。
viewport标签content参数:
- width:用于控制布局viewport的大小
- initial-scale:用于控制初始度量viewport的大小,计算规则=设备的逻辑分辨率宽/initial-scale
- minimum-scale:用于控制最小度量viewport的大小
- maximum-scale:用于控制最大度量viewport的大小
- user-scale:是否允许用户缩放页面
其中布局viewport可以使用document.body.clientWidth来获取,度量viewport可以使用window.innerWidth来获取(在某些浏览器上,如果度量视口小于元素宽度,则 window.innerWidth 将为元素的宽度,不等于度量视口的宽度),window.outerWidth可以获取到设备的逻辑分辨率宽,此时的px对应于安卓的dp或ios的pt。
移动端页面,如果没有设置viewport标签,默认会有一个980px的布局视口,而默认度量视口没有固定值,只有一个默认最大度量视口(即使设置 scale,最多也只能缩放到该大小),不同设备的默认最大度量视口不一致。
<!DOCTYPE>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style type="text/css">
body{
margin:0;
padding-top: 0;
}
#d1{
width: 1800px;
height: 100px;
background: red;
}
</style>
</head>
<body>
<div id='d1'></div>
</body>
<script type="text/javascript">
</script>
</html>
iphone5下最大度量视口为1280px,iphone6下最大度量视口为1600px:
如果只设置了布局视口,则度量视口会根据页面元素的宽度设置一个合适的值:
<!DOCTYPE>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=1000">
<title>Document</title>
<style type="text/css">
body{
margin:0;
padding-top: 0;
}
#d1{
width: 1300px;
height: 100px;
background: red;
}
</style>
</head>
<body>
<div id='d1'></div>
</body>
<script type="text/javascript">
</script>
</html>
如果只设置了度量视口,则布局视口将等于度量视口:
<!DOCTYPE>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="initial-scale=0.3">
<title>Document</title>
<style type="text/css">
body{
margin:0;
padding-top: 0;
}
#d1{
width: 750px;
height: 100px;
background: red;
}
</style>
</head>
<body>
<div id='d1'></div>
</body>
<script type="text/javascript">
</script>
</html>
如果度量视口小于布局视口将产生滚动条:
<!DOCTYPE>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=750,initial-scale=0.8">
<title>Document</title>
<style type="text/css">
body{
margin:0;
padding-top: 0;
}
#d1{
width: 750px;
height: 100px;
background: red;
}
</style>
</head>
<body>
<div id='d1'></div>
</body>
<script type="text/javascript">
</script>
</html>
如果设置的度量视口宽度大于布局视口的宽度,则布局视口会自动设置成度量视口的宽度:
<meta name="viewport" content="width=750,initial-scale=0.2">
最后在来说一下一个已经被安卓废弃的viewport属性target-densitydpi:
target-densitydpi表示目标设备的密度等级,作用是决定css中的1px代表多少物理像素,target-densitydpi 值可以为一个数值或 high-dpi 、 medium-dpi、 low-dpi、 device-dpi 这几个字符串中的一个特别说明的是,当 target-densitydpi=device-dpi 时, css中的1px会等于物理像素中的1px。因为这个属性只有安卓支持(andorid4.0以下的手机不支持viewport的width属性,可以用target-densitydpi代替),并且安卓已经决定要废弃 target-densitydpi 这个属性了,尽量避免使用这个属性。