Android图片加载之Glide应用分析

一、引言

现在Android上的图片加载框架非常成熟,从最早的老牌图片加载框架UniversalImageLoader,到后来Google推出的Volley,再到后来的新兴军Glide和Picasso,当然还有Facebook的Fresco。每一个都非常稳定,功能也都十分强大。作为开发人员,虽然它们的使用场景有重合,但还是有必要去了解每个框架的基本功能及使用方法。

对于Glide这个加载图片的框架,很多人都在用,我之前使用的是UIL,近两年也有在用Glide和Fresco,但Gilde是Google推荐的加载图片框架,功能非常强大,而且还有Google专人维护,所以有必要整理一下Glide的使用。

二、Glide介绍

在泰国举行的谷歌开发者论坛上,谷歌为我们介绍了一个名叫 Glide 的图片加载库,作者是bumptech。这个库被广泛的运用在google的开源项目中,包括2015年google I/O大会上发布的官方APP。

Glide是一个快速高效的Android图片加载库,注重于平滑的滚动。Glide提供了易用的API,高性能、可扩展的图片解码管道(decode pipeline),以及自动的资源池技术。Glide 支持拉取,解码和展示视频快照,图片,和GIF动画。Glide的API很灵活,开发者可以插入和替换成自己喜爱的任何网络栈。默认情况下,Glide使用的是一个定制化的基于HttpUrlConnection的栈,但同时也提供了与Google Volley和Square OkHttp快速集成的工具库。

Glide是谷歌为我们推荐的一个图片加载库。为什么要选择使用Glide呢?

  • 代码有人维护,不至于出现问题,项目组都搞不定的时候问题无法解决(ImageLoader已没人维护了)
  • 代码简洁,可读性很好(Fresco是一个非常优秀的库,但是配置稍显麻烦,同时代码风格读起来有些生疏)
  • 功能强大(400多k的包,包含很多功能)

虽然Glide 的主要目标是让任何形式的图片列表的滚动尽可能地变得更快、更平滑,但实际上,Glide几乎能满足你对远程图片的拉取/缩放/显示的一切需求。

GitHub : Glide
GitHub : 中文文档说明

另有一些基于Glide的优秀库

  • glide-transformations : 一个基于Glide的transformation库,拥有裁剪,着色,模糊,滤镜等多种转换效果
  • GlidePalette : 一个可以在Glide加载时很方便使用Palette的库

三、Glide应用

这里介绍了一些Glide常用用法,有如何加载图片,Glide的缓冲设置,Glide设置圆角,Glide设置图片的background,Glide加载GIF图片等,通常项目中也就用到这些内容。

3.1 依赖导入

在AS的build.gradle文件中添加

dependencies {
  implementation 'com.github.bumptech.glide:glide:4.8.0'
  annotationProcessor 'com.github.bumptech.glide:compiler:4.8.0'
}

3.2 布局

Glide不需要特定的布局文件,直接使用ImageView。

3.3 应用

简单应用

    Glide.with(fragment)
        .load(url)
        .into(imageView);
    // For a simple view:
    @Override public void onCreate(Bundle savedInstanceState) {
      ...
      ImageView imageView = (ImageView) findViewById(R.id.my_image_view);

      GlideApp.with(this)
              .load("http://xmamiga/gEgYUd.jpg")
              .into(imageView);
    }

    // For a simple image list:
    @Override 
    public View getView(int position, View recycled, ViewGroup container) {
        final ImageView myImageView;
        if (recycled == null) {
            myImageView = (ImageView) inflater.inflate(R.layout.my_image_view, container, false);
        } else {
            myImageView = (ImageView) recycled;
        }

        String url = myUrls.get(position);

        GlideApp
            .with(myFragment)
            .load(url)
            .centerCrop()
            .placeholder(R.drawable.loading_spinner)
            .into(myImageView);

        return myImageView;
    }

