Android中.9.png圖片的使用過程和原理

1、Android中放置圖片資源的文件夾

Android中一般有drawable-ldpi、drawable-mdpi、drawable-hdpi、drawable-xhdpi、drawable-xxhdpi等放置圖片資源的文件夾,這幾個文件夾分別對應的像素密度為:

文件夾

對應的像素密度

drawable-ldpi

120dpi

drawable-mdpi

160dpi

drawable-hdpi

240dpi

drawable-xhdpi

320dpi

圖-1

另外自己可以創建一個默認的drawable文件夾,默認對應的像素密度為160dpi。

2、Android中view設置背景圖片時查找圖片資源的順序

Android中view在setBackground加載圖片時,通常會先去設備對應的像素密度的文件夾中去找對應的圖片,如果沒有找到就會去緊挨著的像素密度較高的文件夾中去找,然後再往上找,如果最高像素密度的文件夾中還沒有找到該圖片才會向像素密度較低的文件夾中去找。這是加載對應圖片的一個查找過程。

比如一個設備的像素密度為240dpi,應用程序有drawable、drawable-ldpi、drawable-mdp、drawable-hdpi、drawable-xhdpi、drawable-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方法的源碼為:

圖-2

這個方法裡面第14179行代碼,紅線標註部分,是通過Resources對象根據圖片資源resid去獲取圖片對應的drawable對象,getDrawable方法源碼如下:vcD4KPHA+PGltZyBzcmM9″/uploadfile/Collfiles/20140316/2014031609521611.jpg” alt=”\”>

圖-3

這個方法裡面通過loadDrawable方法返回一個Drawable對象,loadDrawable方法裡面傳入瞭一個TypedValue對象,而TypedValue對象是通過getValue方法獲得的,這裡可以通過代碼查看一下TypedValue對象中存放瞭哪些對應資源圖片的信息。

圖-4

通過demo代碼中,對代碼進行debug,發現,根據圖片資源resid獲取的TypeValued對象中保存的信息主要有density=240和string=“res/drawable-hdpi/about-logo.png”,density是指找到的圖片資源所在drawable-hdpi文件夾對應的像素密度,string是圖片資源的路徑。

實際上loadDrawable方法就是根據這個圖片資源的路徑去獲取到相應的Drawable對象的。此時我是將圖片放置到drawable-hdpi文件夾中的,那麼如果我將圖片移動到drawable-mdpi文件夾中,TypeValued值會一樣嗎?通過測試,發現如下結果:

圖-5

通過圖-4和圖-5得出的typedValue信息可知,其獲得的相應變量值是不一樣的,此時的density=160、string=”res/drawable-mdpi/about-logo.png”,density是drawable-mdpi對應的像素密度,那麼同樣的圖片放置在不同的資源文件夾中,得到的Drawable對象一樣嗎,通過證明,它們是不一樣的。

about_logo.png原始圖片大小為138*64像素,在同樣的480*800像素240dpi的模擬器上運行,其得到的Drawable對象信息如下:

圖片放置在drawable-mdpi文件夾下:

圖-6

圖片放置在drawable-hdpi文件夾下:

圖-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圖片在縮放過後,如何進行局部拉伸渲染到屏幕上的?

源碼跟蹤,在View的draw方法中根據Drawable對象將圖片作為背景繪制到指定區域中,點9圖的實際繪制過程在NinePatch的draw方法中,通過canvas對象調用瞭本地方法nativeDraw對圖片進行瞭繪制。至於如何繪制局部暫時看不到JNI方法的源碼。

圖-8

發佈留言