2012年6月26日 星期二

Android GridView and ListView 非同步圖片讀取

Android GridView 和 ListView中若有圖片顯示的話,有時候會因為圖片size過大造成GridView 和 ListView顯示過慢。
此時可以使用非同步載入圖片的方式,先讓圖片顯示自設的default圖片,然後再開啟thread讀取實際圖片,當讀取完後立即覆蓋自設的default圖片。

程式碼如下:
pic_item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:orientation="vertical"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent"
  android:layout_gravity="center_horizontal"
>
  <ImageView android:id="@+id/pic_list_icon"
      android:layout_gravity="center_horizontal"
    android:layout_width="@dimen/pic_item_icon_width"
    android:layout_height="@dimen/pic_item_icon_height"
    android:scaleType="fitCenter"
  />
  
  <TextView android:id="@+id/pic_list_text"
    android:layout_gravity="center_horizontal"
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content"
    android:maxWidth="@dimen/pic_item_icon_width" 
    android:ellipsize="marquee"
    android:marqueeRepeatLimit="marquee_forever"
    android:scrollHorizontally="true"
  />
</LinearLayout>





AsyncImageFileLoader.java
package com.test.activity.imageloader;

import java.lang.ref.SoftReference;
import java.util.HashMap;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Handler;
import android.os.Message;
import android.util.Log;

public class AsyncImageFileLoader {
    private static String TAG = "AsyncImageFileLoader";
    private HashMap<String, SoftReference<Bitmap>> imageCache;  
    
    public AsyncImageFileLoader() {  
        imageCache = new HashMap<String, SoftReference<Bitmap>>();  
    }  
   
    public Bitmap loadBitmap(final String imageFile, final int show_width, final int show_height
 , final ImageCallback imageCallback)
    {  
        //如果此圖片已讀取過的話,將會暫存在cache中,所以可以直接從cache中讀取
        if (imageCache.containsKey(imageFile)) {  
            SoftReference<Bitmap> softReference = imageCache.get(imageFile);  
            Bitmap bmp = softReference.get();  
            if (bmp != null) {  
                return bmp;  
            }  
        }  
        
        final Handler handler = new Handler() {  
            public void handleMessage(Message message) {  
                imageCallback.imageCallback((Bitmap) message.obj, imageFile);  
            }  
        };  
        
        new Thread() {  
            @Override  
            public void run() { 
                //sleep 500ms 讓GridView可以先顯示所有item,否則會因為開始讀取圖片而使系統忙碌
 //造成圖片讀取完畢後才顯示所有item
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                
                Bitmap bmp = loadImageFromFile(imageFile, show_width, show_height);  
                imageCache.put(imageFile, new SoftReference<Bitmap>(bmp));  
                Message message = handler.obtainMessage(0, bmp);  
                handler.sendMessage(message);  
            }  
        }.start();  
        return null;  
   }  
   
   public Bitmap loadImageFromFile(String file, int display_width, int display_height) {  
       
       Bitmap bmp = readBitmap(file, display_width, display_height);
       return bmp;  
   }  
   
   public interface ImageCallback {  
       public void imageCallback(Bitmap imageBitmap, String imageFile);  
   }  
   
   private static int computeSampleSize(BitmapFactory.Options opts, int minSideLength, int maxNumOfPixels)
    {
        int initialSize = computeInitialSampleSize(opts, minSideLength, maxNumOfPixels);
        
        int roundedSize;
        
        if(initialSize <= 8)
        {
            roundedSize = 1;
            while(roundedSize < initialSize)
            {
                roundedSize<<=1;
            }
        }
        else
        {
            roundedSize = (initialSize+7)/8*8;
        }
        
        Log.d(TAG, "roundedSize = "+roundedSize);
        
        return roundedSize;
    }
    
    private static int computeInitialSampleSize(BitmapFactory.Options opts, int minSideLength, int maxNumOfPixels)
    {
        double w = opts.outWidth;
        double h = opts.outHeight;
        
        int lowerBound = (maxNumOfPixels == -1) ? 1 : (int)Math.ceil(Math.sqrt(w*h/maxNumOfPixels));
        int upperBound = (maxNumOfPixels == -1) ? 128 : (int)Math.min(Math.floor(w/minSideLength),Math.floor(h/minSideLength));
        
        if(upperBound < lowerBound)
        {
            return lowerBound;
        }
        
        if((maxNumOfPixels == -1) && (minSideLength == -1))
        {
            return 1;
        }
        else if(minSideLength == -1)
        {
            return lowerBound;
        }
        else
        {
            return upperBound;
        }
    }
    