其他配置

     .with() //图片加载的环境:1,Context对象。2,Activity对象。3,FragmentActivity对象。4,Fragment对象
     .load() //加载资源:1,drawable资源。2,本地File文件。3,uri。4,网络图片url。5,byte数组(可以直接加载GIF图片)
     .placeholder() //图片占位符
     .error() //图片加载失败时显示
     .crossFade() //显示图片时执行淡入淡出的动画默认300ms
     .dontAnimate() //不执行显示图片时的动画
     .override() //设置图片的大小
     .centerCrop() //和 fitCenter() 图片的显示方式
     .animate() //view动画 2个重构方法
     .transform() bitmap转换
     .bitmapTransform() //bitmap转换。比如旋转,放大缩小,高斯模糊等(当用了转换后你就不能使用.centerCrop()或.fitCenter()了。)
     .priority(Priority.HIGH) //当前线程的优先级
     .signature(new StringSignature(“ssss”))
     .thumbnail(0.1f) //缩略图,3个重构方法:优先显示原始图片的百分比(10%)
     .listener() //异常监听
     .into() //图片加载完成后进行的处理:1,ImageView对象。2,宽高值。3,Target对象

更多配置实例:

    Glide.with(AppContext.context())
                .load(url)
                .placeholder(R.mipmap.placeholdermid) //占位符
                .error(R.mipmap.placeholdermid)       //错误占位符
                .dontAnimate()//没有任何淡入淡出效果
                .override(640, 428)//调整图片大小
                .diskCacheStrategy(DiskCacheStrategy.ALL)
                .priority(Priority.HIGH)//优先级
                .into(iv);


3.4 各路径图片加载方法

从文件加载图片

    File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),"Test.jpg");
    Glide.with(context).load(file).into(imageViewFile);

从资源id加载图片

    int resourceId = R.mipmap.ic_launcher;
    Glide.with(context).load(resourceId).into(imageViewResource);

设置图片显示效果(圆角、圆形、高斯模糊、蒙板、裁剪等等)

Glide.with(this).load(R.mipmap.ic_image_sample)     //模糊
    .bitmapTransform(new BlurTransformation(this))     //圆角
    .bitmapTransform(new RoundedCornersTransformation(this, 24, 0, RoundedCornersTransformation.CornerType.ALL))     //遮盖
    .bitmapTransform(new MaskTransformation(this, R.mipmap.ic_launcher))     //灰度
    .bitmapTransform(new GrayscaleTransformation(this))     //圆形
    .bitmapTransform(new CropCircleTransformation(this))
    .into(mResultIv);

3.5 加载资源

Glide支持网络资源、assets资源、Resources资源、File资源、Uri资源、字节数组

    Glide.with(this).load("http://pic9/258/a2.jpg").into(iv);
    Glide.with(this).load("file:///xxx.jpg").into(iv);
    Glide.with(this).load(R.mipmap.ic_launcher).into(iv);
    Glide.with(this).load(file).into(iv);
    Glide.with(this).load(uri).into(iv);
    Glide.with(this).load(byte[]).into(iv); 

3.6 加载gif图片

加载静态gif图片(静态就是gif相当于一张图片)

    Glide.with(this).load(imageUrl).asBitmap().into(iv);

加载动态gif图片(gif是动的)

    Glide.with(this).load(imageUrl).asGif().into(iv);

显示本地视频
Glide 还能显示视频!只要他们是存储在手机上的。假设你通过让用户选择一个视频后得到了一个文件路径:

String filePath = "/storage/emulated/0/Pictures/example_video.mp4";
Glide.with(context).load(Uri.fromFile(new File( filePath))).into(iv);

这里需要注意的是,这仅仅对本地视频起作用。如果没有存储在该设备上的视频(如一个网络 URL 的视频),它是不工作的!

3.7 设置加载动画

默认是淡入淡出动画

    Glide.with(this)
        .load("http://xmamiga.com/photo/1f/1f7a.jpg")
        .crossFade(int duration)//去减慢(或加快)动画
        .into(iv);

使用 crossFade()

    Glide.with(this)
        .load("http://xmamiga.com/photo/1f/1f7a.jpg")
        .crossFade()//动画默认的持续时间是 300毫秒
        .into(iv);

