android圖片:高效載入大圖
圖片有各種大小和形狀,很多時候我們要顯示的圖片的解析度大小遠大於手機螢幕的解析度,這時候我們通常是先對圖片做壓縮再載入到記憶體顯示,因為一方面原圖佔用太多記憶體,容易導致OOM,另一方面,只要壓縮適當,壓縮後的圖片在手機上的顯示效果和原圖沒太大差別,手機解析度就這麼多,你原圖解析度再高,顯示在手機螢幕上,也超不過其最大解析度,視覺上看起來效果一樣,佔用的記憶體卻大大不一樣。
BitmapFactory提供了對圖片操作的一系列方法,下面是對圖片壓縮的一個寫法。
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight) { // First decode with inJustDecodeBounds=true to check dimensions final BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeResource(res, resId, options); // Calculate inSampleSize options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); // Decode bitmap with inSampleSize set options.inJustDecodeBounds = false; return BitmapFactory.decodeResource(res, resId, options); }
開始時將inJustDecodeBounds屬性設定為true,避免為圖片分配記憶體,但是卻可以讀取圖片的大小,再通過這個大小計算出壓縮倍數inSampleSize,之後再將inJustDecodeBounds屬性設定為false,讀取壓縮後的圖片,併為其分配記憶體。下面看一下計算inSampleSize的具體實現。
public static int calculateInSampleSize( BitmapFactory.Options options, int reqWidth, int reqHeight) { // Raw height and width of image final int height = options.outHeight; final int width = options.outWidth; int inSampleSize = 1; if (height > reqHeight || width > reqWidth) { final int halfHeight = height / 2; final int halfWidth = width / 2; // Calculate the largest inSampleSize value that is a power of 2 and keeps both // height and width larger than the requested height and width. while ((halfHeight / inSampleSize) > reqHeight && (halfWidth / inSampleSize) > reqWidth) { inSampleSize *= 2; } } return inSampleSize; }
這裡有兩個問題:
1、 為什麼要先把高寬除以2.
2、 為什麼每次都是inSampleSize*2,也就是為什麼inSampleSize的值是2的冪。
高寬除以2,是因為存在這麼一種情況,原圖高寬除以2之後,大小比要顯示的高寬要小,這個時候如果inSampleSize*2的話,圖片會有些失真。例如原圖是100*100,而要顯示的圖的大小是80*80,如果設定inSampleSize為2,那麼壓縮後的圖為50*50,50*50的圖片顯示在80*80的ImageView中,會有些失真,所以這個時候inSampleSize應該為1,將100*100的圖片顯示在80*80的ImageView中,就不會失真。
inSampleSize的值是2的冪,是因為解碼器使用基於2的冪值, inSampleSize的其他任何值將被四捨五入到最近的2的冪(Note: thedecoder uses a final value based on powers of 2, any other value will berounded down to the nearest power of 2.)。所以我們這裡直接就把inSampleSize處理為2的冪值。
當然,計算inSampleSize的方法可以根據實際情況來設定,例如你就是要把圖片壓縮的佔用記憶體更小,不考慮失真,那麼完全可以把inSampleSize設定為一個更大的值。
根據上面的方法,假設想在100*100的ImageView中顯示圖片,就可以這麼寫了:
mImageView.setImageBitmap(
decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));