Android 使用imageLoader来加载显示图片



简介

Android上最让人头疼的莫过于从网络获取图片、显示、回收,任何一个环节有问题都可能直接OOM,这个项目或许能帮到你。Universal Image Loader for Android的目的是为了实现异步的网络图片加载、缓存及显示,支持多线程异步加载。它最初来源于Fedor Vlasov的项目,且自此之后,经过大规模的重构和改进


imageloader 加载图片的一般流程是先判断内存中是否有对应的Bitmap,再判断磁盘(disk)中是否有,如果没有就从网络中加载。最后根据原先在UIL中的配置判断是否需要缓存Bitmap到内存或磁盘中。Bitmap加载完后,就对它进行解析,然后显示到特定的ImageView中,


加载过程分析简图:


使用imageLoader可以实现

  1. 多线程下载图片,图片可以来源于网络,文件系统,项目文件夹assets中以及drawable中等
  2. 支持随意的配置ImageLoader,例如线程池,图片下载器,内存缓存策略,硬盘缓存策略,图片显示选项以及其他的一些配置
  3. 支持图片的内存缓存,文件系统缓存或者SD卡缓存
  4. 支持图片下载过程的监听
  5. 根据控件(ImageView)的大小对Bitmap进行裁剪,减少Bitmap占用过多的内存
  6. 较好的控制图片的加载过程,例如暂停图片加载,重新开始加载图片,一般使用在ListView,GridView中,滑动过程中暂停加载图片,停止滑动的时候去加载图片
  7. 提供在较慢的网络下对图片进行加载

初始化imageloader

方式一:

//创建默认的ImageLoader配置参数    
ImageLoaderConfiguration configuration = ImageLoaderConfiguration    
        .createDefault(this);    
//Initialize ImageLoader with configuration.    
ImageLoader.getInstance().init(configuration); 
方式二:
File cacheDir =getExternalStoragePath()+imageloader/Cache");  //获取自定义缓存路径
ImageLoaderConfigurationconfig = new ImageLoaderConfiguration   
          .Builder(this)   
          .memoryCacheExtraOptions(480, 800) // maxwidth, max height,即保存的每个缓存文件的最大长宽   
          .threadPoolSize(3)//线程池内加载的数量   
          .threadPriority(Thread.NORM_PRIORITY -2)   
          .denyCacheImageMultipleSizesInMemory()   
           .memoryCache(new UsingFreqLimitedMemoryCache(2* 1024 * 1024)) // You can pass your own memory cache implementation/你可以通过自己的内存缓存实现   
           .memoryCacheSize(2 * 1024 * 1024)     
          .discCacheSize(50 * 1024 * 1024)     
          .discCacheFileNameGenerator(newMd5FileNameGenerator())//将保存的时候的URI名称用MD5 加密   
           .tasksProcessingOrder(QueueProcessingType.LIFO)   
           .discCacheFileCount(100) //缓存的文件数量   
           .discCache(new UnlimitedDiscCache(cacheDir))//自定义缓存路径   ,这里是缓存到sd卡上 
           .defaultDisplayImageOptions(DisplayImageOptions.createSimple())   
           .imageDownloader(new BaseImageDownloader(this,5 * 1000, 30 * 1000)) // connectTimeout (5 s), readTimeout (30 s)超时时间   
           .writeDebugLogs() // Remove for releaseapp   
          .build();//开始构建   
ImageLoader.getInstance().init(config);  

// 获取SD卡路径
	public  String getExternalStoragePath() {
		// 获取SdCard状态
		String state = android.os.Environment.getExternalStorageState();

		// 判断SdCard是否存在并且是可用的

		if (android.os.Environment.MEDIA_MOUNTED.equals(state)) {

			if (android.os.Environment.getExternalStorageDirectory().canWrite()) {

				return android.os.Environment.getExternalStorageDirectory()
						.getPath();
			}
		}
		return null;

	}


方式三:


// 获取最大内存
		int maxMemorySize = (int) (Runtime.getRuntime().maxMemory());

		ImageLoaderConfiguration.Builder config = new ImageLoaderConfiguration.Builder(
				context);
		config.threadPriority(Thread.NORM_PRIORITY - 2);
		config.denyCacheImageMultipleSizesInMemory();// 不会在内存中缓存多个大小的图片
		config.diskCacheFileNameGenerator(new Md5FileNameGenerator());// 为了保证图片名称唯一
		config.diskCacheSize(maxMemorySize / 10);// 内存缓存大小默认是:app可用内存的1/8,这里设为1/10

		config.tasksProcessingOrder(QueueProcessingType.LIFO);
		config.writeDebugLogs(); // Remove for release app

		// Initialize ImageLoader with configuration.
		ImageLoader.getInstance().init(config.build());

