Android屏幕适配前先了解这些

前言:
之前很火的屏幕适配方案不知道大家都去尝试过写进项目中没,应该有一部分人在隔岸观火,大概的原因就是目前并没有遇到能把项目重构的适配问题,另一方面就是有的适配方案还没有很成熟的应用,都不想拿自己的项目去测试。就拿那些github开源库上面的适配方案来说,没有几个人去上面提issues。就在最近我去试了一下今日头条的适配方案,然后。。。哎~~接着往下看吧

一 ppi和dpi这两个单位是什么?有什么关系?

ppi(Pixels per inch) 指每英寸上的物理像素数数目,即 "像素密度“。一般再购买手机的时候都会在参数中看到该设备的ppi数值,ppi数值越大屏幕显像效果越好。不过ppi是物理上的概念,是客观存在的不会改变的值,跟开发中常见的dpi是完全不同的。

dpi(Dots Per Inch)指每英寸有多少个点,最初是用在印刷行业,用来描述每英寸有多少小黑点。dpi被用于Android开发中用来描述屏幕像素密度的单位,是手机出厂就写在系统配置中的一个固定数值,一般是固定不变的,除非你root之后去系统文件中修改这个值,不过手机root有太多的风险,不推荐去root,开发中可以用DisplayMetrics类去获取dpi数值。

ppi和dpi是没有任何关系的。有些文档中ppi 等于 dpi的言论都是瞎扯的,它们之间也没有什么换算关系,还有的文章说 dpi的取值取决于ppi处于哪个dpi的范围,然后取这个范围最大的值,这一点是没有任何的依据,至于dpi的赋值我们也无法得知手机厂商是根据什么去确定的。

ppi的数值我们可以通过以下公式算出,一般的话手机参数里面都能看到ppi的数值,该公式并不适用计算dpi。

在这里插入图片描述

dpi不能用上面的公式求出,dpi可以通过DisplayMetrics类的densityDpi属性获取当前手机的dpi数值,该类也可以获取到跟屏幕密度有关的其它属性。一般获取DisplayMetrics类有以下方法:

方式1:
//content:Activity,Content,Application. 
DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
方式2:
//getSystemService可以通过 Activity,Content,Application等获取.
 DisplayMetrics displayMetrics = new DisplayMetrics();
 WindowManager windowManager = (WindowManager)getSystemService(Context.WINDOW_SERVICE);
 windowManager.getDefaultDisplay().getMetrics(displayMetrics);
二 为什么dp满足不了现在的屏幕适配(设计图按1080x1920设计)?

为什么强调设计图呢, 因为设计图是UI设计师根据APP的类型以及使用场景精心设计的,同时设计稿直接决定app界面预期的显示效果,决定了每个控件预期的大小,而屏幕适配也是要解决在Android尺寸限制的范围内,按照一套设计图写出的布局要在大部分机型上面显示效果都跟设计图一样。一般的设计师会给一套尺寸,比如1080 X 1920 即 360dp X 640dp 比例 9:16的,或者IOS和Android使用一套设计图(一般都会让Android用IOS的设计稿)。在没有严格要求的话我们只是使用了dp来写布局, 反正现在一直都是?,渐渐的发现dp已经逃不过设计师的法眼了(好多机型显示的效果都跟设计图有较多的差异)。

接着看,国内Android手机的ppi数值是厂商定制的,跟手机的硬件相关,ppi数值越大显像效果越好,但ppi只是描述了手机硬件方面的像素密度,并不用于开发中。每种通用的尺寸和密度都涵盖一个实际屏幕尺寸和密度范围。例如, 两部正常屏幕尺寸的设备在手动测量时,实际屏幕尺寸和 高宽比可能略有不同。类似地,对于两台hdpi 屏幕密度的设备,其实际像素密度可能略有不同。 Android 将这些差异抽象概括到应用,使您可以提供为通用尺寸和密度设计的 UI,让系统按需要处理任何最终调整。(有可能Android手机系统出厂设置的dpi数值也会参考该取值范围的)。

在这里插入图片描述

ldpi(低)~120dpi
mdpi(中)~160dpi
hdpi(高)~240dpi
xhdpi(超高)~320dpi
xxhdpi(超超高)~480dpi
xxxhdpi(超超超高)~640dpi

小屏幕至少为 426dp x 320dp
正常屏幕至少为 470dp x 320dp
大屏幕至少为 640dp x 480dp
超大屏幕至少为 960dp x 720dp

为了简化屏幕适配,一般机型的dpi的取值会参考上面的范围,但是总会有一些特殊的机型就是不采纳官方的建议。如小米 MIX 2 分辨率 2160x1080 屏幕尺寸 为6, ppi为403 获取到的dpi为440,该分辨率下的手机dpi 大致为480。为什么要强调dpi的数值呢?我想大家都知道我们再布局的尺寸方面都会选择dp,因为dp是会随着分辨率的不同而变化的,一般的关系如下:

