之前一直使用的是Material Design的图标库,下载下来以后直接放入了对应文件夹,什么尺寸对应什么dpi都没有仔细研究过。
最近在Toolbar上添加几个不是MD图标库内的图标时发现,放入的图标在显示时有时候感觉被放大了,有时候又显得模糊。让我对这个图标的尺寸和显示系统产生了好奇,折腾了一番,终于算是基本弄清楚了。
PX、DP和DPI
首先复习一下屏幕像素密度的知识:
- px:像素点
- dpi:像素密度,即每英寸像素数
- dp:屏幕密度独立单位
不同手机的像素密度不同,同px的元素可能有不同的物理尺寸,这不利于多屏幕的适配。因此Android以160dpi(每英寸160像素)为基准定义了单位dp。
即1dp的元素在160dpi的屏幕用1个像素点px显示,在320dpi的屏幕用2px显示,但它们的显示实际物理长度均为1/160=0.00625英寸。320dpi在同样大小的屏幕内用了更多的像素显示,所以显得更「清晰」。
hdpi、xhdpi、xxhdpi
为了方便换算和显示,Android预定义了一系列的dpi作为基准,例如mdpi定义为160dpi、hdpi定义为240dpi(实际上是一定的范围,但不影响理解)。
我们拿到的图片资源文件是以像素px为单位的,图标的显示却是以dp为单位的。在使用ImageView进行显示时,在规定好图标的长宽后其内容会自动缩放(不同的ScaleType缩放的逻辑不一样),像素过低的图标会显得「不清晰」。
适用于高dpi屏幕的图标可以包含大量细节,在低dpi时直接缩放的话效果可能会出现锯齿、模糊或无法识别其中的元素等情况。为了分别针对不同显示密度的屏幕进行优化,Android在drawable和mipmap文件夹内为不同dpi的屏幕建立了不同的文件夹,在不同的设备上读取相应dpi文件夹内的图片资源进行显示。
Toolbar的icon显示逻辑
与ImageView这样的控件相比,Toolbar显示icon的逻辑就显得比较简单粗暴。在Material Design中,Toolbar的推荐高度为56dp,其中icon的尺寸建议为24dp,那么icon在不同dpi下的实际像素尺寸如下:
ldpi | 120dpi | 0.75 | 18px |
mdpi | 160dpi | 1 | 24px |
hdpi | 240dpi | 1.5 | 36px |
xhdpi | 320dpi | 2 | 48px |
xxhdpi | 480dpi | 3 | 72px |
xxxhdpi | 640dpi | 4 | 96px |
这里的问题在于,Toolbar的MenuView在显示时读取图片资源后,不会检查是否应该缩放,而是直接居中显示。那么,如果你的图片资源经过屏幕像素密度换算后不是「恰好」24dp的话,最后显示的效果就会与期望的效果不一致。
例如,xhdpi文件夹存放的应该是48px的icon,如果放入了96px大小的icon的话在Toolbar上就会显得2倍大。反之,在xxxhdpi中放入48px的icon看上去就会额外小。这也是为什么MD图标库中的icon会给mdpi到xxxhdpi一套图标的原因。
解决方案
通常情况下Toolbar的icon都是纯色的png图片,体积非常小。以ic_search_white_24dp.png这个图标为例,mdpi文件夹内的图片大小为396字节,而xxxhdpi文件夹内的图片大小也只有915字节,即使全部使用最大尺寸的图标,对安装包体积的影响也微乎其微。
而且Toolbar的icon都是抽象的图标、细节不多,在低dip的设备上进行缩放时效果并没有太大差别,根据Google发布的设备屏幕尺寸分布情况,hdpi以上的设备也已经占了85%以上。所以如果想要减小安装包体积的话,Toolbar的icon是可以全部只使用一份96px*96px的图片资源,并存放在xxxhdpi中的。
至于其他只在ImageView等控件中显示的资源,如果只有一份的话,放在哪个文件夹内其实是无所谓的。
图标设计规范
根据Material Design的设计规范,Toolbar icon的尺寸应为24dp,触摸响应大小为48dp(Toolbar会自动进行设置),而在icon内部应有一定的留白,一般为2-4dp。因此对于一张96px的icon来说,图片内的四周应有12px左右的边距。
这里推荐一个神器 iconmonstr,在搜索框输入关键词找到想要的icon后,选择png、调整大小为96px、边距12px后,就可以直接下载了。