最好是使用用方式三,这里是动态获取内存的大小 ,可以有效的避免一些内存溢出的效果  。。。。

当然要将图片缓存到本地自定义的路径中,可以使用用方式二与方式三相结合使用    


特别说明:


1、ImageLoaderConfiguration 配置中的.discCacheFileNameGenerator()方法是将缓存下来的文件以什么方式命名

          里面可以调用的方法有  1.new Md5FileNameGenerator() //使用MD5对UIL进行加密命名

                         2.new HashCodeFileNameGenerator()//使用HASHCODE对UIL进行加密命名

使用中的图像操作

配制一 

DisplayImageOptions options = new DisplayImageOptions.Builder()  
          .showImageOnLoading(R.drawable.ic_stub)            //加载图片时的图片  
          .showImageForEmptyUri(R.drawable.ic_empty)         //没有图片资源时的默认图片  
          .showImageOnFail(R.drawable.ic_error)              //加载失败时的图片  
          .cacheInMemory(true)                               //启用内存缓存  
          .cacheOnDisk(true)                                 //启用外存缓存  
          .considerExifParams(true)                          //启用EXIF和JPEG图像格式  
          .displayer(new RoundedBitmapDisplayer(20))         //设置显示风格这里是圆角矩形  
          .build(); 


配制二:

DisplayImageOptions options = new DisplayImageOptions.Builder()  
                          .showImageOnLoading(R.drawable.ic_launcher) //设置图片在下载期间显示的图片  
                          .showImageForEmptyUri(R.drawable.ic_launcher)//设置图片Uri为空或是错误的时候显示的图片  
                          .showImageOnFail(R.drawable.ic_launcher)  //设置图片加载/解码过程中错误时候显示的图片
                          .cacheInMemory(true)//设置下载的图片是否缓存在内存中  
                          .cacheOnDisc(true)//设置下载的图片是否缓存在SD卡中  
                          .considerExifParams(true)  //是否考虑JPEG图像EXIF参数(旋转,翻转)
                          .imageScaleType(ImageScaleType.EXACTLY_STRETCHED)//设置图片以如何的编码方式显示  
                          .bitmapConfig(Bitmap.Config.RGB_565)//设置图片的解码类型//  
                          .decodingOptions(android.graphics.BitmapFactory.Options decodingOptions)//设置图片的解码配置  
                          //.delayBeforeLoading(int delayInMillis)//int delayInMillis为你设置的下载前的延迟时间
//设置图片加入缓存前,对bitmap进行设置  
                         //.preProcessor(BitmapProcessor preProcessor)  
                          .resetViewBeforeLoading(true)//设置图片在下载前是否重置,复位  
                           .displayer(new RoundedBitmapDisplayer(20))//是否设置为圆角,弧度为多少  
                          .displayer(new FadeInBitmapDisplayer(100))//是否图片加载好后渐入的动画时间  
                           .build();//构建完成  



特别说明:

   1、imageScaleType(ImageScaleType imageScaleType) 是设置 图片的缩放方式
     缩放类型mageScaleType:

              EXACTLY :图像将完全按比例缩小的目标大小
              EXACTLY_STRETCHED:图片会缩放到目标大小完全
              IN_SAMPLE_INT:图像将被二次采样的整数倍
              IN_SAMPLE_POWER_OF_2:图片将降低2倍,直到下一减少步骤,使图像更小的目标大小
              NONE:图片不会调整
  2、.displayer(BitmapDisplayer displayer)   是设置 图片的显示方式
      显示方式displayer
             RoundedBitmapDisplayerint roundPixels)设置圆角图片
             FakeBitmapDisplayer()这个类什么都没做
             FadeInBitmapDisplayerint durationMillis)设置图片渐显的时间
SimpleBitmapDisplayer()正常显示一张图片


加载中使用的地址

String imageUri = "http://site.com/image.png"; // 网络图片  
String imageUri = "file:///mnt/sdcard/image.png"; //SD卡图片  
String imageUri = "content://media/external/audio/albumart/13"; // 媒体文件夹  
String imageUri = "assets://image.png"; // assets  
String imageUri = "drawable://" + R.drawable.image; //  drawable文件  

