基本概念
首先几个基本概念解释:
● dpi:该值代表的是一英寸上有多少个像素点,常见取值为120,160,240。一般这个值才叫做密度
在android里面获取的方法为 metrics.densityDpi;
屏幕尺寸/分辨率得出DPI,一个单位尺寸显示多少像素点。
● density:名字上虽然叫做密度,但是dpi才是密度,这个就叫做density。这个值是一个和标准DPI(默认160dpi后面解释)的比例,一般为 2,1.5 , 1.0,0.75
在android里面获取的方法为 metrics.density;
或者 metrics.densityDpi / 160DPI(标准DPI)
● dp(dip):设备无关像素,系统会自动根据density自动转换为px。
在android里面获取的方法为dp转px公式为
px= dpvalue *metrics.density;
px= dpvalue * (metrics.densityDpi / 160DPI(标准DPI))
● px:代表的是分辨率
这些值上面对应在现实生活中是这几个常用词汇:
● 屏幕尺寸:代表的屏幕宽高信息尺寸(单位cm)
● 屏幕分辨率:代表屏幕宽和高分别有多少像素点(单位px)
● 屏幕密度:代表一个单位屏幕尺寸上显示多少个像素点(单位dpi)
采用PX编写控件宽高尺寸出现的问题
接下来的讲的控件宽高尺寸适配都是用的px单位
为什么出现DPI,不进行直接对屏幕尺寸和分辨率进行适配
可以说我们安卓写控件宽高大小(用px)不需要关心屏幕尺寸也不需要关心分辨率(这样做会累死,因为很多设备虽然屏幕尺寸相同但是分辨率不同或者反过来,不同情况下控件宽高(用px)也不一样,一个个去适配宽高大小太复杂了),所以就需要个东西对尺寸和分辨率都进行考虑。
接着就会发现虽然屏幕尺寸和分辨率都很乱,但是大致上可以找到规律,就是 一个屏幕尺寸上显示多少个像素点主要就四种,所以DPI大体上只有四种,只需要关心这四种DPI就行,不需要陷入到具体的屏幕尺寸和分辨率上。
Android Design [1] 里把主流设备的 dpi 归成了四个档次,120 dpi、160 dpi、240 dpi、320 dpi。
为什么出现density而不是对DPI进行适配
由于市面上的设备DPI不相同,如果不进行DPI适配的话,就会出现大小不一问题
比如在120DPI上(一个屏幕单位显示120个像素点)设计的120px的控件尺寸 在屏幕上显示会占用一个屏幕单位。
跑在240DPI上(一个屏幕单位显示240个像素点),就会只占用0.5个屏幕单位,因此120DPI上跑的所有控件到了240DPI的设备上都会缩小一半。
所以就需要进行适配不同DPI的设备,虽然DPI我们只关心四种,比起适配不同屏幕尺寸和不同分辨率的设备需要适配的工作量大大降低,但是还是需要适配四种也就是四倍的工作量。
安卓的资源目录(图片,布局,drawable等)会带着后缀比如mdpi,xdpi,xxdpi…这些不同的资源目录下面存放着的就是对应DPI设备上使用的资源。
对于图片来说我们不需要关心,让UI去做不同DPI的图就行;但是 对于同一个布局里面的控价宽高尺寸(用px)来说仅仅因为尺寸不一样就需要重新写四套布局吗?UI也需要对每个标注出四套标注吗? 这可是四倍的工作量
所以我们希望可以根据不同DPI去做一个尺寸的比例缩小放大
首先要适配,就需要我们设计图采用一个基准DPI去设计控件尺寸大小,然后在不同DPI的设备上,根据我们设计图用的基准DPI的值 去除以 对应显示设备的DPI,就可以得到我们需要进行缩放的比例了,最后进行对应尺寸X比例即可实现放大缩小在不同DPI的设备上完成适配。
比如如果我们设计图是在120DPI的设备上出的标注,那么上面说的120DPI上跑的120px的控件要在240DPI上采用240PX的控件才可以,可以看到是120*2得出来的;
但是如果要泡在160和320的设备上,对应的比例就可能除不尽导致最后得出的尺寸有小数,不够精准。
所以我们设计图该采用哪种基准DPI进行出标注呢?
我们会发现这四个主流的DPI也有个规律:
如果以 160 dpi 作为基准的话,只要尺寸的 DP 是 4 的公倍数,XHDPI 下乘以 2,HDPI 下乘以 1.5,LDPI 下乘以 0.75 即可满足所有尺寸下都是整数 pixel 。
所以我们默认设计图和我们最终编写 适配的都是在160DPI设备上跑的,不同DPI的设备进行对应的缩放比例调整即可,这个缩放比例就是density。
因为记忆这四个DPI的数值还是比较麻烦,所以我们直接记忆density即可,也就是说我们的控件宽高尺寸X对应的density即可。最后得出的公式就是:
最终显示在不同DPI设备上的控件宽高尺寸 = 160DPI标注图上控件宽高尺寸 X 对应的density(显示设备DPI / 160DPI得出)。
我们只需要根据这个公式就可以计算出不同DPI下的宽高尺寸,但是好像我们并没有解决根本问题,上面说的只是节省了UI的工作量他们不需要出四套不同dpi上不同宽高尺寸的图了,我们还是依然需要写四个不同的布局文件。
采用DP编写控件宽高尺寸
接上面说的,我们依然还是需要写四套布局写四个不同的宽高尺寸,如果有个工具可以自动帮我们做转换就好了,我们上面一直说的都是用的px进行的适配,如果我们自定义一个尺寸单位,这个尺寸单位如果在变成px的过程中进行上面我们说的density转换 那么是不是就可以达到我们只写一个布局文件的目的了。
没错,这个单位就是dp,密度无关像素,也就是dpi无关,因为dp最终转换px的时候会进行不同DPI的适配(也就是X对应的density)现在是不是就可以理解为什么叫做密度无关了吧。
这样我们以后编写尺寸都用dp进行编写(采用160DPI的设计图),dp转换为px的时候 乘以 对应设备缩放的density(显示设备DPI/160DPI)即可。
这样在160DPI上采用的dp就是px,因为我们就是在160DPI上出的px标注。
在320dpi上采用的dp最终转换为px的时候会乘以2,因为我们是按照160DPI设计的。
可以看出dp是px的一个封装
国内屏幕参数太乱导致density精度不足导致最终显示有问题
假设宽分辨率1080px,480DPI的设备得出屏幕宽度518400英寸;
另外一个设备分辨率1440px,560DPI的设备得出屏幕宽度806400英寸。如果一个100dp宽度的控件显示在这两个设备上分别占用300px,350px像素点,对应显示在屏幕上的尺寸分别为0.625英寸,0.625英寸(不同尺寸屏幕 显示的控件尺寸居然一样);
得出对应占用整个屏幕宽度的百分比分别为0.000000121,0.00000078。
我们发现经过系统转换DP为PX,最终占据的px已经经过转换但是 在屏幕上显示的百分比却有问题(从不同屏幕尺寸显示的控件尺寸一样就可以看出不对了),可以发现 根本原因还是分辨率和尺寸太乱了也就是说DPI太乱了,安卓官方想的是主流DPI适配即可 (主流dpi经过160基准DPI的转换因为density都是整数所以都可以正常转换,显示的尺寸比例不同设备也都一样),
但是国内形形色色的手机太多了(转换的density有小数最终转换为px会精度丢失,导致比例不同),官方的适配方案已经在国内不可行了,所以出现了今日头条的方案。
今日头条
根据上面的分析我们已经知道了 官方的density计算方案是设备DPI/基准DPI,但是对于国内来说由于设备DPI太多太乱(屏幕尺寸和分辨率太乱)且没有规律因此导致density精度丢失转换出问题。
我们就需要另外想办法了,像当初dp诞生一样我们在重新创建一个单位进行转换?改动太大且无法兼容;改变每个 View 的 dp 值?不现实,在每个设备上都要通过代码动态计算 View 的 dp 值,工作量太大(这和当初改变每个view的px值思路一样);修改density计算方案看起来可行,因为官方已经有这种转换的机制了我们修改转换机制即可。
如何才能保证不同dpi最终计算出来的density不存在精度丢失问题呢?
之前的办法在于找到DPI之间的比例进行等比缩放,但是DPI太多了比例会丢精度导致数值不精准;既然DPI不固定,我们就找到固定的部分,我们发现我们写的dp是固定的,之前一直迁就设备,主动权在设备上导致我们被动,所以要让主动权在我们手上,我们控件写的dp是固定的,如果可以让所有设备屏幕的总dp都一样是不是也就可以保证比例一样。
我们首先需要让UI出图的标准是根据宽高总DP进行出图,而不是之前的基准160DPI出图,UI出的屏幕宽高DP就是实际的屏幕宽高DP,而屏幕宽高dp=屏幕宽高px(像素)/density,所以我们想要最终的屏幕宽高dp(设计图宽高dp)一样就需要重新计算density也就是:
当前设备屏幕总宽度(单位为像素)/ 设计图总宽度(单位为 dp) = density
这样动态修改desnity就可以认为所有设备的总dp都和设计图的dp一样了
方案验证
方案优缺点
优点是一刀切不需要改动density;但是缺点也是这个,由于采用的是让屏幕总dp和设计图dp一样的方案,如果是我们自己写的view那么对应的dp比例就可以正常转换; 但是如果我们用了别人的view,而别人view的dp不是按照我们设计图dp写的并且和我们设计图dp差别很大,就会出现问题。
这就是两个设计图尺寸不一致导致的非常严重的问题,当两个设计图尺寸差距越大,那适配的效果也就天差万别了
方案一
无法让三方库来适应我们项目的设计图尺寸,所以只有我们自身作出修改,去适应三方库的设计图尺寸,我们将项目自身的设计图尺寸修改为这个三方库的设计图尺寸,就能完成项目自身和三方库的适配
方案二
参考链接
基础概念方面文章:https://blog.csdn.net/qq_30196607/article/details/50803392
https://www.imooc.com/article/260877
头条方案的理解:https://blog.csdn.net/xcbyaya/article/details/137981609