1. 图片缩放后对齐的问题
在Android的开发中,如何使用图片对很多开发人员是一个很头痛的事。最常见的问题是在一台手机上调试好的UI,在不同dpi的手机上会变形。
Android提供了适配不同dpi的方案,每种dpi都有对应的资源目录。但是我们在开发程序时,对图片资源却不能这样处理,因为图片资源太大,如果我们每种类型的dpi都使用一套图片,应用的尺寸就会急剧膨胀,这可不是我们想看到的结果。
如果只有一套图片,但是有drawable,drawable-mdpi,drawable-hdpi等好几个目录,到底放在哪个目录下才能保证在每种dpi的设备上都显示正确了?这个问题无法给出一个简单的答案,只能是根据具体情况分析。
首先,我们要理解图片放在这些目录下会有什么样的后果:
q drawable目录:这个是所谓的“缺省”目录。如果把图片放在这里,从最终效果上讲,和放在drawable-mdpi目录下是一样的。
q drawable-nodpi目录:放在这个目录下的图片被装载进内存后,不会被缩放,所谓不缩放的意思是一张72x72像素的图片,在内存中的大小仍然是72x72像素。但是在不同的设备上这张图片看起来大小会不一样。
q drawable-mdpi,drawable-xdpi,dreawable-xhdpi:如果将图片放在这些和dpi相关的目录下,当程序运行在相同dpi的设备上时,不会被缩放;当运行在dpi不相同的设备上时,会根据dpi之间的换算关系进行缩放。
对于放在drawable-nodpi下的图片比较简单,不用太多解释。但是对于放在其他目录下的图片,Android对图片缩放的目的是什么呢?其实目的和在layout文件中使用dp做为数量单位一样,是为了让一张图片在不同dpi的设备上看起来大小一样。例如,一张放在drawable-mdpi目录下,大小为72x72像素的图片,如果运行在mdpi的设备上,大小还是72x72像素,但是运行在drawable-hdpi的设备上时,尺寸就会被放大为108x108像素。
如果不考虑图片缩放的效果,通过缩放能够让应用适应不同的屏幕,不是很好吗?为什么开发人员还会觉得头痛呢?这是因为在开发中,很多UI效果是通过拼图的方式实现的,如果两张图片缩放后无法对齐,哪怕只有一个像素,也会让效果失色。要解决这个问题,先让我们看看Android是如何进行缩放的。
Android上最常见的四种dpi是ldpi(120dpi),mdpi(160dpi),hdpi(240dpi)和xhdpi(320dpi)。这四种dpi的比值是120:160:240:320,化简后就是3:4:6:8。放在mdpi目录下的文件,在不同dpi的设备上缩放后的大小就是n*3/4,n*4/4,n*6/4和n*8/4(n为图片大小),如果希望放在mdpi下的图片缩放的效果最好,图片的尺寸必须是4的倍数,否则算法在处理图片的过程中,会不按比例的加入或删除几行(或几列)的像素,这样两张图片中的线条在缩放后就可能会相互错开几个像素。同理,放在ldpi下的图片,大小最好是3的倍数;放在hdpi下的图片,大小最好是6的倍数;放在xhdpi下的图片,大小最好是8的倍数。当然我们并不是要求每一张图都要符合这个规律,但是对于哪些需要精确对齐的图片,这样做还是有必要的。所以一套图片放在哪个目录下不是关键,关键是要确定图片的大小。
注意,这里谈论的图片缩放问题是指在图片装载进内存时就会进行的缩放。除此以外,很多widget在使用图片时还会再次缩放。当然Android也提供了方法禁止widget缩放。例如最常用的ImageView类可以通过设置scaleType属性为“center”来禁止缩放,或者把ImageView的宽度和高度设为“wrap_content”。但是要明白的是,这里设置了让ImageView不去缩放图片,图片装载时还是会被系统缩放的,很多开发人员在这里迷惑了,以为设置了Widget禁止缩放后,显示效果会和原图一样,折腾很长时间也不知道问题出在哪。
如果正在使用的widget也会去缩放图片,同样也要注意缩放的大小问题,原则也是要让每种图片按相同整数倍进行缩放。还要注意的是,在很多情况下,我们在布局上会让UI的宽度撑满整个屏幕。但是很多相同dpi,差不多大小的设备,它们的屏幕分辨率也可能有细微差别,这样如果根据屏幕宽度来缩放,可能有的设备看上去很完美,有的设备却还是对不齐。这种情况下,如果希望达到最佳效果,只能是根据dpi还有屏幕的宽度来动态的计算出一个最佳的宽度了,既要尽可能的占满屏幕,又要让缩放符合比例。但是这样就太麻烦了,而且也未必在所有的情况下都能有这么一个完美值。最好的办法还是在明白问题所在后,从UI设计上就要避免这种问题的出现。所以类似的问题没有一劳永逸的解决方案,需要我们根据实际情况来调整。
2. NinePatch图片的缩放
NinePatch
图片是Android中定义的一种特殊的图片格式,它通过在普通图片的边缘做标记的方式把一张图片分成9个部分。如下所示:
对NinePatch图缩放时,图片中1,3,7,9四个角保持不变。2号和8号区域水平缩放,4号和6号区域垂直缩放,5号区域则全缩放。
但是对NinePath图片最大的误解就是NinePatch的四个角是绝对不缩放的。除非图片是放在目录drawable-nodpi下,或者是运行在相同dpi的设备上,NinePath图片装载进内存时一样会先根据dpi的比值进行缩放。所谓四个角不缩放,是指装载进内存后,再调用缩放函数进行缩放时,四个角的尺寸不会再变化了。所以我们在制作圆角的NinePathc图时要注意,指定的四个角的大小也要符合前面讲的规律,根据所放目录的dpi来决定四个角的像素大小。
3. 为图片指定Density
Android装载图片时会根据当前设备的dpi进行缩放。我们也能在Bitmap类里或BitmapDrawable类里调用setDensity()和setTargetDesnsity()来改变一张图片的缩放比例。
publicfinal class Bitmap implements Parcelable {
public void setDensity(intdensity);
......
}
publicclass BitmapDrawable extends Drawable {
public void setTargetDensity(intdensity);
......
}
当调用这两个方法后,图片将不按照缺省的density来缩放,而是根据我们指定的density。