安卓屏幕适配
1.基础概念
1.1 屏幕尺寸
- 含义:手机对角线的物理尺寸
- 单位:英寸(inch)
1.2 屏幕分辨率
- 含义:手机在横向、纵向上的像素点数总和
一般描述成屏幕的"宽x高" 如:1080x1920 也就意味着 横向方向上有1080个像素点 纵向方向上有1920个像素点
- 单位:px(pixel)
1.3 屏幕像素密度
- 含义:每英寸包含多少像素
- 单位:dpi(dot per ich)
- 公式:dpi = √(宽² + 高²)/ 屏幕尺寸 例:当有一台屏幕分辨率为1080 * 1920,屏幕尺寸为5寸的屏幕,他的dpi即为 √(1080² + 1920²)/ 5 = 2203 / 5 = 440dpi
1.4 密度无关像素
- 含义:与终端上的实际物理像素点无关,可以保证在不同屏幕像素密度的设备上显示相同的效果
- 单位:dp(density-independent pixel),又可称dip
根据公式,最后会将dp转成相应的px,下面会进行介绍
1.5 独立比例像素
- 含义:Android开发时用此单位设置文字大小,可根据字体大小首选项进行缩放
- 单位:sp
1.6 density(密度)
- 含义:每dp包含多少px
2.Android传统屏幕适配(dp)
Android截止到目前为止,常规的加上各种奇奇怪怪的屏幕已经有2万多种(甚至更多),那这种情况下我们开发的时候还用px编写UI的话简直就是灾难,所以谷歌针对了不同分辨率的屏幕该如何适配这个问题下,使用了dp方案适配,接下来就了解一下dp方案的原理到底是怎么样的
2.1 原理
2.1.1 dp是如何转换成px的
android中的dp在渲染前会将dp转成px,计算公式如下
- px = density * dp
- density = dpi / 160
- px = (dpi/160) * dp
- dpi = √(宽² + 高²)/ 屏幕尺寸
介绍完公式之后,首先我们设定一个UI稿尺寸,定义一个200*200dp的控件,再定义2个屏幕例子,来看看200dp的控件这个适配效果是怎么样
- UI稿子:以Iphone6为例子,屏幕分辨度:750 * 1334,屏幕尺寸为 4.7
- 首先算出dpi是多少,参考dpi公式,我们得出dpi=√(750² + 1334²)/ 4.7 = 326dpi
- 套用公式,算出200dp= 200 * (326/160) = 407px
- 设备1:屏幕分辨率为 1080*1920 ,屏幕尺寸为5寸
- 首先算出dpi是多少,参考dpi公式,我们得出dpi=√(1080² + 1920²)/ 5 = 440dpi
- 套用公式,算出200dp= 200 * (440/160) = 550px
- 设备2:屏幕分辨率为 720*1240,屏幕尺寸为4寸
- 首先算出dpi是多少,参考dpi公式,我们得出dpi=√(720² + 1240²)/ 4 = 358pi
- 套用公式,算出200dp= 200 * (358/160) = 447.5px
计算200dp换算px后对比设计稿与两个设备宽度下占有的百分来判断适配效果怎么样。
- UI稿:407 / 750 = 54%
- 设备1:550 / 1080 = 51%
- 设备2:447.5 / 720 = 62%
通过以上例子可以看出来,使用dp的情况下,虽然效果一般,但是还是尽量的进行了适配
2.1.2 在代码中,DP是如何转换的
无论在pt、sp、dp,最后都会通过TypedValue.applyDimension转换成对应px
//屏幕信息
public class DisplayMetrics {
//密度
public int density;
//当前设备dpi
public float densityDpi;
//字体的缩放因子,正常情况下和density相等,但是调节系统字体大小后会改变这个值
public float scaledDensity;
}
public class TypedValue {
//无论在pt、sp、dp、mm等最后都会通过该方法转换成对应px
public static float applyDimension(@ComplexDimensionUnit int unit, float value,
DisplayMetrics metrics)
{
switch (unit) {
case COMPLEX_UNIT_PX:
return value;
case COMPLEX_UNIT_DIP:
return value * metrics.density;
case COMPLEX_UNIT_SP:
return value * metrics.scaledDensity;
case COMPLEX_UNIT_PT:
return value * metrics.xdpi * (1.0f/72);
case COMPLEX_UNIT_IN:
return value * metrics.xdpi;
case COMPLEX_UNIT_MM:
return value * metrics.xdpi * (1.0f/25.4f);
}
return 0;
}
}
2.2 总结
- 认识了基础概念,屏幕尺寸、屏幕分辨率、屏幕像素密度(dpi)、密度无关像素(dp)、密度(density)
- 传统适配方案原理就是通过px=density * dp这个公式,也根据了几个例子测试了适配效果
- Android任何地方,只要是其他单位转px,必然会调用TypedValue.applyDimension
2.3 缺点
因为Android目前屏幕碎片化严重,但是往往设计也只会给一份设计图,如果我们用设计稿上的尺寸去布局的话,在差异较大的屏幕上适配效果又特别一般,所以我们需要找到相比dp适配更优的方案,接下来就看看字节的方案是怎么样吧。
3. 头条适配方案
在普遍只有一份尺寸的UI稿来说,传统dp适配在不同的机型上适配效果一般
3.1 传统适配方案的问题
举个例子,我们布局中有一个100dp宽的控件,我们看看这个控件在2个设备上的比例是怎么样的
设备1:1080宽的设备 dpi:480,屏幕总dp宽度为1080/(480/160) = 360dp,接着计算100dp占总宽比例是多少? 100/393=27.8% 设备2:1440宽的设备 dpi:560,屏幕总dp宽度为1440/(560/160) = 411dp,接着计算100dp占总宽比例是多少? 100/411=24.3%
可以看出来,100dp宽的控件在以上2台设备上的比例完全完全不一样,当屏幕越大控件显示得越小,所以我们需要解决同一个UI稿,在不同设备上看起来的比例都是一致的。
3.2 需求
- 如何保证一份UI稿中的控件,在任何屏幕的比例都是相同的?
- 只需要直接输入UI稿的尺寸,就可以进行开发并保证在任何屏幕的比例都是相同的
- 侵入性低,性能高,并且不影响第三方控件的UI
3.3 优化思路
先说结论,我们保证每个设备的宽度总DP等于设计稿,从density入手,再次声明density就是每dp包含多少个px
先搬出公式 当前设备屏幕总宽度(单位为像素)/ 屏幕的总 dp 宽度 = density 我们稍作修改,改成: 当前设备屏幕总宽度(单位为像素)/ 设计稿的总 dp 宽度 = density
只要 density 根据不同的设备进行实时计算并作出改变,就能保证 设计图总宽度 不变,也就完成了适配。
3.4 验证
假设设计图总宽度380dp,同样我们设定有一个100dp宽高的View,我们首先换算一下设计图中控件对比设计图的比例= 100/380= 0.263 我们接着验证两个设备
设备1:屏幕总宽度1080px,根据上面的思路求出density,1080/380=2.84(density)
这个宽高100宽高的控件根据dentisy换算成实际px=100*2.84=284px
接着我们计算一下实际换算后的比例与设计图中的比例是否一致 实际换算后的比例=284/1080 = 0.262
设备2:屏幕总宽度1440px,根据上面思路求出density=1440/380= 3.79 这个宽高100dp的控件,根据density计算得出实际宽度=100*3.79=379px
接着继续我们计算一下换算后的比例与设计图中的比例是否一致 379/1440=0.263
3.5结论
通过对比传统dp适配,今日头条的适配方案效果会更佳,而我们只需要确认我们的设计稿宽高,再改变系统的density值,这样我们就可以做出效果十分出色的适配。