标签:
尊重原创 http://write.blog.csdn.net/postedit/26142025
代码下载:http://download.csdn.net/detail/yuanzeyao2008/7363999
假设你希望了解Volley框架的原理,欢迎阅读
前面已经说过,Volley框架能够用来请求String,XML,Json,Image以及一些自己定义的类型,这篇文章主要解说使用Volley请求大量图片。并使用GridView展示出来。这个功能在非常多应用中都会用到,如PPS等类型的播放器,淘宝等等。
像这类应用无非就是要解决一下问题:
1、避免OOM,在使用GridView等控件显示大量图片时,假设对缓存处理不当,是很easy出现OOM的。
2、GridView错位显示,比方GridView中的某个ImageView等待某一个图片(没有返回)。然而你此时又滑动了GridView,那么此时的GridView中的ImageView须要的是另外一张图片,假设你之前的图片已经返回回来是不能显示出来的。
把这两个问题攻克了,此类问题也就差点儿相同了,剩下的就是效率问题了,这点Volley已经处理好了,我们须要处理的就是以上两点
1、解决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); } } }
。
版权声明:本文博客原创文章。博客,未经同意,不得转载。
标签:
原文地址:http://www.cnblogs.com/lcchuguo/p/4632646.html