Android中.9.png图片的使用历程和原理

Android中.9.png图片的使用过程和原理

1、Android中放置图片资源的文件夹

Android中一般有drawable-ldpidrawable-mdpidrawable-hdpidrawable-xhdpidrawable-xxhdpi等放置图片资源的文件夹,这几个文件夹分别对应的像素密度为:

文件夹

对应的像素密度

drawable-ldpi

120dpi

drawable-mdpi

160dpi

drawable-hdpi

240dpi

drawable-xhdpi

320dpi

Android中.9.png图片的使用历程和原理


图-1

另外自己可以创建一个默认的drawable文件夹,默认对应的像素密度为160dpi

2Androidview设置背景图片时查找图片资源的顺序

AndroidviewsetBackground加载图片时,通常会先去设备对应的像素密度的文件夹中去找对应的图片,如果没有找到就会去紧挨着的像素密度较高的文件夹中去找,然后再往上找,如果最高像素密度的文件夹中还没有找到该图片才会向像素密度较低的文件夹中去找。这是加载对应图片的一个查找过程。

比如一个设备的像素密度为240dpi,应用程序有drawabledrawable-ldpidrawable-mdpdrawable-hdpidrawable-xhdpidrawable-xxhdpi六个文件夹,则在一个view设置背景图片时,查找图片的顺序为drawable-hdpi   ===== >  drawable-xhdpi  ====>  drawable-xxhdpi ====>   drawable-mdpi  ====>   drawable  ====>  drawable-ldpi

这个顺序可以通过一个小的demo自己验证,是android查找图片资源的规则,不是通过几句话就能说明的。

3、设置view背景图片的过程

源码跟踪:

View view = new View(this);

view.setBackgroundResource(R.drawable.about_logo);

使用这里是设置一个view的背景图片,setBackgroundResource方法的源码为:

Android中.9.png图片的使用历程和原理

图-2

这个方法里面第14179行代码,红线标注部分,是通过Resources对象根据图片资源resid去获取图片对应的drawable对象,getDrawable方法源码如下:

Android中.9.png图片的使用历程和原理

-3

这个方法里面通过loadDrawable方法返回一个Drawable对象,loadDrawable方法里面传入了一个TypedValue对象,而TypedValue对象是通过getValue方法获得的,这里可以通过代码查看一下TypedValue对象中存放了哪些对应资源图片的信息。

Android中.9.png图片的使用历程和原理

-4

通过demo代码中,对代码进行debug,发现,根据图片资源resid获取的TypeValued对象中保存的信息主要有density=240string=res/drawable-hdpi/about-logo.png”,density是指找到的图片资源所在drawable-hdpi文件夹对应的像素密度,string是图片资源的路径。

实际上loadDrawable方法就是根据这个图片资源的路径去获取到相应的Drawable对象的。此时我是将图片放置到drawable-hdpi文件夹中的,那么如果我将图片移动到drawable-mdpi文件夹中,TypeValued值会一样吗?通过测试,发现如下结果:

Android中.9.png图片的使用历程和原理

-5

通过图-4和图-5得出的typedValue信息可知,其获得的相应变量值是不一样的,此时的density=160string=”res/drawable-mdpi/about-logo.png”,densitydrawable-mdpi对应的像素密度,那么同样的图片放置在不同的资源文件夹中,得到的Drawable对象一样吗,通过证明,它们是不一样的。

about_logo.png原始图片大小为138*64像素,在同样的480*800像素240dpi的模拟器上运行,其得到的Drawable对象信息如下:

图片放置在drawable-mdpi文件夹下:

Android中.9.png图片的使用历程和原理

图-6

图片放置在drawable-hdpi文件夹下:

Android中.9.png图片的使用历程和原理

-7

通过代码测试得出如下数据:

图片放置的文件夹

对应像素密度

设备像素密度

得到的图片对应的Bitmap的宽高值

drawable-mdpi

160dpi

240dpi

207*96

drawable-hdpi

240dpi

240dpi

138*64

由此看出,放置在不同文件夹下面的相同的图片,在相同像素密度下所取得的图片Bitmap大小是不同的,如上表格中,drawable-mdpi下的图片实际上是进行了缩放的。

程序得到的图片宽度 实际图片宽度 设备像素密度 图片资源文件夹对应的像素密度

程序得到的图片高度 实际图片高度 设备像素密度 图片资源文件夹对应的像素密度

由此可以看出如果图片放置在低密度文件夹中,而要在高像素密度设备上显示时,其会先进行放大,然后再显示,这样就会导致高像素密度设备上显示模糊。

注:图片Bitmap放大的过程可以在源码中找到,源码在BitmapFactory.decodeStream方法中。详情请自己查看跟踪源码。

4、点9图片的使用方法

9图的处理过程和上面的普通png图片是一样的,会根据所放置的资源文件夹和屏幕的像素密度先进行缩放,在显示的时候点9图会再进行局部拉伸,所以如果将带圆角的点9图片放置在低像素密度资源文件夹下,当使用高像素密度设备显示时,图片会先进行放大在进行局部拉伸,这样会导致在放大过程中图片圆角和边缘被拉伸,显示时会变的模糊。

解决方案:

1、尽量将点9图片放置在高像素密度资源文件夹中,这样即使在低像素密度手机上显示时会先对图片进行缩小再进行局部拉伸,但是在低像素密度手机上运行应用时,所有使用点9图片的地方都会对图片进行一次计算缩放,影响性能;

2、针对不同像素密度手机做多套点9图片。

补充:点9图片在缩放过后,如何进行局部拉伸渲染到屏幕上的?

源码跟踪,在Viewdraw方法中根据Drawable对象将图片作为背景绘制到指定区域中,点9图的实际绘制过程在NinePatchdraw方法中,通过canvas对象调用了本地方法nativeDraw对图片进行了绘制。至于如何绘制局部暂时看不到JNI方法的源码。

Android中.9.png图片的使用历程和原理

-8