dpi 120 : 1dp = 0.75px;
dpi 160 : 1dp = 1px;
dpi 240 : 1dp = 1.5px;
dpi 320 : 1dp = 2px;
dpi 480 : 1dp = 3px;
dpi 640 : 1dp = 4px;

计算公式:
px = density * dp;
dp = px / density;
density = dpi / 160;

根据上面的公式可以看到dpi影响了dp转px的数值,所以可以说dp适配也就是dpi的适配,对于360dp X 640dp的设计稿来说,对应的分辨率为1080 X 1920和1440 X 2560,使用的数值为 1dp = 3px。正常的机型我们使用dp的话基本可以完成适配,但是当碰到分辨率一样dpi不同的手机,比如dpi = 440 1dp = 2.75px 或者 dpi = 420 1dp = 2.625px 的机型的时候,那就懵逼了,如一个Button的宽度为100dp,再dpi = 480的机型中显示的宽度效果为300px,再dpi = 440显示的效果宽度为275px,这样我们的布局就会跟预期的不太一样,这是dp无法适配的。

另外现在主流的是大屏手机,长度方向的像素点一般大于1920px,大致在2040px~2880px之间,但是宽度基本保持再1080px,配置好的手机是1440px,市场90%以上主流手机宽度都是1080px的。如:

华为:
nova 2s ,Mate 10 Pro 等等分辨率是2160X1080  dpi = 480  ;
nova 3 2340 x1080  dpi = 480;
小米:
MIX 2040x1080  dpi = 480,  MIX2 2160x1080  dpi = 440 ,
Max 2160X1080  dpi = 480等等;
oppo:
R11s 2160x1080  dpi = 480, R15 2280x1080  dpi = 480,
等等...

手机dpi的大小决定了当前dp转px的倍数关系,目前大部分机型的dpi都是480,也就是说设计图上一个组件的margintop 为100dp = 300px,那么当运行在分辨率为1080X2280的机型中该组件相对于设计图的位置就会偏上,在分辨率为1080x1920的机型中正常,这就会导致一个问题,在大屏手机中正好显示完整的布局会再小屏幕中就会出现控件被遮挡或者控件的高度比不一致,最明显的就是开屏页的logo位置。这也是dp无法解决的适配问题。

个人而言,适配宽度用dp基本能够适配,毕竟那些特殊dpi的机型还是少数,写布局注意点的话就不会出现太明显的适配问题。适配高度就需要使用其他的更有效的适配方式了。

三 宽高限定符,AndroidAutoLayout,smallestWidth,今日头条适配方案怎么取舍?

宽高限定符适配和smallestWidth适配方案大致思想都是一样,smallestWidth比宽高限定符更加的智能可靠。但是这两种方案需要增加好多资源文件,想要适配什么屏幕就要去配置该类型的资源文件,全局适配。这两种适配方案再宽高适配上还是很有效果的。鸿神的AndroidAutoLayout已经停止维护了,我想大家都不会优先考虑这个方案了,这里也不去讨论。今日头条适配方案我想大家都或多或少的了解过,该方案还是比较精简灵活的,可以自己选择以宽度适配还是高度适配,下面是在高度纬度上面的测试数据:

设计图: 
360dp X 640dp 分辨率为 1080 X 1920 这里的屏幕高度包括状态栏。
控件高度为103dp  高度/屏幕高度 = 0.1609375.
      
    
    
模拟器 1: 
分辨率为 1080 x 2280 .实际是 1080 X 2136 .状态栏高度Wie:72px.   
控件高度为103dp  高度/屏幕高度 = 0.1497093. 适配后:0.1609649.

    
    
模拟器 2: 
分辨率为 1080 x 1920 .实际是 1080 X 1776 .状态栏高度Wie:72px.   
控件高度为103dp  高度/屏幕高度 = 0.18133803.适配后:0.16035.


    
模拟器3:
分辨率为 480 * 800.状态栏高度Wie:36px.   尺寸小于设计图的.
控件高度为103dp  高度/屏幕高度 = 0.19375. 适配后:0.16125.



小米4: 
分辨率为 1080 x 1920  .状态栏高度Wie:60px.   
控件高度为103dp  高度/屏幕高度 = 0.16612904. 适配后:0.1609375.



小米MIX2: 
分辨率为 1080 x 2160  .状态栏高度Wie:66px. 底部虚拟导航键高度为:130px   
控件高度为103dp  高度/屏幕高度 = 0.13940887. 适配后:0.16108374.



OPPO R15:  
分辨率  1080 x 2280.  尺寸是 6.28 .  状态栏高度为:84px.
控件高度为103dp  高度/屏幕高度 = 0.13552631.  适配后:0.1609649.