   private Bitmap readBitmap(String img_file, int show_width, int show_height)
    {
        Log.d(TAG, "readBitmap");

        BitmapFactory.Options opt = new BitmapFactory.Options();            
        
        opt.inJustDecodeBounds = true; //設定BitmapFactory.decodeStream不decode,只抓取原始圖片的長度和寬度
        BitmapFactory.decodeFile(img_file, opt);        
        
        opt.inPreferredConfig = Bitmap.Config.ARGB_8888;
        opt.inPurgeable  = true;
        opt.inInputShareable = true;
 //計算適合的縮放大小,避免OutOfMenery
        opt.inSampleSize = computeSampleSize(opt, -1, show_width*show_height); 
        opt.inJustDecodeBounds = false;//設定BitmapFactory.decodeStream需decodeFile

        Bitmap bmp = BitmapFactory.decodeFile(img_file, opt);
        System.gc(); // system garbage recycle
        
        if(bmp != null)
        {    
            Log.d(TAG, "decodeFile success");            
            
            return bmp;
        }
        else
        {
            Log.d(TAG, "bmp == null");
            return null;
        }        
    }
}





main.java
import com.test.activity.imageloader.ImageCallback;

        
GridView mGridView = (GridView) findViewByOd(R.id.grid_view);
mGridView.setAdapter(new PictureListAdapter(this, mGridView, itemsArray, pathsArray))


public class PictureListAdapter extends BaseAdapter
{
        GridView mGridView = null;
        private LayoutInflater mInflater;      
        private List<String> Items;
        private List<String> Paths;        
        private Bitmap loadingIcon;        
        private AsyncImageFileLoader asyncImageFileLoader;  
        
        public PictureListAdapter(Context context, GridView gridView, List<String> items, List<String> paths)
        {
            mInflater = LayoutInflater.from(context);
            mGridView = gridView;
            Items = items;
            Paths = paths;  
            
            loadingIcon = BitmapFactory.decodeResource(context.getResources(), R.drawable.media_loading);
            
            asyncImageFileLoader = new AsyncImageFileLoader();              
        }
        
        @Override
        public int getCount() {
            // TODO Auto-generated method stub
            return Items.size();
        }

        @Override
        public Object getItem(int position) {
            // TODO Auto-generated method stub
            return Items.get(position);
        }

        @Override
        public long getItemId(int position) {
            // TODO Auto-generated method stub
            return position;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            // TODO Auto-generated method stub
            ViewHolder mHolder;
            
            if(convertView == null)
            {
                convertView = mInflater.inflate(R.layout.pic_item, null);
                
                mHolder = new ViewHolder();
                mHolder.icon = (ImageView) convertView.findViewById(R.id.pic_list_icon);
                mHolder.text = (TextView) convertView.findViewById(R.id.pic_list_text);
                
                convertView.setTag(mHolder);
            }
            else
            {
                mHolder = (ViewHolder) convertView.getTag();
            }    
            
            mHolder.text.setText(Items.get(position).toString());
            
            //設定此mHolder.icon的tag為檔名,讓之後的callback function可以針對此mHolder.icon替換圖片
            ImageView imageView = mHolder.icon; 
      imageView.setTag(Paths .get(position).toString());    
            
            Bitmap cachedBitmap = asyncImageFileLoader.loadBitmap(Paths.get(position).toString(), 200, 200, new ImageCallback() {  
                @Override
                public void imageCallback(Bitmap imageBitmap, String imageFile) {
                    // 利用檔案名稱找尋當前mHolder.icon
                    ImageView imageViewByTag = (ImageView) mGridView.findViewWithTag(imageFile);  
                    if (imageViewByTag != null) {  
                        if(imageBitmap != null)
                            imageViewByTag.setImageBitmap(imageBitmap);  
                    } 
                }  
            });  

            if(cachedBitmap != null)
                mHolder.icon.setImageBitmap(cachedBitmap);
            else
                mHolder.icon.setImageBitmap(loadingIcon); //顯示預設的圖片
            
            return convertView;
        }
        
        private class ViewHolder
        {
            TextView text;
            ImageView icon;
        }
        
} 

沒有留言:

張貼留言