android: Drawable中getIntrinsicWidth()方法的一些思考
昨天在画一个自定义View的时候需要获取图片的宽高,用到了这个属性,特意记录一下。
getIntrinsicWidth() 中间的“Intrinsic” 是“固有”的含义, 很容易给人错觉好像是图片的原始宽高,但实际上不是。与它类似的API还有一个叫:getIntrinsicHeight()
事实上,上述两个API的确可以用来获取ImageView中显示图片的大小,但是在某些情况下,这两个API返回的大小可能与原图的大小不一致,比如原始图片大小是72px*72px,分别把原图放置在xhdpi,xxhdpi,xxxhdpi文件夹下,读取到的值就不一样了,这个值的大小与运行设备的当前dpi有关,不能单纯单从字面意思上把它理解成ImageView中图片的原始大小,这是不对的。
具体来说,比如你有一张图片,大小是70px * 70px, 如果你将这张图片放置到xxhdpi目录下,而你用来测试的手机的分辨率比如说是1920*1080(对应density为3,dpi为480), 那么这个时候你运行,调用ImageView的 getDrawable().getIntrinsicWidth() 方法拿到的宽度确实是70px, 但是你把图片换到 xhdpi目录下,再次使用这款手机测试运行,得到的宽度就不是 70px了,这个时候可能就会是105px。
为什么会这样?
从字面意思看,固有宽度,好像就是用来拿图片的固有宽高的,但是实际上Android设备众多,分辨率五花八门,为了保证一致的体验,系统在图片显示时会根据实际情况对将要显示的图片进行放大或缩小。
还是以刚才的例子举例,你有一张70px*70px的图片,当你放到xxhdpi目录下的时候,相当于告诉系统,我这张图片是为分辨率为1920*1080,像素密度为3的手机设计的,but, 市面上的手机千千万,那万一用户手上的手机分辨率不是1920*1080,像素密度不是3怎么办?难道就不显示这张图片了么?那肯定不行,这个时候系统就采取了一种折衷的办法,既然图片指定的分辨率与实际运行的设备分辨率不符,那我将图片放大缩小下就行了。
比如开发者将图片放到了xhdpi下,它对应的分辨率是1280*720,像素密度是2,实际运行的手机的分辨率是1920*1080,像素密度是3, 那也就是说图片设计的分辨率要比实际运行的手机的分辨率要小,OK,既然比手机运行的分辨率要小,那就放大一下图片,该放大多少呢,原来的像素密度是2,1920*1080的像素密度是3,那就放大 (3/2)倍就可以 了,所以你通过 getIntrinsicWidth() 拿到的大小就变成了:
width = 70 * (3/2) = 105px
同理,如果我把这张图片放到xxxhdpi目录下,对应的像素密度是4,而实际运行的设备的分辨率是1920*1080,像素密度是3, 这个时候系统就会认为原始图片较大,为迎合实际的设备分辨率,需要对其进行缩小,缩小为多少呢?缩小为:
width = 70* (3/4) = 53px
这样就能最大程度的保证图片的显示效果,不再需要程序员根据不同的设备分辨率来放置不同的图片。当然了,放大缩小肯定是有代价的,比如你的图片尺寸本来就很小,那么放大后很可能会模糊,这个时候为了保证体验还是需要把对应分辨率的图片放置到对应的图片文件夹的,典型的如app的splash界面, 这个界面有时会是一张大图,如果被系统放大的话,观感很不好,这个时候就需要特殊处理了,但原则上像一些应用内的小图标,UI 都会给出最契合当前主流分辨率的图片,即使放大一点看起来也没有多大差别。
为什么不能用getWidth(), getHeight()来获取图片的宽高?
getWidth() 和 getHeight() 其实拿到的是ImageView的宽高,它同样会受到设备分辨率的影响,比如我给ImageView设置为100dip*100dip,在像素密度为3的手机上,getWidth()和getHeight()拿到的结果就是 300px*300px, 在像素密度是4的手机上,拿到的结果就是400px*400px。不过它拿到的始终都是ImageView在当前设备上显示的宽高,不是ImageView中图片的宽高,所以就没必要继续讨论了。
那真的就不能用getWidth()和getHeight()来拿到宽高么? 其实吧,如果ImageView宽高定义的都是:wrap_content, 那这个时候 ImageView的 getWidth() 和 getIntrinsicWidth() 得到的值就一样了。
所以总结下来就是,getIntrinsicWidth() 会受到图片文件夹(xhdpi, xxhdpi, ...etc) 和 设备运行分辨率的影响,当该图片所在的图片文件夹对应的分辨率与设备运行的分辨率刚好一致时,拿到的宽度才是原始图片的宽度,否则这个宽度可能会变大或变小,要注意。
另外,利用BitmapFactory 从 资源库中加载图片时,也会有类似的问题,这个之前总结过,就不再赘述了,详情请参考:
BitmapFactory: 通过Bitmap的getWidth和getHeight方法获取到的尺寸与实际尺寸不符的问题