华为p20: 
分辨率为 1080 x 2240  .状态栏高度Wie:85px.   
控件高度为103dp  高度/屏幕高度 = 0.13770053.适配后:0.16087344.



oppo R9s: 
分辨率为 1080 x 1920 .状态栏高度Wie:54px.   
控件高度为103dp  高度/屏幕高度 = 0.1609375. 适配后:0.1609375.

用今日头条的适配方案后再大屏手机中的高度比基本等于设计图中的高度比,这样在屏幕高度相差很大的真机环境中显示效果会好很多。今日头条适配方案更加的灵活,我们再适配的时候虽然是全局的修改,但是我们可以指定特定的界面上不适配(也就是把设置恢复为默认的设置),这样即使是第三方的界面只要有代码就可以选择适配适配。另外还可以的自由的配置是以宽度为基准还是以高度为基准点去适配,但是两者不能兼得。

四 今日头条适配方案到底可行吗?

那么问题来了,再日常开发中只是适配宽度的话,遇见的需求不多,适配高度确实是遇见不少,然后我再适配高度的时候发现了问题。当我们用今日头条适配方案在高度上去适配大屏手机的话(比如分辨率为1080X2160)那样计算出来的dpi的数值肯定会比原数值高好多。比如小米 MIX2 分辨率为 1080 X 2160 高度适配之后再高度纬度的dpi数值为523 那么就是100dp = 317px,正常情况的dpi为440 100dp = 275px。高度适配之后对宽度方向影响很大的。对下表的数据分析能看出,目前流行机型的宽度定大部分都在1080,高度大于1920的机型居多,再大屏手机里面我们要首选适配高度的问题,先来看下一个简单的适配问题。

需求: 开屏页logo展示位置。

设计稿: 1080px X 1920px 360dp X 640dp。

logo: 大小100dp X 100dp 水平居中,marginTop100dp。topMargin / 屏幕高度:0.15635。

测试机型: 小米四(1080X1920) vivo x21(1080X2280)。

真机数据未适配前:

未适配前:
小米4:  
10-12 10:28:25.146 12746-12746/cn.screen.adaptation E/WANG: getWidth300
10-12 10:28:25.146 12746-12746/cn.screen.adaptation E/WANG: getHeight300
10-12 10:28:25.146 12746-12746/cn.screen.adaptation E/WANG: topMargin / 屏幕高度0.15625

VIVO X21:
10-12 10:31:15.246 23724-23724/cn.screen.adaptation E/WANG: getWidth300
10-12 10:31:15.246 23724-23724/cn.screen.adaptation E/WANG: getHeight300
10-12 10:31:15.246 23724-23724/cn.screen.adaptation E/WANG: topMargin / 屏幕高度0.13157895

我们可以看到小米4手机的topMargin / 屏幕高度跟设计图的一致。VIVO X21就相差很大了。这样显示出来的logo的位置就会跟设计图设计的有很大的差距,这种差距是随着手机竖直分辨率的增大而增大。

真机适配后:

高度适配后:
小米4:  
10-12 10:28:25.146 12746-12746/cn.screen.adaptation E/WANG: getWidth300
10-12 10:28:25.146 12746-12746/cn.screen.adaptation E/WANG: getHeight300
10-12 10:28:25.146 12746-12746/cn.screen.adaptation E/WANG: topMargin / 屏幕高度0.15625

VIVO X21:
10-12 10:30:33.760 23502-23502/? E/WANG: getWidth356
10-12 10:30:33.760 23502-23502/? E/WANG: getHeight356
10-12 10:30:33.760 23502-23502/? E/WANG: topMargin / 屏幕高度0.15614036
    
我们可以明显的看到logo的topMargin / 屏幕高度基本跟设计搞的一致,这样就达到了logo在大多数机型上面显示的效果跟设计稿的一样。但是可以发现logo的宽高都增加了56px,这也是因为适配高度的时候更改了dpi的数值,dpi的数值偏大就会造成全局的dp转px的倍率变大,这样我们的logo的大小和该界面的其它的控件的大小都会有影响。    
总结:

屏幕适配任重而道远,我们要针对设计稿,针对界面,针对控件去选择我们的适配方式,技术好并不代表好用,有的时候会反其道而行之。本人还是很喜欢今日头条适配方案的,用注解做起来逼格瞬间提升,想再那个界面适配就在那个界面适配,想取消适配就取消适配,也就一个注解的事。另外还有一点就是,适配方案推出那么多时间也不短了,有几个开发者实战了呢?所谓实践出真理今日头条适配方案坑很多,我们一起慢慢踩~~欢迎大家提出文章里面的错误,大家共同学习!

