尊重原創 http://write.blog.csdn.net/postedit/26142025
html
代碼下載:http://download.csdn.net/detail/yuanzeyao2008/7363999java
假設你但願瞭解Volley框架的原理,歡迎閱讀android
前面已經說過,Volley框架可以用來請求String,XML,Json,Image以及一些本身定義的類型,這篇文章主要解說使用Volley請求大量圖片。並使用GridView展現出來。這個功能在很是多應用中都會用到,如PPS等類型的播放器,淘寶等等。框架
像這類應用無非就是要解決一下問題:ide
一、避免OOM,在使用GridView等控件顯示大量圖片時,假設對緩存處理不當,是很easy出現OOM的。
二、GridView錯位顯示,比方GridView中的某個ImageView等待某一個圖片(沒有返回)。然而你此時又滑動了GridView,那麼此時的GridView中的ImageView需要的是另一張圖片,假設你以前的圖片已經返回回來是不能顯示出來的。
把這兩個問題攻克了,此類問題也就差點兒相同了,剩下的就是效率問題了,這點Volley已經處理好了,咱們需要處理的就是以上兩點
函數
一、解決OOM
我就順便講解說決OOM的常用策略吧
OOM出現的狀況多是顯示的圖片很是大但是數量不是很是多。也有多是顯示的圖片很是多但是圖片不是很是大,咱們先看第一種狀況
http://developer.android.com/training/displaying-bitmaps/load-bitmap.html
咱們直接看官網怎麼處理這個問題。我翻譯了一下
對於某些圖片。分辨率每每比咱們需要的高,比方咱們使用Galley控件展現咱們使用手機拍攝的照片時,這些照片的分辨率都比咱們的屏幕分辨率高,因此在顯示的時候咱們應該減小這些圖片的分辨率,因爲此時高分辨率的圖片在視覺上不會給咱們帶來不論什麼區別。反而會佔用咱們移動設備的珍貴內存。如下咱們就學習怎樣將高分辨率的圖片減小它的分辨率工具
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), R.id.myimage, options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
String imageType = options.outMimeType;
咱們在使用BitmapFactory讀取圖片數據的時候,假設咱們把options.inJustDecodeBounds設置爲true,那麼BitmapFactory僅僅會讀取圖片的高度和寬度等信息。不會將圖片數據讀入內存
options另外一個屬性options.inSampleSize,BitmapFactory在解析圖片數據的時候,會將圖片的寬度和高度變爲原來的1/inSampleSize,這樣圖片大大小就會變爲1/(inSampleSize*inSampleSize),比方inSampleSize=2 那麼圖片大小會變爲原來的1/4
那麼inSampleSize這個值怎麼獲取呢。經過下面算法就能夠
/** optiosns 就是上面我獲得的options reqWidth 就是咱們需要圖片的寬度 reqHeight 就是咱們需要圖片的高度 */ 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; }
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(); //首先將這個值設置爲true, 因此僅僅會獲取高度和寬度 options.inJustDecodeBounds = true; BitmapFactory.decodeResource(res, resId, options); // 依據需要的寬度和高度 圖片的實際高度和寬度 計算縮小比例 options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); // 而後將該值設置爲false,真正解析圖片 options.inJustDecodeBounds = false; return BitmapFactory.decodeResource(res, resId, options); }
public class VolleyApplication extends Application { private static final String TAG = "VolleyApplication"; //請求隊列 private RequestQueue mRequestQueue; private static VolleyApplication instance; //用於圖片請求 private ImageLoader mImageLoader; //使用單例模式 public static VolleyApplication getInstance() { return instance; } public RequestQueue getRequestQueue() { return mRequestQueue; } public ImageLoader getImageLoader() { return mImageLoader; } @Override public void onCreate() { super.onCreate(); //初始化內存緩存文件夾 File cacheDir = new File(this.getCacheDir(), "volley"); /** 初始化RequestQueue,事實上這裏你可以使用Volley.newRequestQueue來建立一個RequestQueue,直接使用構造函數可以定製咱們需要的RequestQueue,比方線程池的大小等等 */ mRequestQueue=new RequestQueue(new DiskBasedCache(cacheDir), new BasicNetwork(new HurlStack()), 3); instance=this; //初始化圖片內存緩存 MemoryCache mCache=new MemoryCache(); //初始化ImageLoader mImageLoader =new ImageLoader(mRequestQueue,mCache); //假設調用Volley.newRequestQueue,那麼如下這句可以不用調用 mRequestQueue.start(); } }
public class MemoryCache implements ImageCache { private static final String TAG = "MemoryCache"; private LruCache<String, Bitmap> mCache; public MemoryCache() { //這個取單個應用最大使用內存的1/8 int maxSize=(int)Runtime.getRuntime().maxMemory()/8; mCache=new LruCache<String, Bitmap>(maxSize){ @Override protected int sizeOf(String key, Bitmap value) { //這種方法必定要重寫,否則緩存沒有效果 return value.getHeight()*value.getRowBytes(); } }; } @Override public Bitmap getBitmap(String key) { return mCache.get(key); } @Override public void putBitmap(String key, Bitmap value) { mCache.put(key, value); } }
(1) 首先給出Activity的佈局吧
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <GridView android:id="@+id/grid_image" android:layout_width="match_parent" android:layout_height="wrap_content" android:columnWidth="128dip" android:stretchMode="columnWidth" android:numColumns="2" android:verticalSpacing="1dip" android:horizontalSpacing="1dip" android:gravity="center" ></GridView> </LinearLayout>
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="wrap_content" android:layout_height="wrap_content" > <ImageView android:id="@+id/photo" android:layout_width="128dip" android:layout_height="128dip" android:src="@drawable/empty_photo" android:layout_centerInParent="true" /> </RelativeLayout>
public class ImageActivity extends Activity { private static final String TAG = "ImageActivity"; private GridView mGridView; private ImageAdapter adapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); this.setContentView(R.layout.imagelayout); initViews(); } private void initViews() { Log.i(TAG,"initViews"); mGridView=(GridView)this.findViewById(R.id.grid_image); adapter=new ImageAdapter(this,mGridView); mGridView.setAdapter(adapter); } }
public class ImageAdapter extends BaseAdapter implements OnScrollListener { private static final String TAG = "ImageAdapter"; private Context context; private String[] items=Images.imageThumbUrls; private GridView mGridView; /** * 標識是不是第一次運行,假設是第一次運行 onScrollStateChanged是不調用的 */ private boolean isFirstEnter; /** * 第一個可以看見的item */ private int firstSeeItem; /** * 記錄上一次可以看見的第一個,因爲假設已經到頂部。向下滑動GridView也會運行onScrollStateChanged 因此第一個可以見的沒有變化,那麼就不運行 */ private int orifirstItem; /** * 可以看見item的總數 */ private int totalSeeItem; public ImageAdapter(Context context,GridView mGridView) { this.context=context; this.mGridView=mGridView; //註冊這個是爲了在滑動的時候中止下載圖片,否則很是卡 mGridView.setOnScrollListener(this); isFirstEnter=true; } @Override public int getCount() { return items.length; } @Override public Object getItem(int position) { return items[position]; } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { Log.v(TAG, "imagedown--->getView"); ImageView imgView=null; if(convertView==null) { convertView=LayoutInflater.from(context).inflate(R.layout.imageitems, null); } imgView=(ImageView)convertView.findViewById(R.id.photo); imgView.setImageResource(R.drawable.empty_photo); //經過GridView的findViewByTag方法找到該View,防止錯位發生 imgView.setTag(items[position]); return convertView; } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { Log.v(TAG, "imagedown--->onScroll"); firstSeeItem=firstVisibleItem; totalSeeItem=visibleItemCount; if(isFirstEnter && visibleItemCount>0) { orifirstItem=firstVisibleItem; startLoadImages(firstSeeItem,totalSeeItem); isFirstEnter=false; } } @Override public void onScrollStateChanged(AbsListView view, int scrollState) { Log.v(TAG, "imagedown--->onScrollStateChanged"); if(orifirstItem!=firstSeeItem) { if(scrollState==SCROLL_STATE_IDLE) { startLoadImages(firstSeeItem,totalSeeItem); orifirstItem=firstSeeItem; }else { ImageUtils.cancelAllImageRequests(); } } } /** 開始下載圖片 first就是第一個可以看見的position total就是可以看見個Item個數 */ private void startLoadImages(int first,int total) { Log.v(TAG, "imagedown--->startLoadImages,first-->"+first+",total-->"+total); for(int i=first;i<first+total;i++) { ImageUtils.loadImage(items[i], mGridView); } } }
public class ImageUtils { private static final String TAG = "ImageUtils"; public static void loadImage(final String url,final GridView mGridView) { ImageLoader imageLoader=VolleyApplication.getInstance().getImageLoader(); ImageListener listener=new ImageListener() { ImageView tmpImg=(ImageView)mGridView.findViewWithTag(url); @Override public void onErrorResponse(VolleyError arg0) { //假設出錯。則說明都不顯示(簡單處理),最好準備一張出錯圖片 tmpImg.setImageBitmap(null); } @Override public void onResponse(ImageContainer container, boolean arg1) { if(container!=null) { tmpImg=(ImageView)mGridView.findViewWithTag(url); if(tmpImg!=null) { if(container.getBitmap()==null) { tmpImg.setImageResource(R.drawable.empty_photo); }else { tmpImg.setImageBitmap(container.getBitmap()); } } } } }; ImageContainer newContainer=imageLoader.get(url, listener,128,128); } /** * 取消圖片請求 */ public static void cancelAllImageRequests() { ImageLoader imageLoader = VolleyApplication.getInstance().getImageLoader(); RequestQueue requestQueue = VolleyApplication.getInstance().getRequestQueue(); if(imageLoader != null && requestQueue!=null){ int num = requestQueue.getSequenceNumber(); //這種方法是我本身寫的,Volley裏面是沒有的。因此僅僅能使用我給的Volley.jar纔有這個函數 imageLoader.drain(num); } } }
。