搜尋此網誌

2013-12-02

Loading Large Bitmaps Efficiently

KH.Chen
 
 
這篇文章還有分享一些catch跟清除memory的方法,不過那是用在listView跟gridView的,
我目前用的viewPager看來並不適合,所以我說明會著重在怎樣載入bitmap是比較恰當的。
 
首先假設我們有一個fragment,inflate這個fragment的時候我們設計了一個layout xml,
其中有一個imageView,我們要將某張照片載入到這個imageView,流程應該是怎樣?
 
1.取得ImageView的height跟weight,要如何取得呢?簡單的程式碼如下:
  DisplayMetrics dm = new DisplayMetrics();
        this.getWindowManager().getDefaultDisplay().getMetrics(dm);
        mScreenWidth = dm.widthPixels; // width
        mScreenHeight = dm.heightPixels;// height
 
上面的程式碼就可以拿到這支手機的屏幕解析度,你再依照layout的weight去算即可
 
2.壓縮bitmap,要怎樣壓縮bitmap讓imageView的畫質跟讀取速度取得平衡?
請看下面的程式碼:
 
    public static Bitmap decodeSampledBitmapFromFile(String filePath, int reqWidth, int reqHeight) {
        // First decode with inJustDecodeBounds=true to check dimensions
        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(filePath, options);
        // Calculate inSampleSize
        options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
        // Decode bitmap with inSampleSize set
        options.inJustDecodeBounds = false;
        return BitmapFactory.decodeFile(filePath, options);
    }
 
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;
    }
 
簡單說就是先算出這張bitmap的原始height跟width,再跟imageView的height跟width做比較
再用他寫的演算法,算出來inSampleSize的倍率要多少,這樣就是最適合此imageView的
bitmap壓縮結果。
 
 
3.display to UI:那要如何將這張bitmap顯示到UI上面呢?要是寫在main thread的話,bitmap太大,勢必會讓手機停頓延遲,這樣使用者就會覺得lag,爛程式,所以我們要用另一個thread
來讀取,最簡單的方法就是開一個asyncTask,當bitmap讀完的時候再更新UI,大概就是下面這樣
class BitmapWorkerTask extends AsyncTask<String, Void, Bitmap> {
protected Bitmap doInBackground(String... params) {
//在這邊拿到要設定bitmap的參數
            filePath = params[0];
            return decodeSampledBitmapFromFile(filePath, mScreenWidth, mScreenHeight);
        }
 
protected void onPostExecute(Bitmap bitmap) {
//在這邊判斷一下bitmap是否為null,更新main UI的imageView
            if (imageViewReference != null && bitmap != null) {
                final ImageView imageView = imageViewReference.get();
                if (imageView != null) {
                    imageView.setScaleType(ScaleType.CENTER);
                    imageView.setImageBitmap(bitmap);
                }
            }
 }
 
還有最後提醒一下,imageView能用setImageResource,就不要用setImageBitmap,兩者的速度
差別是天跟地,bitmap是取得外部的resource用的,一些內建有的圖檔就用setImageResource就好,這個viewPager performance的改善花了我很多時間,分享給大家,還有如果是用listView或是gridView這種大量的bitmap,那一定還需要自己實做memory release跟catch機制,裡面也有寫道,ViewPager是自己會delete沒有用到的fragment,所以比較沒有這個問題。
 

沒有留言 :

張貼留言