一、几个概念
1.1、分辨率:分辨率就是手机屏幕的像素点数,一般描述成屏幕的“宽×高”。
1.2、屏幕大小:屏幕大小是手机对角线的物理尺寸,以英寸(inch)为单位。
1.3、密度(dpi,dots per inch;或PPI,pixels per inch)
从英文顾名思义,就是每英寸的像素点数,数值越高当然显示越细腻。
dpi = 屏幕对角线上的像素点数/对角线的物理尺寸(inch)
实际密度与系统密度
1.3.1、“实际密度”就是我们自己算出来的密度,这个密度代表了屏幕真实的细腻程度。如上述例子中的440dpi就是实际密度
1.3.2、系统密度:每部安卓手机屏幕都有一个初始的固定密度,这些数值是120、160、240、320、480,我们权且称为“系统密度”
安卓对界面元素进行缩放的比例依据正是系统密度,而不是实际密度
drwable | dpi | density | 分辨率 |
---|---|---|---|
ldpi | 120dpi | 0.75 | 240*320px |
mdpi | 160dpi | 1 | 320*480px |
hdpi | 240dpi | 1.5 | 480*800px |
xhdpi | 320dpi | 2 | 720*1280px |
xxhdpi | 480dp | 3 | 1080*1920px |
dp 与px
dp (或dip density-independent) 旨在保证不同密度的屏幕上,显示的相对物理尺寸是一直的。举个例子。A和B是两款不同屏幕密度的手机,他们的物理尺寸(长和宽)一致,dp绘制图像,可以保证无论A和B的屏幕密度是多少,在A和B两款手机上显示的物理尺寸是一样的。
dp的官方说明:
A virtual pixel unit that you should use when defining UI layout, to express layout dimensions or position in a density-independent way.
The density-independent pixel is equivalent to one physical pixel on a 160 dpi screen, which is the baseline density assumed by the system for a "medium" density screen. At runtime, the system transparently handles any scaling of the dp units, as necessary, based on the actual density of the screen in use. The conversion of dp units to screen pixels is simple: px = dp * (dpi / 160). For example, on a 240 dpi screen, 1 dp equals 1.5 physical pixels. You should always use dp units when defining your application's UI, to ensure proper display of your UI on screens with different densities.
px = dp * (dpi / 160)
简单来说,以160dpi的设备为准,该设备上1dp = 1px。如果屏幕密度大,1dip代表的px就多,比如在320dpi的屏幕上,1dip=2px(即1dp代表2个像素)
二、Android系统寻址drawable图片的过程
Android 项目res中有多个drawable文件夹,如下图,在6个drawable文件夹下分别放置了同一张icon图片
drawable-ldpi
drawable-mdpi
drawable-hdpi
drawable-xhdpi
drawable-xxhdpi
drawable-xxxhdpi
2.1、app 在不同的屏幕密度的手机上是如何选择不同drawable文件夹下图片的?
当我们使用资源id来去引用一张图片时,Android会使用一些规则来去帮我们匹配最适合的图片。
什么叫最适合的图片?
比如我的手机屏幕密度是xxhdpi,那么drawable-xxhdpi文件夹下的图片就是最适合的图片。当我引用android_logo这张图时,如果drawable-xxhdpi文件夹下有这张图就会优先被使用,并且这张图片是不会被缩放的。如果drawable-xxhdpi文件夹下没有这张图时, 系统就会自动去其它文件夹下找这张图了,优先会去更高密度的文件夹下找这张图片。我们当前的场景就是drawable-xxxhdpi文件夹,然后发现这里也没有android_logo这张图,接下来会尝试再找更高密度的文件夹,发现没有更高密度的了,这个时候会去drawable-nodpi文件夹找这张图,发现也没有,那么就会去更低密度的文件夹下面找,依次是drawable-xhdpi -> drawable-hdpi -> drawable-mdpi -> drawable-ldpi。
所以寻址过程如下:
- 根据系统屏幕密度,查找对应最理想的图片(假设最理想的是drwable-xhdpi)
- 若理想dpi文件夹中图片不存在,则向高密度文件夹依次寻找(如xxhdpi->xxxhdpi)
- 向上寻址无果,则尝试在drawable-nodpi(密度无关)中寻找
-
drawable-nodpi 寻址无果,则尝试向下寻址图片(hdpi->mdpi->ldpi)
2.2、寻找到图片后,进行缩放操作
dpi范围-密度 表格
dpi范围 | 密度 |
---|---|
0dpi ~ 120dpi | ldpi |
120dpi ~ 160dpi | mdpi |
160dpi ~ 240dpi | hdpi |
240dpi ~ 320dpi | xhdpi |
320dpi ~ 480dpi | xxhdpi |
480dpi ~ 640dpi | xxxhdpi |
每一种密度的dpi范围都有一个最大值,这个最大值之间的比例就是图片会被系统自动放大的比例。
假设手机的屏幕密度为320dpi,寻址ic_arrow的图片. 目标图片选址文件夹为drawable-xhdpi(2倍图)
- 假设ic_arrow仅存放在了drawable-xxhdpi(3倍图)中,经过系统寻址后最终定位到drawable-xxhdpi中的图片。需要对图片进行等比缩小。
缩小比例 = 320(系统密度)/480(drawable-xxhdpi的密度) = 2/3
- 假设ic_arrow图片保存在了drawable-hdpi(1.5倍图区域)中,经过系统寻址后定位到drwable-hdpi中的图片。需要对图片进行定比放大。
放大比例 = 320(系统密度)/240(drawable-hdpi的密度) = 4/3
三、非标准屏幕密度 是如何寻址drawable图片
在第二节中 这对标准屏幕(屏幕密度为120、160、240、320、480、640的屏幕),drawable图片的寻址和放缩过程 我们基本清楚了。Android手机的屏幕千千万万,那些非标准密度的屏幕,图片的寻址和放缩规则也一样吗?
答案是肯定的。
针对标准密度屏幕和非标准密度屏幕。
3.1、图片寻址规则:
以Android手机屏幕的系统密度为基准,优先向上寻址;
寻址到最高的dpi仍未命名图片,则依次向下寻址,直至命中图片
3.2、图片的放缩规则:
图片放缩比例 = 系统密度/最终选址drawable文件夹代表的图片密度
假设 系统密度为440dpi,系统最终选址命中的图片文件夹为drawable-xxhdpi(代表480dpi)
则命中图片的放缩倍数 = 440/480
假设drawable-xxhdpi中图片的宽度为143,则放缩之后图片宽度为143*440/480=131
drwable | dpi | density | 分辨率 |
---|---|---|---|
ldpi | 120dpi | 0.75 | 240*320px |
mdpi | 160dpi | 1 | 320*480px |
hdpi | 240dpi | 1.5 | 480*800px |
xhdpi | 320dpi | 2 | 720*1280px |
xxhdpi | 480dp | 3 | 1080*1920px |
四、Android项目中 应该保存几份drawable-dpi文件
Google建议 为主流的机型尽量都做一套适配图片,这样UI显示效果最佳。
4.1、那问题来了,主流的屏幕密度是什么样的?
我没有找到2021年最新的统计数据,但找到了2018年的一份统计数据
统计数据显示 2018年主流的屏幕密度为xhdpi和xxhdpi,按照国内Android手机的更新换代频率推测,2021年 xxhpi应该已经成为主流的屏幕密度(480dpi)
4.2、国内优秀app是怎么做的?
我查看了微信、快手、今日头条、知乎等几款Android app,解压查看res文件夹下drawable适配情况,如下:
应用 | dpi分布 | 主要dpi |
---|---|---|
微信 | mdpi(102)、hdpi(588)、xhdpi(237)、xxhdpi(1044)、xxxdpi(104) | xxhdp |
快手 | hdpi(99)、mdpi(299)、xhdpi(346)、xxhdpi(6338)、xxxhdpi(279) | xxhdpi |
今日头条 | hdpi(3)、xhdpi(1886)、xxhdpi(784)、xxxhdpi(34) | xhdpi |
知乎 | xhdpi(133)、xxhdpi(1723)、xxxhdpi(47 | xxhdpi |
除了今日头条以xhdp为未主要适配对象,其他都重点适配了xxhdpi。
所以最终结论是:
Android手机app 仅适配一个主流屏幕密度即可(目前为止为xxhdpi),其他的屏幕密度依靠Android系统的图片寻址和放缩机制自动适配即可。
五、参考文章
https://www.jianshu.com/p/b3b67fd8aa24?utm_campaign=hugo&utm_medium=reader_share&utm_content=note&utm_source=weixin-friends
https://blog.csdn.net/bencheng06/article/details/84582065