分享一个轻量级图片加载类 ImageLoader
ImageLoader这类的图片加载网络上一大推,像比较出名的有nostra13的-Image-Loader图片加载,xUtil的图片加载,还有Facebook的Fresco。很多,但本着求学的态度,最近在做项目时有图片加载这个需求就自己写了个轻量级的(本地)图片缓存加载功能,分享给各位。
里面涉及了LruCache,ExecutorService,处理大图的BitmapFactory原理,view.setTag().
好了,不多说,先一步一步来:
首先看一下我封装的类怎么使用:
//本地照片绝对路径 StringimageUrl=(String)t; //得到ImageView ImageViewgrid_item=holder.getView(R.id.grid_item); //设置tag,标记用的,反正图片显示错位 grid_item.setTag(imageUrl); /** *显示图片 * *@paramcontext *:上下文 *@paramimageView *:ImageView控件 *@paramsourcePath *:图片地址 *@paramr_Id *:默认图片id,R.drowable.id; *@paramcallback *:图片显示回调 */ newImageLoader().displayBmp(mContext,grid_item,imageUrl,R.drawable.img_bg,this);
是不是很简单。
接下来具体分析ImageLoader这个类:
都知道手机的内存有限,不可能将所有的图片都加进内存,所以android提供了一个LruCache方法,用到的算法是:近期最少使用算法,及在图片不断的加进缓存,最少使用的图片也在不断的移除缓存,从而避免的内存不够的问题。
LruCache的初始化代码如下:
publicImageLoader(){
//取应用内存的8/1作为图片缓存用
intcacheSize=maxMemory/8;
//得到LruCache
mLruCache=newLruCache<String,Bitmap>(cacheSize){
@Override
protectedintsizeOf(Stringkey,Bitmapbitmap){
returnbitmap.getByteCount();
}
};
}
/**
*将图片存储到LruCache
*/
publicvoidputBitmapToLruCache(Stringkey,Bitmapbitmap){
if(getBitmapFromLruCache(key)==null&&mLruCache!=null){
mLruCache.put(key,bitmap);
}
}
/**
*从LruCache缓存获取图片
*/
publicBitmapgetBitmapFromLruCache(Stringkey){
returnmLruCache.get(key);
}
LruCache就像HashMap一样利用put和get得到缓存的东西。
接着看图片的具体加载,先把代码贴出来:
/**
*显示图片
*
*@paramcontext
*:上下文
*@paramimageView
*:ImageView控件
*@paramsourcePath
*:图片地址
*@paramr_Id
*:默认图片id,R.drowable.id;
*@paramcallback
*:图片显示回调
*/
publicvoiddisplayBmp(finalContextcontext,finalImageViewimageView,finalStringsourcePath,finalintr_Id,
finalImageCallbackcallback){
finalStringpath;
if(!TextUtils.isEmpty(sourcePath)){
path=sourcePath;
}else{
return;
}
//先试着从缓存得到图片,path作为图片的key
Bitmapbmp=mLruCache.get(path);
if(bmp!=null){
if(callback!=null){
//回调图片显示
callback.imageLoad(imageView,bmp,sourcePath);
}
//imageView.setImageBitmap(bmp);
return;
}
//如果bmp==null,给imageView显示默认图片
imageView.setImageResource(r_Id);
//启动线程池
threadPoolUtils.getExecutorService().execute(newRunnable(){
Bitmapbitmap=null;
@Override
publicvoidrun(){
//TODOAuto-generatedmethodstub
try{
//加载图片地址对应的缩略图
bitmap=revitionImageSize(imageView,sourcePath);
}catch(Exceptione){
}
if(bitmap==null){
try{
//如果缩略图没加载成功显示默认设置的图片
bitmap=BitmapFactory.decodeResource(context.getResources(),r_Id);
}catch(Exceptione){
}
}
if(path!=null&&bitmap!=null){
//将缩略图放进缓存,path作为key
putBitmapToLruCache(path,bitmap);
}
if(callback!=null){
handler.post(newRunnable(){
@Override
publicvoidrun(){
//回调图片显示
callback.imageLoad(imageView,bitmap,sourcePath);
}
});
}
}
});
}
代码不是狠多,主要就是先从缓存加载图片,当加载图片为空时,再从手机的图片地址加载图片
bitmap=revitionImageSize(imageView,sourcePath);
加载缓存图片就不多说了,看的也明白,mLruCache.get(key);就这么简单
具体分析revitionImageSize()这个方法吧:
publicBitmaprevitionImageSize(ImageViewimageView,Stringpath)throwsIOException{
//得到布局ImageView的宽高
intimg_width=imageView.getWidth();
intimg_height=imageView.getHeight();
BufferedInputStreamin=newBufferedInputStream(newFileInputStream(newFile(path)));
BitmapFactory.Optionsoptions=newBitmapFactory.Options();
options.inJustDecodeBounds=true;
BitmapFactory.decodeStream(in,null,options);
in.close();
intheight=options.outHeight;
intwidth=options.outWidth;
Bitmapbitmap=null;
intinSampleSize=1;
//计算出实际宽高和目标宽高的比率
finalintheightRatio=Math.round((float)height/(float)img_height);
finalintwidthRatio=Math.round((float)width/(float)img_width);
//选择宽和高中最小的比率作为inSampleSize的值,这样可以保证最终图片的宽和高
//一定都会大于等于目标的宽和高。
inSampleSize=heightRatio<widthRatio?heightRatio:widthRatio;
//调用上面定义的方法计算inSampleSize值
options.inSampleSize=inSampleSize;
options.inJustDecodeBounds=false;
in=newBufferedInputStream(newFileInputStream(newFile(path)));
bitmap=BitmapFactory.decodeStream(in,null,options);
in.close();
returnbitmap;
}
代码我也写了注释了,一般在加载图片时都有对图片一定的压缩处理避免OOM,所以上面的处理方法也是挺常见的,对要显示图片根据imageview控件大小进行一定的压缩。
如果对图片压缩处理不是很理解的朋友这么我简单解释一下:
首先加载完图片:
BufferedInputStreamin=newBufferedInputStream(newFileInputStream(newFile(path)));
然后:
options.inJustDecodeBounds=true;
再接着:
intheight=options.outHeight; intwidth=options.outWidth;
这时候注意程序并没有把图片真正的加载进来,options.inJustDecodeBounds=true;
这句在起作用,但图片的宽和高的信息我们却得到了,就可以处理压缩图片了!
压缩完图片再:
options.inJustDecodeBounds=false;
重新得到压缩后的图片:
bitmap=BitmapFactory.decodeStream(in,null,options);
解释完毕。
仔细看代码的同学会发现displayBmp()方法里面有个回调参数:
回调接口如下:
/**
*显示图片回调
*
*@authorAdministrator
*
*/
publicinterfaceImageCallback{
publicvoidimageLoad(ImageViewimageView,Bitmapbitmap,Object...params);
}
具体实现是在显示图片的地方回调的:
/**
*图片缓存回调
*/
@Override
publicvoidimageLoad(ImageViewimageView,Bitmapbitmap,Object...params){
if(imageView!=null&&bitmap!=null){
Stringurl=(String)params[0];
//判断这里的url是否对应imageView.getTag()
//如果将这句判断去掉那么就会出现经常出现的图片显示错位问题!!!!
if(url!=null&&url.equals((String)imageView.getTag())){
((ImageView)imageView).setImageBitmap(bitmap);
}
}
}
代码注释的地方也写了,不太理解的同学可以私信交流,另外附上我githubgithub连接上的源码,可以下载下了运行方便好理解:
为了你方便使用,在你的项目中添加如下依赖即可:
dependencies{
compile'com.zts:imageloader:1.1.1'
}