最近的一个demo是模拟手Q中发送图片界面,它实际上是一个由缩略图组成的预览界面,在开发过程中,遇到两个问题:
-
因为该界面是由缩略图组成,如果直接将原始图片加载到内存中来处理,有可能导致OOM(一张图片是很大的,况且有很多张图片),如何有效的获取原始图片的缩略图呢?
-
在demo的第一版中,图片能正确加载到界面,可是当我去滑动gridView时,会非常卡,这是什么原因导致,又该如何处理呢?
又是各种度娘,google,问题最终还是解决了,这里总结一下解决方案。
问题一:图片缩放
一般来说,取缩略图的方法是是使用BitmapFactory的decodeFile方法,通过BitmapFactory.Options的inSampleSize属性来控制缩放比例。属性值inSampleSize表示缩略图大小为原始的N分之几。然而,我们不可能将所有图片都加载到内存中来进行缩放操作,因为有些图片很大,而手机中内存又是很宝贵的,该怎么办呢?经研究发现,BitmapFactory.Options中有个inJustDecodeBounds属性,如果该值设置为true,,那么将不返回实际的bitmap,不给其分配实际的内存空间,而仅仅是一些图片大小信息。
有了这个属性,相应的方法就很简单了,可以先设置Options中有个inJustDecodeBounds为true,使用decodeFile获取图片的大小信息,计算出一个缩放比例(inSampleSize)后,再设置inJustDecodeBounds为false,将缩放后的图片加载到内存中。 具体代码如下:
private Bitmap getBitmapFromUrl(String path){
Bitmap bitmap = null;
//先从缓存中读取
bitmap = gridviewBitmapCaches.get(path);
if (bitmap != null) {
return bitmap;
}
BitmapFactory.Options options = new BitmapFactory.Options();
//设置inJustDecodeBounds为ture,来加载图片的边界信息
ptions.inJustDecodeBounds = true;
bitmap = BitmapFactory.decodeFile(path, options);
//计算出实际的缩放比例
int be = Math.min((int)(options.outHeight/(float)mImageHeight), (int)(options.outWidth/(float)mImageWidth));
if (be <= 0) {
be = 1;
}
options.inSampleSize = be;
//现在讲inJustDecodeBounds重新设置为false,获取实际的缩放后的图片
ptions.inJustDecodeBounds = false;
bitmap=BitmapFactory.decodeFile(path,options);
return bitmap;
}
问题二:GridView滑动的时候卡
在demo刚成型的时候,发现在滑动GridView的时候是很卡的,仔细想了想,终于发现问题所在,因为我是在主线程来加载图片,并更新UI,这样,IO本身就是一个耗时的操作,若将图片加载放到主线程来执行,必然会阻塞IO。知道了问题所在,解决问题的方法也就有了,即异步来加载图片。 Android规定只有主线程能更新UI,并提供了一些在新线程中来更新UI的方式,其中最简单,最轻量的莫过于使用AsyncTask了,AsyncTask的具体用法可以查阅相关的文档,这里给出具体的实现代码:
//这个是在GridViewAdapter中
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = converView;
if (view == null) {
view = getLayoutInflater().inflate(R.layout.qq_photo_select_item, null);
}
view.setLayoutParams(new GridView.LayoutParams(mImageWidth,
mImageHeight));
PhotoInfo imageInfo = getItem(position);
ImageView imageView = (ImageView) view
.findViewById(R.id.photo_select_item_photo_iv);
imageView.setAdjustViewBounds(false);
String path = imageInfo.getPath();
AsyncLoadImageTask task = new AsyncLoadImageTask(imageView);
task.execute(path);
}
//这里就是异步任务
private class AsyncLoadImageTask extends AsyncTask<String, Void, Bitmap> {
private String url = null;
private final WeakReference<ImageView> imageViewReference;
public AsyncLoadImageTask(ImageView imageview) {
super();
// TODO Auto-generated constructor stub
imageViewReference = new WeakReference<ImageView>(imageview);
}
@Override
protected Bitmap doInBackground(String... params) {
Bitmap bitmap = null;
this.url = params[0];
bitmap = getBitmapFromUrl(url);
MainActivity.gridviewBitmapCaches.put(url, bitmap);
return bitmap;
}
@Override
protected void onPostExecute(Bitmap resultBitmap) {
if (imageViewReference != null) {
ImageView imageview = imageViewReference.get();
imageview.setImageBitmap(resultBitmap);
}
}
}
问题解决,当然,这里还有很多优化的时候,比如在滑动GridView的时候,在它的滑动事件里,去recycle(对于那些不可见的convertView)相应的bitmap。