参考 https://developer.android.google.cn/guide/practices/screens_support

欢迎关注:
我的掘金
我的简书
我的CSDN
我的Github

注解版今日头条适配方案 (供参考学习)

五 主流机型

注: 以下机型的dpi数值只有一部分得到真机验证,其余存在些许误差望更正,体现在(宽/density)这个数值上。

华为-荣耀系列:
机型分辨率ppi尺寸宽/density高/density
华为畅享81440x7202695.99360dp720dp
华为nova 21920x10804405360dp640dp
华为P91920x10804245.2360dp640dp
华为Mate 91920x10803735.9360dp640dp
华为P101920x10804325.1360dp640dp
华为Mate 10 Pro2160x10804026360dp720dp
华为nova 2s2160x10804026360dp720dp
华为畅享8 Plus2160x10804075.93360dp720dp
华为Mate 10 Pro2160x10804026360dp720dp
华为nova 2s2160x10804026360dp720dp
华为P20 Pro2240x10804086.1360dp746.7dp
华为P202244x10804285.8360dp748dp
华为nova 3e2280x10804325.84360dp760dp
华为nova 3i2340x10804096.3360dp780dp
华为nova 32340x10804096.3360dp780dp
华为Mate 102560x14404985.9360dp640dp
华为Mate 202560x14404826.1360dp640dp
华为Mate RS保时捷版2880x14405376360dp720dp
小米:
机型分辨率ppi尺寸宽/density高/density
小米红米61440x7202955.45360dp720dp
小米Max 21920x10803426.44360dp640dp
小米5X1920x10804015.5360dp640dp
小米61920x10804285.15360dp640dp
小米Max 21920x10803426.44360dp640dp
小米MIX2040x10803616.4360dp680dp
小米6X2160x10804036.0360dp720dp
小米MIX 2s2160x10804036.0360dp720dp
小米红米Note 52160x10804036.0360dp720dp
小米Max 32160x10803506.9360dp720dp
小米MIX 22160x10804036.0392.7dp785.5dp
小米8 SE2244x10804245.88360dp748dp
小米82248x10804026.21360dp749.3dp
小米8透明探索版2248x10804026.21360dp749.3dp
小米红米6 Pro2280x10804325.84360dp760dp
OPPO
机型分辨率ppi尺寸宽/density高/density
OPPO A571280x7202825.2360dp640dp
OPPO A831440x7202825.7360dp720dp
OPPO A51520x7202716.2360dp760dp
OPPO R9S1920X10804015.5360dp640dp
OPPO R111920x10804015.5360dp640dp
OPPO R11 Plus1920x10803676360dp640dp
OPPO R11s2160x10804016.0360dp720dp
OPPO R11s Plus2160x10803766.43360dp720dp
OPPO R152280x10804026.28360dp760dp
OPPO A32280x10804056.2360dp760dp
OPPO R172340x10804026.4360dp780dp
OPPO Find X2340x10804016.42360dp780dp
OPPO R17 Pro2340x10804026.4360dp780dp
VIVO
机型分辨率ppi尺寸宽/density高/density
vivo Y711440x7202696.0360dp720dp
vivo Y831520x7202706.22360dp760dp
vivo x7Plus1920x10803865.7360dp640dp
vivo X20Plus2160x10803766.43360dp720dp
vivo X202160x10804016.0360dp720dp
vivo Y972280x10804016.3360dp760dp
vivo X21屏幕指纹版2280x10804026.28360dp760dp
vivo X212280x10804026.28360dp760dp
vivo Z12280x10804036.26360dp760dp
vivo Y852280x10804036.26360dp760dp
vivo NEX2316x10803886.59360dp772dp
vivo X232340x10804026.4360dp780dp
魅族
机型分辨率ppi尺寸宽/density高/density
魅族魅蓝S61440x7202825.7360dp720dp
魅族151920x10804035.46360dp640dp
魅族PRO 71920x10804245.2360dp640dp
魅族魅蓝Note 61920x10804015.5360dp640dp
魅族16th2160x10804026360dp720dp
魅族16th Plus2160x10803726.5360dp720dp
魅族16 X2160x10804026.0360dp720dp
魅族15 Plus2560x14404945.95360dp640dp
魅族PRO 7 Plus2560x14405155.7360dp640dp
锤子
机型分辨率ppi尺寸宽/density高/density
锤子科技Smartisan T21920x10804454.95360dp640dp
锤子科技坚果Pro1920x10804015.5360dp640dp
锤子科技坚果Pro 2S2160x10804026.0360dp720dp
锤子科技坚果Pro 22160x10804026.0360dp720dp
锤子科技坚果R12240x10804036.17360dp746.7dp
  • 26
    点赞
  • 70
    收藏
    觉得还不错? 一键收藏
  • 25
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 25
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值