添加自定义动画

    Glide.with(this)
        .load("http://xmamiga.com/photo/1f/1f7a.jpg")
        .animate(R.anim.fade_in)
        .into(iv);

去除动画

    Glide.with(this)
        .load("http://xmamiga.com/photo/1f/1f7a.jpg")
        .dontAnimate()
        .into(iv);

3.8 在 ListView 和 RecyclerView 中的使用

在 ListView 或 RecyclerView 中加载图片的代码和在单独的 View 中加载完全一样。Glide 已经自动处理了 View 的复用和请求的取消:

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        String url = urls.get(position);
        Glide.with(fragment)
            .load(url)
            .into(holder.imageView);
    }

对 url 进行 null 检验并不是必须的,如果 url 为 null,Glide 会清空 View 的内容,或者显示 placeholder Drawable 或 fallback Drawable 的内容。

Glide 唯一的要求是,对于任何可复用的 View 或 Target ,如果它们在之前的位置上,用 Glide 进行过加载操作,那么在新的位置上要去执行一个新的加载操作,或调用 clear() API 停止 Glide 的工作。

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        if (isImagePosition(position)) {
            String url = urls.get(position);
            Glide.with(fragment)
                .load(url)
                .into(holder.imageView);
        } else {
            Glide.with(fragment).clear(holder.imageView);
            holder.imageView.setImageDrawable(specialDrawable);
        }
    }

对 View 调用 clear() 或 into(View),表明在此之前的加载操作会被取消,并且在方法调用完成后,Glide 不会改变 view 的内容。如果你忘记调用 clear(),而又没有开启新的加载操作,那么就会出现这种情况,你已经为一个 view 设置好了一个 Drawable,但该 view 在之前的位置上使用 Glide 进行过加载图片的操作,Glide 加载完毕后可能会将这个 view 改回成原来的内容。

这里的代码以 RecyclerView 的使用为例,但规则同样适用于 ListView。

3.9 Glide的缓存

用过手机的都知道,当划上划下一个ListView的时候,第二次都比第一次快,就是因为为GlideView对资源进行了缓存,而且封装的很好,甚至不需要自己去设定缓存大小,Glide会智能地自己给我们根据设备设置缓存大小。

缓存是为了减少或者杜绝多的网络请求。为了避免缓存,Glide用了内存缓存和‘外存缓存机制’,并且 提供了相应的方法,完全封装,不需要处理细节。Glide会自动缓存到内存,除非调用.skipMemoryCache( true )。尽管调用了这个,Glide还是会缓存到外存,还有一种情形,就是有一张图片,但是这张图变化非常快,这个时候可能并不想缓存到外存中,就使用.diskCacheStrategy( DiskCacheStrategy.NONE )。如果两种都不需要,可以两个方法组合着一起使用。

自定义外存缓存机制

Glide默认会缓存Image的很多个版本,比如原图,如果你的imageView大小的缓存。.diskCacheStrategy()有以下几种缓存策略:

  • DiskCacheStrategy.NONE 什么都不缓存
  • DiskCacheStrategy.SOURCE 只缓存最高解析图的image
  • DiskCacheStrategy.RESULT 缓存最后一次那个image,比如有可能你对image做了转化
  • DiskCacheStrategy.ALL image的所有版本都会缓存

四、总结

在众多的图片加载框架中,Glide是Google推荐的,并在自家的项目中大量使用的一个非常强大的框架,专注于平滑滚动,并且还提供Gif,本地Vedio首帧的解码和显示。Glide提供了非常便捷的链式调用接口,以及丰富的拓展和自定义功能,开发者可以非常简单地对框架进行配置和图片再加工。

回过头来,没有最好的框架,只有最适合自己的框架。

五、扩展

程序员开放的思想永存,网络上有先行车提供基于Glide封装的优秀库:

  • glide-transformations:一个基于Glide的transformation库,拥有裁剪,着色,模糊,滤镜等多种转换效果
  • GlidePalette:一个可以在Glide加载时很方便使用Palette的库

参考