Android-Universal-Image-Loader是一个开源的UI组件程序,该项目的目的是提供一个可重复使用的仪器为异步图像加载,缓存和显示。
(1).使用多线程加载图片
(2).灵活配置ImageLoader的基本参数,包括线程数、缓存方式、图片显示选项等;
(3).图片异步加载缓存机制,包括内存缓存及SDCard缓存;
(4).采用监听器监听图片加载过程及相应事件的处理;
(5).配置加载的图片显示选项,比如图片的圆角处理及渐变动画。
常用的功能,网上都有了,现在讲一下,加载后手工
(1)圆角化等处理
/** * 加载图片 * * @param uri * @param imageView * @param options * @param spinner 进度条,可以要可以不要 * @param imageLoadingListener 图片加载监听器 * @param needRoundImage 图片是否圆角化 * */ public void display(String uri , ImageView imageView , DisplayImageOptions options , final ProgressBar spinner , ImageLoadingListener imageLoadingListener , final boolean needRoundImage){ try { if(options == null){ options = new DisplayImageOptions.Builder() // .showStubImage(R.drawable.default_photo_small) // .showImageForEmptyUri(R.drawable.default_photo_small) // .showImageOnFail(R.drawable.default_photo_small) .resetViewBeforeLoading(true) .cacheOnDisc(true) .imageScaleType(ImageScaleType.EXACTLY) // .imageScaleType(ImageScaleType.IN_SAMPLE_POWER_OF_2) .bitmapConfig(Bitmap.Config.RGB_565) // .displayer(new FadeInBitmapDisplayer(300)) .build(); } if(imageLoadingListener == null){ imageLoadingListener = new SimpleImageLoadingListener() { @Override public void onLoadingStarted(String imageUri, View view) { if(spinner != null){ spinner.setVisibility(View.VISIBLE); } } @Override public void onLoadingFailed(String imageUri, View view, FailReason failReason) { String message = null; switch (failReason.getType()) { case IO_ERROR: message = "Input/Output error"; break; case DECODING_ERROR: message = "Image can't be decoded"; break; case NETWORK_DENIED: message = "Downloads are denied"; break; case OUT_OF_MEMORY: message = "Out Of Memory error"; break; case UNKNOWN: message = "Unknown error"; break; } CLog.e(TAG, imageUri+"/t加载失败!"); if(spinner != null){ spinner.setVisibility(View.GONE); } } @Override public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) { /** * 圆角化处理 */ if(needRoundImage){ ((ImageView) view).setImageBitmap(toRoundBitmap(loadedImage)); } if(spinner != null){ CLog.d(TAG, imageUri+"/t加载成功!" ); spinner.setVisibility(View.GONE); } } }; } imageLoader.displayImage(uri, imageView , options , imageLoadingListener); } catch (Exception e) { CLog.e(TAG , "exception!" , e); } } /** * 转换图片成圆形 * @param bitmap * 传入Bitmap对象 * @return */ public Bitmap toRoundBitmap(Bitmap bitmap) { int width = bitmap.getWidth(); int height = bitmap.getHeight(); float roundPx; float left, top, right, bottom, dst_left, dst_top, dst_right, dst_bottom; if (width <= height) { roundPx = width / 2; left = 0; top = 0; right = width; bottom = width; height = width; dst_left = 0; dst_top = 0; dst_right = width; dst_bottom = width; } else { roundPx = height / 2; float clip = (width - height) / 2; left = clip; right = width - clip; top = 0; bottom = height; width = height; dst_left = 0; dst_top = 0; dst_right = height; dst_bottom = height; } Bitmap output = Bitmap.createBitmap(width, height, Config.ARGB_8888); Canvas canvas = new Canvas(output); final int color = 0xff424242; final Paint paint = new Paint(); final Rect src = new Rect((int) left, (int) top, (int) right, (int) bottom); final Rect dst = new Rect((int) dst_left, (int) dst_top, (int) dst_right, (int) dst_bottom); final RectF rectF = new RectF(dst); paint.setAntiAlias(true);// 设置画笔无锯齿 canvas.drawARGB(0, 0, 0, 0); // 填充整个Canvas paint.setColor(color); // 以下有两种方法画圆,drawRounRect和drawCircle // canvas.drawRoundRect(rectF, roundPx, roundPx, paint);// 画圆角矩形,第一个参数为图形显示区域,第二个参数和第三个参数分别是水平圆角半径和垂直圆角半径。 canvas.drawCircle(roundPx, roundPx, roundPx, paint); paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));// 设置两张图片相交时的模式,参考http://trylovecatch.iteye.com/blog/1189452 canvas.drawBitmap(bitmap, src, dst, paint); //以Mode.SRC_IN模式合并bitmap和已经draw了的Circle return output; }
(2)手工从缓存中取出bitmap
/** * 使用此加载框架的imageloader加载的图片,设置了缓存后,下次使用,手工从缓存取出来用,这时特别要注意,不能直接使用: * imageLoader.getMemoryCache().get(uri)来获取,因为在加载过程中,key是经过运算的,而不单单是uri,而是: * String memoryCacheKey = MemoryCacheUtil.generateKey(uri, targetSize); * * @return */ public Bitmap getBitmapFromCache(String uri){//这里的uri一般就是图片网址 List<String> memCacheKeyNameList = MemoryCacheUtil.findCacheKeysForImageUri(uri , imageLoader.getMemoryCache()); if(memCacheKeyNameList != null && memCacheKeyNameList.size() > 0){ CLog.d(TAG , "memCache size ============> " + memCacheKeyNameList.size()); for(String each:memCacheKeyNameList){ CLog.d(TAG , "memCache each ============> " + each); } return imageLoader.getMemoryCache().get(memCacheKeyNameList.get(0)); } return null; }
取出的key有可能有两个,如:
http://tizi-zujuan-thumb.oss.aliyuncs.com/qfactory_word_img/201405/ae/309e9b359d0541f87e40f0d8eCKS74F.gif
http://tizi-zujuan-thumb.oss.aliyuncs.com/qfactory_word_img/201405/ae/309e9b359d0541f87e40f0d8eCKS74F.gif_720x1280
(3)加载图片(没有imageview控件,只想加载图片的bitmap到内存中)
/** * 加载图片 * @param uri * @param listener */ public void loadImage(String uri , ImageLoadingListener listener){ // imageLoader.loadImage(uri, listener); options = new DisplayImageOptions.Builder() .showStubImage(R.drawable.default_photo_small) .showImageForEmptyUri(R.drawable.default_photo_small) .showImageOnFail(R.drawable.default_photo_small) // .resetViewBeforeLoading(true) .cacheOnDisc(true) .cacheInMemory(true) .imageScaleType(ImageScaleType.EXACTLY) // .imageScaleType(ImageScaleType.IN_SAMPLE_POWER_OF_2) // .bitmapConfig(Bitmap.Config.RGB_565) // .displayer(new FadeInBitmapDisplayer(300)) .resetViewBeforeLoading(true) .build(); imageLoader.loadImage(uri, options, listener); }
加载使用示例:
/** * 网络加载图片 * 这里需要注意的是,一个问题界面,会有多张如公式等小图,他们在加载过程中,都会到方法onLoadingStarted,但如果图多同时加载,后面几张图就可以加载不出来, * 会进入onLoadingCancelled方法,所以这里在onLoadingCancelled方法里再调用typesetDelegate.refresh();方法刷新一下图, * 图应该是下载下来了,只是不知道为什么到了onLoadingCancelled方法。 */ UniversalImageLoaderUtil.getInstance().loadImage(getBitmapUrl().trim(), new ImageLoadingListener() { @Override public void onLoadingStarted(String imageUri, View view) { CLog.d(TAG , "onLoadingStarted ===>" + imageUri); } @Override public void onLoadingFailed(String imageUri, View view, FailReason failReason) { CLog.e(TAG , "onLoadingFailed ===>" + imageUri); } @Override public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) { if(null == loadedImage){ return; } boolean bool = (Math.abs(loadedImage.getWidth() - TiKuBitmapSpan.this.getPresetWidth()) > TYPESET_THRESHOLD) || (Math.abs(loadedImage.getHeight() - TiKuBitmapSpan.this.getPresetHeight()) > TYPESET_THRESHOLD); /** * 调用refresh方法,刷新控件界面,那么又会重走该render方法,这里图片已在缓存,所以就不会再去网络加载,不会再走回这里造成死循环,一直刷新 */ typesetDelegate.refresh(bool); CLog.d(TAG , "onLoadingComplete bool===>" + bool); } @Override public void onLoadingCancelled(String imageUri, View view) { /** * 调用refresh方法,刷新控件界面,那么又会重走该render方法,这里图片已在缓存,所以就不会再去网络加载,不会再走回这里造成死循环,一直刷新 */ typesetDelegate.refresh(true); CLog.e(TAG , "onLoadingCancelled ===>" + imageUri); } });
这里特别要注意的是,同时加载多个url的bitmap,有可能会因超时等原因(具体我也不知道什么的原因),最后跳到监听器的onLoadingCancelled方法(此时图片也可能在后台加载了),所以对种特殊情况也要处理。
完整的工具类代码:
package com.yiduoyun.tiku.util; import java.util.List; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Bitmap.Config; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.PorterDuff.Mode; import android.graphics.PorterDuffXfermode; import android.graphics.Rect; import android.graphics.RectF; import android.view.View; import android.widget.ImageView; import android.widget.ProgressBar; import com.nostra13.universalimageloader.cache.disc.naming.Md5FileNameGenerator; import com.nostra13.universalimageloader.cache.memory.impl.LruMemoryCache; import com.nostra13.universalimageloader.core.DisplayImageOptions; import com.nostra13.universalimageloader.core.ImageLoader; import com.nostra13.universalimageloader.core.ImageLoaderConfiguration; import com.nostra13.universalimageloader.core.assist.FailReason; import com.nostra13.universalimageloader.core.assist.ImageLoadingListener; import com.nostra13.universalimageloader.core.assist.ImageScaleType; import com.nostra13.universalimageloader.core.assist.MemoryCacheUtil; import com.nostra13.universalimageloader.core.assist.QueueProcessingType; import com.nostra13.universalimageloader.core.assist.SimpleImageLoadingListener; import com.nostra13.universalimageloader.core.display.FadeInBitmapDisplayer; import com.nostra13.universalimageloader.core.display.RoundedBitmapDisplayer; import com.yiduoyun.tiku.R; /** * 使用开源图片加载框架Universal-Image-Loader * * @author chenwenbiao * @date 2013-11-1 上午11:21:45 * @version V1.0 */ public class UniversalImageLoaderUtil { private static String TAG = UniversalImageLoaderUtil.class.getName(); private static UniversalImageLoaderUtil universalImageLoaderUtil = null; private static ImageLoader imageLoader = null; private static DisplayImageOptions options = null; // private static ProgressBar spinner = null; /** * 返回图片加载工具类实例 * @return */ public static UniversalImageLoaderUtil getInstance() { if (universalImageLoaderUtil == null) { synchronized (UniversalImageLoaderUtil.class) { if (universalImageLoaderUtil == null) { universalImageLoaderUtil = new UniversalImageLoaderUtil(); } } } return universalImageLoaderUtil; } /** * 记得使用前初始化context * @param aContext */ public void setContext(Context aContext) { imageLoader = ImageLoader.getInstance(); // imageLoader.init(ImageLoaderConfiguration.createDefault(aContext)); ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(aContext.getApplicationContext()) .memoryCache(new LruMemoryCache(16 * 1024 * 1024)) // .memoryCacheSize(16 * 1024 * 1024) .discCacheSize(100 * 1024 * 1024) .denyCacheImageMultipleSizesInMemory() .discCacheFileNameGenerator(new Md5FileNameGenerator()) .tasksProcessingOrder(QueueProcessingType.LIFO) // .discCacheFileCount(100) // .writeDebugLogs() .threadPoolSize(10) .build(); /*File cacheDir = StorageUtils.getCacheDirectory(aContext); CLog.d(TAG , "cache direction =======> " + cacheDir); ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(aContext) .memoryCacheExtraOptions(480, 800) // default = device screen dimensions .discCacheExtraOptions(480, 800, CompressFormat.JPEG, 75, null) // .taskExecutor(null) // .taskExecutorForCachedImages(null) .threadPoolSize(3) // default .threadPriority(Thread.NORM_PRIORITY - 1) // default .tasksProcessingOrder(QueueProcessingType.FIFO) // default .denyCacheImageMultipleSizesInMemory() .memoryCache(new LruMemoryCache(2 * 1024 * 1024)) .memoryCacheSize(2 * 1024 * 1024) .memoryCacheSizePercentage(13) // default .discCache(new UnlimitedDiscCache(cacheDir)) // default .discCacheSize(50 * 1024 * 1024) .discCacheFileCount(100) .discCacheFileNameGenerator(new HashCodeFileNameGenerator()) // default .imageDownloader(new BaseImageDownloader(aContext)) // default // .imageDecoder(new BaseImageDecoder(true)) // default .defaultDisplayImageOptions(DisplayImageOptions.createSimple()) // default .writeDebugLogs() .build();*/ /*File cacheDir = StorageUtils.getCacheDirectory(aContext); ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(aContext) //如果图片尺寸大于了这个参数,那么就会这按照这个参数对图片大小进行限制并缓存 .memoryCacheExtraOptions(480, 800) // default=device screen dimensions .discCacheExtraOptions(480, 800, CompressFormat.JPEG, 75 , null) // .memoryCacheExtraOptions(40, 80) // default=device screen dimensions // .discCacheExtraOptions(40, 80, CompressFormat.JPEG, 75 , null) // .taskExecutor(AsyncTask.THREAD_POOL_EXECUTOR) // .taskExecutorForCachedImages(AsyncTask.THREAD_POOL_EXECUTOR) .threadPoolSize(3) // default .threadPriority(Thread.NORM_PRIORITY - 1) // default .tasksProcessingOrder(QueueProcessingType.FIFO) // default .denyCacheImageMultipleSizesInMemory() .memoryCache(new LruMemoryCache(2 * 1024 * 1024)) .memoryCacheSize(2 * 1024 * 1024) .discCache(new UnlimitedDiscCache(cacheDir)) // default .discCacheSize(50 * 1024 * 1024) .discCacheFileCount(100) .discCacheFileNameGenerator(new HashCodeFileNameGenerator()) // default .imageDownloader(new BaseImageDownloader(aContext)) // default .imageDecoder(new BaseImageDecoder(false)) // default .defaultDisplayImageOptions(DisplayImageOptions.createSimple()) // default // .enableLogging() .build(); */ try { imageLoader.init(config); } catch (Exception e) { CLog.e(TAG , "exception" , e); } } /** * 获取图片加载器实例 * @return */ public static ImageLoader getImageLoader(){ return imageLoader; } /** * 加载图片 * * @param uri * @param imageView * @param options * @param spinner 进度条,可以要可以不要 * @param imageLoadingListener 图片加载监听器 * @param needRoundImage 图片是否圆角化 * */ public void display(String uri , ImageView imageView , DisplayImageOptions options , final ProgressBar spinner , ImageLoadingListener imageLoadingListener , final boolean needRoundImage){ try { if(options == null){ options = new DisplayImageOptions.Builder() // .showStubImage(R.drawable.default_photo_small) // .showImageForEmptyUri(R.drawable.default_photo_small) // .showImageOnFail(R.drawable.default_photo_small) .resetViewBeforeLoading(true) .cacheOnDisc(true) .imageScaleType(ImageScaleType.EXACTLY) // .imageScaleType(ImageScaleType.IN_SAMPLE_POWER_OF_2) .bitmapConfig(Bitmap.Config.RGB_565) // .displayer(new FadeInBitmapDisplayer(300)) .build(); } if(imageLoadingListener == null){ imageLoadingListener = new SimpleImageLoadingListener() { @Override public void onLoadingStarted(String imageUri, View view) { if(spinner != null){ spinner.setVisibility(View.VISIBLE); } } @Override public void onLoadingFailed(String imageUri, View view, FailReason failReason) { String message = null; switch (failReason.getType()) { case IO_ERROR: message = "Input/Output error"; break; case DECODING_ERROR: message = "Image can't be decoded"; break; case NETWORK_DENIED: message = "Downloads are denied"; break; case OUT_OF_MEMORY: message = "Out Of Memory error"; break; case UNKNOWN: message = "Unknown error"; break; } CLog.e(TAG, imageUri+"/t加载失败!"); if(spinner != null){ spinner.setVisibility(View.GONE); } } @Override public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) { /** * 圆角化处理 */ if(needRoundImage){ ((ImageView) view).setImageBitmap(toRoundBitmap(loadedImage)); } if(spinner != null){ CLog.d(TAG, imageUri+"/t加载成功!" ); spinner.setVisibility(View.GONE); } } }; } imageLoader.displayImage(uri, imageView , options , imageLoadingListener); } catch (Exception e) { CLog.e(TAG , "exception!" , e); } } public void display(String uri , ImageView imageView , DisplayImageOptions options , final ProgressBar spinner , final boolean needRoundImage){ display(uri, imageView, options, spinner, null , needRoundImage); } /** * 加载图片,并将图片圆角化 * * @param uri * @param imageView * @param roundPixels */ public void display(String uri , ImageView imageView , int roundPixels){ try { if(roundPixels < 1){ roundPixels = 10000;//搞个巨大的数据,让它变圆 } options = new DisplayImageOptions.Builder() .showStubImage(R.drawable.default_photo_big) .showImageForEmptyUri(R.drawable.default_photo_big) .showImageOnFail(R.drawable.default_photo_big) .resetViewBeforeLoading(true) .cacheOnDisc(true) .imageScaleType(ImageScaleType.IN_SAMPLE_INT) .displayer(new RoundedBitmapDisplayer(roundPixels)) .bitmapConfig(Bitmap.Config.RGB_565) .displayer(new FadeInBitmapDisplayer(300)) .build(); imageLoader.displayImage(uri, imageView, options, new SimpleImageLoadingListener() { @Override public void onLoadingFailed(String imageUri, View view, FailReason failReason) { String message = null; switch (failReason.getType()) { case IO_ERROR: message = "Input/Output error"; break; case DECODING_ERROR: message = "Image can't be decoded"; break; case NETWORK_DENIED: message = "Downloads are denied"; break; case OUT_OF_MEMORY: message = "Out Of Memory error"; break; case UNKNOWN: message = "Unknown error"; break; } CLog.e(TAG, "load image error! message:" + message); } }); } catch (Exception e) { CLog.e(TAG , "exception!" , e); } } /** * 转换图片成圆形 * @param bitmap * 传入Bitmap对象 * @return */ public Bitmap toRoundBitmap(Bitmap bitmap) { int width = bitmap.getWidth(); int height = bitmap.getHeight(); float roundPx; float left, top, right, bottom, dst_left, dst_top, dst_right, dst_bottom; if (width <= height) { roundPx = width / 2; left = 0; top = 0; right = width; bottom = width; height = width; dst_left = 0; dst_top = 0; dst_right = width; dst_bottom = width; } else { roundPx = height / 2; float clip = (width - height) / 2; left = clip; right = width - clip; top = 0; bottom = height; width = height; dst_left = 0; dst_top = 0; dst_right = height; dst_bottom = height; } Bitmap output = Bitmap.createBitmap(width, height, Config.ARGB_8888); Canvas canvas = new Canvas(output); final int color = 0xff424242; final Paint paint = new Paint(); final Rect src = new Rect((int) left, (int) top, (int) right, (int) bottom); final Rect dst = new Rect((int) dst_left, (int) dst_top, (int) dst_right, (int) dst_bottom); final RectF rectF = new RectF(dst); paint.setAntiAlias(true);// 设置画笔无锯齿 canvas.drawARGB(0, 0, 0, 0); // 填充整个Canvas paint.setColor(color); // 以下有两种方法画圆,drawRounRect和drawCircle // canvas.drawRoundRect(rectF, roundPx, roundPx, paint);// 画圆角矩形,第一个参数为图形显示区域,第二个参数和第三个参数分别是水平圆角半径和垂直圆角半径。 canvas.drawCircle(roundPx, roundPx, roundPx, paint); paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));// 设置两张图片相交时的模式,参考http://trylovecatch.iteye.com/blog/1189452 canvas.drawBitmap(bitmap, src, dst, paint); //以Mode.SRC_IN模式合并bitmap和已经draw了的Circle return output; } /** * 使用此加载框架的imageloader加载的图片,设置了缓存后,下次使用,手工从缓存取出来用,这时特别要注意,不能直接使用: * imageLoader.getMemoryCache().get(uri)来获取,因为在加载过程中,key是经过运算的,而不单单是uri,而是: * String memoryCacheKey = MemoryCacheUtil.generateKey(uri, targetSize); * * @return */ public Bitmap getBitmapFromCache(String uri){//这里的uri一般就是图片网址 List<String> memCacheKeyNameList = MemoryCacheUtil.findCacheKeysForImageUri(uri , imageLoader.getMemoryCache()); if(memCacheKeyNameList != null && memCacheKeyNameList.size() > 0){ CLog.d(TAG , "memCache size ============> " + memCacheKeyNameList.size()); for(String each:memCacheKeyNameList){ CLog.d(TAG , "memCache each ============> " + each); } return imageLoader.getMemoryCache().get(memCacheKeyNameList.get(0)); } return null; } /** * @Title: getLocalBitmapFromCache * @Description: 这个方法只适用于加载本地图片的情况(考虑到计算key的过程),这里的key使用的是文本,注意添加内存缓存和获取Bitmap缓存的key一定要一致 * @author gaoshunsheng * @param @param uri * @return Bitmap * @throws */ // public Bitmap getLocalBitmapFromCache(String text) // { // return imageLoader.getMemoryCache().get(text); // } // // /** // * @Title: addBitmap2Memorycache // * @Description: 添加Bitmap对象到缓存中,这里一般用于本地资源图片,key为文本 // * @author gaoshunsheng // * @param @param text // * @param @param value // * @return void // * @throws // */ // public void addBitmap2Memorycache(String text, Bitmap value) // { // imageLoader.getMemoryCache().put(text, value); // } // // /** // * @Title: loadImageAsync // * @Description: 异步加载图片,不显示在某个View上 // * @author gaoshunsheng // * @param @param uri // * @param @param listener // * @return void // * @throws // */ // public void loadImageAsync(String uri, ImageLoadingListener listener) // { // imageLoader.loadImage(uri, listener); // } /** * 清除缓存 */ public void clearCache(){ if(imageLoader != null){ try { imageLoader.clearMemoryCache(); imageLoader.clearDiscCache(); } catch (Exception e) { CLog.e(TAG , "exception" , e); } } } /** * 加载图片 * @param uri * @param listener */ public void loadImage(String uri , ImageLoadingListener listener){ // imageLoader.loadImage(uri, listener); options = new DisplayImageOptions.Builder() .showStubImage(R.drawable.default_photo_small) .showImageForEmptyUri(R.drawable.default_photo_small) .showImageOnFail(R.drawable.default_photo_small) // .resetViewBeforeLoading(true) .cacheOnDisc(true) .cacheInMemory(true) .imageScaleType(ImageScaleType.EXACTLY) // .imageScaleType(ImageScaleType.IN_SAMPLE_POWER_OF_2) // .bitmapConfig(Bitmap.Config.RGB_565) // .displayer(new FadeInBitmapDisplayer(300)) .resetViewBeforeLoading(true) .build(); imageLoader.loadImage(uri, options, listener); } }
记得在Application类里加上初始化代码:
UniversalImageLoaderUtil.getInstance().setContext(this);