"http://site.com/image.png" // from Web
"file:///mnt/sdcard/image.png" // from SD card
"file:///mnt/sdcard/video.mp4" // from SD card (video thumbnail)
"content://media/external/images/media/13" // from content provider
"content://media/external/video/media/13" // from content provider (video thumbnail)
"assets://image.png" // from assets
"drawable://" + R.drawable.img // from drawables (non-9patch images)


设置显示图片

displayImage方法

可以根据需要使用不同的构造

1、ImageLoader.getInstance().displayImage(uri, imageView);
2、  ImageLoader.getInstance().displayImage(uri, imageView, options);
3、  ImageLoader.getInstance().displayImage(uri, imageView, listener);
4、  ImageLoader.getInstance().displayImage(uri, imageView, options, listener);
5、  ImageLoader.getInstance().displayImage(uri, imageView, options, listener, progressListener);
     imageUrl   图片的URL地址
     imageView  显示图片的ImageView控件  
     options    DisplayImageOptions配置信息 
     listener   图片下载情况的监听
     progressListener  图片下载进度的监听


loadImage方法

并常用的加载方法:

ImageView mImageView = (ImageView) findViewById(R.id.image);  
        String imageUrl = "";  

        ImageLoader.getInstance().loadImage(imageUrl, new SimpleImageLoadingListener(){  
  
            @Override  
            public void onLoadingComplete(String imageUri, View view,  
                    Bitmap loadedImage) {  
                super.onLoadingComplete(imageUri, view, loadedImage);  
                mImageView.setImageBitmap(loadedImage);  
            }  
              
        });  

 

初始化一个图片显示大小 

 ImageView mImageView = (ImageView) findViewById(R.id.image);  
        String imageUrl = "";  
          
        ImageSize mImageSize = new ImageSize(100, 100);  
          
        ImageLoader.getInstance().loadImage(imageUrl, mImageSize, new SimpleImageLoadingListener(){  
  
            @Override  
            public void onLoadingComplete(String imageUri, View view,  
                    Bitmap loadedImage) {  
                super.onLoadingComplete(imageUri, view, loadedImage);  
                mImageView.setImageBitmap(loadedImage);  
            }  
              
        });  


 

设置options参数加载 

ImageView mImageView = (ImageView) findViewById(R.id.image);  
        String imageUrl = "";  
        ImageSize mImageSize = new ImageSize(100, 100);  //设置图片显示大小 为100 X 100
          
        //显示图片的配置  
        DisplayImageOptions options = new DisplayImageOptions.Builder()  
                .cacheInMemory(true)  
                .cacheOnDisk(true)  
                .bitmapConfig(Bitmap.Config.RGB_565)  
                .build();  
          
        ImageLoader.getInstance().loadImage(imageUrl, mImageSize, options, new SimpleImageLoadingListener(){  
  
            @Override  
            public void onLoadingComplete(String imageUri, View view,  
                    Bitmap loadedImage) {  
                super.onLoadingComplete(imageUri, view, loadedImage);  
                mImageView.setImageBitmap(loadedImage);  
            }  
              
        });  




 

加载图片的监听

imageLoader.displayImage(imageUrl, imageView, options, new ImageLoadingListener() {  
    @Override  
    public void onLoadingStarted() {  
       //开始加载的时候执行  
    }  
    @Override  
    public void onLoadingFailed(FailReason failReason) {        
       //加载失败的时候执行  
    }   
    @Override   
    public void onLoadingComplete(Bitmap loadedImage) {  
       //加载成功的时候执行  
    }   
    @Override   
    public void onLoadingCancelled() {  
       //加载取消的时候执行  
  
    }}); 

特别说明 

  在图片加载成功之后(onlaodingComplete()方法中),可以对获取到的Bitmap进行各种大小设置,图形裁剪操作以及动画效果添加等,最后再加图片展示到控件上


  /** 
     * 图片加载第一次显示监听器 
     * @author Administrator 
     * 
     */  
    private static class AnimateFirstDisplayListener extends SimpleImageLoadingListener {  
          
        static final List<String> displayedImages = Collections.synchronizedList(new LinkedList<String>());  
  
        @Override  
        public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {  
            if (loadedImage != null) {  
                ImageView imageView = (ImageView) view;  
                // 是否第一次显示  
                boolean firstDisplay = !displayedImages.contains(imageUri);  
                if (firstDisplay) {  
                    // 图片淡入效果  
                    FadeInBitmapDisplayer.animate(imageView, 500);  
                    displayedImages.add(imageUri);  
                }  
            }  
        }       @Override  
                public void onLoadingStarted(String imageUri, View view) {  
                    spinner.setVisibility(View.VISIBLE);  
                }  
  
                @Override  
                public void onLoadingFailed(String imageUri, View view, FailReason failReason) {  
                    String message = null;  
                    switch (failReason.getType()) {  // 获取图片失败类型  
                        case IO_ERROR:              // 文件I/O错误  
                            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;  
                    }  
                    Toast.makeText(ImagePagerActivity.this, message, Toast.LENGTH_SHORT).show();  
   
                }  
            }
}
 

设置进度的方法

imageLoader.displayImage(imageUrl, imageView, options, new ImageLoadingListener() {  
    @Override  
    public void onLoadingStarted() {  
       //开始加载的时候执行  
    }  
    @Override  
    public void onLoadingFailed(FailReason failReason) {        
       //加载失败的时候执行  
    }      
    @Override      
    public void onLoadingComplete(Bitmap loadedImage) {  
       //加载成功的时候执行  
    }      
    @Override      
    public void onLoadingCancelled() {  
       //加载取消的时候执行  
    },new ImageLoadingProgressListener() {        
      @Override  
      public void onProgressUpdate(String imageUri, View view, int current,int total) {     
      //在这里更新 ProgressBar的进度信息  
      }  
    }); 


缓存的清理

@Override  
public void onDestroy() {  
          super.onDestroy();  
          imageLoader.clearMemoryCache();  
          imageLoader.clearDiskCache();  
} 


// 获取占用最大内存
		maxMemory = (int) Runtime.getRuntime().maxMemory();
                if (maxMemory < 40 * 1024 * 1024) {
			ImageLoader.getInstance().clearMemoryCache();
			ImageLoader.getInstance().clearDiskCache();
		}


 这里可以依据需求来设定,我这里采用的是检测 下内存,如果内存大小小于40M,那么在销毁的时候 ,清理缓存 ,这样处理可以有效的解决下加载图片过程中出现的内存溢出问题(例如 512M内存的手机) 

在ListView与GridView中滑动过程中停止加载图片

listView.setOnScrollListener(new PauseOnScrollListener(imageLoader, pauseOnScroll, pauseOnFling));    
gridView.setOnScrollListener(new PauseOnScrollListener(imageLoader, pauseOnScroll, pauseOnFling));

第一个参数就是我们的图片加载对象ImageLoader, 第二个是控制是否在滑动过程中暂停加载图片,如果需要暂停传true就行了,第三个参数控制猛的滑动界面的时候图片是否加载


补充说明 


如果经常出现OOM(别人那边看到的,觉得很有提的必要)
   ①减少配置之中线程池的大小,(.threadPoolSize).推荐1-5;
   ②使用.bitmapConfig(Bitmap.config.RGB_565)代替ARGB_8888;
   ③使用.imageScaleType(ImageScaleType.IN_SAMPLE_INT)或者.imageScaleType(ImageScaleType.EXACTLY);
   ④避免使用RoundedBitmapDisplayer.他会创建新的ARGB_8888格式的Bitmap对象;
   ⑤使用.memoryCache(new WeakMemoryCache()),不要使用.cacheInMemory();





 Android自定义控件ImageViwe(一)——依据控件的大小来设置缩放图片显示
    点击打开链接
    
 Android自定义ImageView(二)——实现双击放大与缩小图片
    点击打开链接
    
 Android自定义控件ImageViwe(三)——随手指进行图片的缩放
   点击打开链接
    
 Android自定义控件ImageViwe(四)——多点触控实现图片的自由移动  
    点击打开链接
    
 Android ListView分组排序显示数据
    点击打开链接
    
 Android自定义下拉刷新功能的ListView
   点击打开链接
    
 Android音乐播放器高级开发
    点击打开链接
    
 Android自定义控件之流式布局
 点击打开链接







早起的年轻人 CSDN认证博客专家 移动开发 项目管理 Java
只要用心去做,每一件事情还是有可能成功的,当然成功是没有界限的,只不过是达到自己心里的那个目标,公众号:我的大前端生涯,一个爱喝茶的程序员,通常会搞搞SpringBoot 、Herbinate、Mybatiys、Android、iOS、Flutter、Vue、小程序等.
©️2020 CSDN 皮肤主题: 代码科技 设计师:Amelia_0503 返回首页