2018-03-08Glide最新版V4使用指南

Glide最新版V4使用指南

使用场景

Glide是一个Android的图片加载和缓存库,它主要专注于大量图片的流畅加载,Glide几乎可以胜任任何你需要使用到图片从网络拉取,压缩,显示的场景。

1.集成

Github地址: https://github.com/bumptech/glide

  • app或lib级别的build.gradle文件添加依赖:

    repositories {
     mavenCentral()
     maven { url 'https://maven.google.com' }
       }
    
  • Android Studio3.0使用:

    dependencies {
      compile 'com.github.bumptech.glide:glide:4.3.1'
      annotationProcessor 'com.github.bumptech.glide:compiler:4.3.1'
    }
    

    使用implementation还是api需要视情况而定,implementation只能用于当前module,如果在库中以这种方式设置依赖,那么在app的module是引用不到的,但是api可以,api相当于compile。

  • 在proguard.pro/proguard.cfg中添加混淆:

    -keep public class * implements com.bumptech.glide.module.GlideModule
    -keep public class * extends com.bumptech.glide.module.AppGlideModule
    -keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** {
      **[] $VALUES;
      public *;
    }
    
    # for DexGuard only
    -keepresourcexmlelements manifest/application/meta-data@value=GlideModule
    

2.基本用法

大多数情况下加载图片只需要一行代码:

  Glide.with(fragment)
       .load(myUrl)
       .into(imageView);

3.注解(V4新特性)和自定义方法

Glide使用了annotation processor来生成API,允许应用修改RequestBuilder、RequestOptions和任意的包含在单一流式API库中的方法。这是V4的特性,运用注解后使用起来更方便:

   GlideApp.with(fragment)
           .load(myUrl)
           .placeholder(R.drawable.placeholder)
           .fitCenter()
           .into(imageView);

Glidev4中的Glide.with().load()后没有之前版本的fitCenter和placeholder这样的方法,但是GlideApp有,可以直接在builder中使用。GlideApp可以代替之前版本的Glide开头。

3.1在项目中实现AppGlideModule
  @GlideModule
  public class CustomGlideModule extends AppGlideModule {}

这个类实现必须要有@GlideModule注解,如果你添加的方法失效,那就检查下这里。
如果是library就实现LibraryGlideModule,以使用OkHttp为例:

  @GlideModule
  public final class OkHttpLibraryGlideModule extends LibraryGlideModule {
    @Override
    public void registerComponents(Context context, Registry registry) {
      registry.replace(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory());
    }
  }

OkHttpUrlLoader是Glide的OKHttp扩展库中的类,如果需要使用Glide的实现,可以在依赖中添加:

  compile 'com.github.bumptech.glide:okhttp3-integration:4.3.1'

Android Studio 3.0

  implementation 'com.github.bumptech.glide:okhttp3-integration:4.3.1'

添加完依赖不需要自己实现OkHttpLibraryGlideModule类,库中已经自带了,会自动使用OKHttp的。
然后编译工程可以发现在build中生成了四个类:

  • GlideApp
  • GlideOptions
  • GlideRequest
  • GlideRequests
3.2 GlideExtension

为了添加新的方法,修改已有的方法或者添加对其他类型格式的支持,你需要在扩展中使用加了注解的静态方法。
GlideOption用来添加自定义的方法,GlideType用来支持新的格式。

3.2.1 GlideOption

先新建一个CustomGlideExtension类:

  @GlideExtension
  public class CustomGlideExtension {
      //缩略图的最小尺寸,单位:px
      private static final int MINI_THUMB_SIZE = 100;

  /**
   * 将构造方法设为私有,作为工具类使用
   */
  private CustomGlideExtension() {
  }

  /**
   * 1.自己新增的方法的第一个参数必须是RequestOptions options
   * 2.方法必须是静态的
  * @param options
  */
  @GlideOption
  public static void miniThumb(RequestOptions options) {
      options.fitCenter()
             .override(MINI_THUMB_SIZE);
  }
}

编译工程,打开build目录中的GlideOptions,可以看见自动生成了两个方法:

   public class GlideOptions extends RequestOptions {

     /**
      * @see CustomGlideExtension#miniThumb(RequestOptions)
      */
     public GlideOptions miniThumb() {
       CustomGlideExtension.miniThumb(this);
       return this;
    }

     /**
      * @see CustomGlideExtension#miniThumb(RequestOptions)
      */
     public static GlideOptions miniThumbOf() {
       return new GlideOptions().miniThumb();
     }
   
     ...
   }

现在可以使用你自定义的方法了:

  GlideApp.with(fragment)
          .load(url)
          .miniThumb(thumbnailSize)
          .into(imageView);
3.2.2 GlideType

以添加对GIF格式的支持为例,只是举例,实际上API中已经支持了。
在刚才的CustomGlideExtension类中加上:

  @GlideExtension
  public class CustomGlideExtension {

      private static final RequestOptions DECODE_TYPE_GIF = GlideOptions.decodeTypeOf(GifDrawable.class).lock();
         
           @GlideType(GifDrawable.class)
           public static void asGIF(RequestBuilder<GifDrawable> requestBuilder) {
               requestBuilder.transition(new DrawableTransitionOptions())
                             .apply(DECODE_TYPE_GIF);
      }
  }

编译工程,打开build目录中的GlideRequests,可以看见自动生成了一个方法:

  public class GlideRequests extends RequestManager {
    /**
     * @see CustomGlideExtension#asGIF(RequestBuilder)
     */
    public GlideRequest<GifDrawable> asGIF() {
      GlideRequest<GifDrawable> requestBuilder = this.as(GifDrawable.class);
      CustomGlideExtension.asGIF(requestBuilder);
      return requestBuilder;
    }
  }

现在可以使用你添加的类型了:

  GlideApp.with(fragment)
          .asGIF()
          .load(url)
          .into(imageView);

4. 占位符

占位符就是请求的图片没加载出来时显示的默认图片。
Glide支持三种不同情况下的占位符:

  • Placeholder 请求图片加载中
  • Error 请求图片加载错误
  • Fallback 请求url/model为空
4.1 设置占位符:
  GlideApp.with(fragment)
          .load(url)
          .placeholder(R.drawable.placeholder) 
          .error(new ColorDrawable(Color.RED))
          .fallback(new ColorDrawable(Color.GREY))
          .into(view);

5. Options

5.1 RequestOptions:

Glide中的大多请求参数都可以通过RequestOptions类和apply()方法来设置。
Glide中的请求参数主要有:

  • Placeholders 占位符
  • Transformations 变换
  • Caching Strategies 缓存策略
  • 组件特定参数:编码质量,解码参数等。
    apply方法可以调用多次,但是如果两次apply存在冲突的设置,会以最后一次为准。
5.2 TransitionOptions:

TransitionOptions决定图片加载完成如何从占位符图片(或者之前的图片)过渡。

  • 淡入

  • 交叉淡入

  • 不过渡

    Glide.with(fragment)
         .load(url)
         .transition(DrawableTransitionOptions.withCrossFade())
         .into(view);
    

注意
TransitionOptions是和你要加载的资源的类型绑定的,也就是说,如果你请求一张位图(Bitmap),你就需要使用BitmapTransitionOptions,而不是DrawableTransitionOptions。因此,你请求的这张位图,你需要用简单的淡入,而不能用 交叉淡入(DrawableTransitionOptions.withCrossFade())。
如果既不是Bitmap也不是Drawable可以使用GenericTransitionOptions

5.3 RequestBuilder:

作用:

  • 1.指定加载类型。asBitmap()、asGif()、asDrawable()、asFile()。

  • 2.指定要加载url/model。

  • 3.指定要加载到那个View。

  • 4.指定要应用的RequestOption

  • 5.指定要应用的TransitionOption

  • 6.指定要加载的缩略图
    那么如何得到RequestBuilder呢?

    RequestBuilder<Drawable> requestBuilder = Glide.with(fragment);
    

默认得到一个Drawable RequestBuilder,如果要指定类型为Bitmap,可以这样写:

  RequestBuilder<Bitmap> requestBuilder = Glide.with(fragment).asBitmap();

应用RequestOptions

  RequestBuilder<Drawable> requestBuilder = Glide.with(fragment);
  requestBuilder.apply(requestOptions);
  requestBuilder.transition(transitionOptions);

RequestBuilder也可以重复使用:

 RequestBuilder<Drawable> requestBuilder =
         Glide.with(fragment)
             .asDrawable()
             .apply(requestOptions);

 for (int i = 0; i < numViews; i++) {
    ImageView view = viewGroup.getChildAt(i);
    String url = urls.get(i);
    requestBuilder.load(url).into(view);
 }

6. Transformations

Glide会自动读取ImageView的缩放类型,所以一般在layout文件指定scaleType即可。
CenterCrop, CenterInside, CircleCrop, FitCenter, RoundedCorners
Glide支持在java代码中设置这些缩放类型:

  • 1.CenterCrop 缩放宽和高都到达View的边界,有一个参数在边界上,另一个参数可能在边界上,也可能超过边界
  • 2.CenterInside 如果宽和高都在View的边界内,那就不缩放,否则缩放宽和高都进入View的边界,有一个参数在边界上,另一个参数可能在边界上,也可能在边界内
  • 3.CircleCrop 圆形且结合了CenterCrop的特性
  • 4.FitCenter 缩放宽和高都进入View的边界,有一个参数在边界上,另一个参数可能在边界上,也可能在边界内
  • 5.RoundedCorners 圆角
    有三种用法:
6.1 使用RequestOptions
 RequestOptions options = new RequestOptions();
 options.centerCrop();
 Glide.with(fragment)
     .load(url)
     .apply(options)
     .into(imageView);

2 使用RequestOptions中的transform方法

6.2 使用RequestOptions中的transform方法
 Glide.with(fragment)
     .load(url)
     .apply(RequestOptions.fitCenterTransform())
     .into(imageView);
6.3 V4特性
 GlideApp.with(fragment)
   .load(url)
   .fitCenter()
   .into(imageView);

第三种方法最简便,推荐。

多个变换
  Glide.with(fragment)
    .load(url)
    .transform(new MultiTransformation(new FitCenter(), new YourCustomTransformation())
    .into(imageView);

7. Transitions(动画)

普通动画

Glide中的过渡动画是指占位符到请求图片或缩略图到完整尺寸请求图片的动画。过渡动画只能针对单一请求,不能跨请求执行。
过渡动画执行时机:

  • 1.图片在磁盘缓存

  • 2.图片在本地

  • 3.图片在远程
    如果图片在内存缓存上是不会执行过渡动画的。如果需要在内存缓存上加载动画,可以这样:

    GlideApp.with(this).load(R.drawable.img_default).listener(new RequestListener() {
    
        @Override
        public boolean onLoadFailed(@Nullable GlideException e, Object model, Target target, boolean isFirstResource) {
            return false;
        }
    
        @Override
        public boolean onResourceReady(Object resource, Object model, Target target, DataSource dataSource, boolean isFirstResource) {
            if (dataSource == DataSource.MEMORY_CACHE) {
                //当图片位于内存缓存时,glide默认不会加载动画
                imageView.startAnimation(AnimationUtils.loadAnimation(getActivity(), R.anim.fade_in));
            }
            return false;
        }
    }).fitCenter()
      .transition(GenericTransitionOptions.with(R.anim.fade_in))
      .into(imageView);
    

通常的用法如下:

  Glide.with(fragment)
       .load(url)
       .transition(DrawableTransitionOptions.withCrossFade())
       .into(view);

TransitionOptions的介绍:TransitionOptions。有三种TransitionOptions:

  • 1.GenericTransitionOptions 通用型
  • 2.DrawableTransitionOptions
  • 3.BitmapTransitionOptions
    如果要使用自定义的动画,可以使用GenericTransitionOptions.with(int viewAnimationId)或者BitmapTransitionOptions.withCrossFade(int animationId, int duration)或者DrawableTransitionOptions.withCrossFade(int animationId, int duration)

出于性能考虑,最好不要在ListView,GridView,RecycleView中使用过渡动画,使用TransitionOptions.dontTransition()可以不加载动画,也可以使用dontAnimate不加载动画

  GlideApp.with(mContext)
          .load(imgUrl)
          .placeholder(R.drawable.img_default)
          .dontAnimate()
          .into(holder.imageview);

自定义过渡动画

  • 1.实现TransitionFactory
  • 2.重写build()
    可以控制图片在内存缓存上是否执行动画。
    具体写法参考DrawableCrossFadeFactory,然后调用TransitionOptionswith(TransitionFactory transitionFactory)加载。

加载图片成圆形(CropCircleTransformation)

  Glide.with(mContext)
   .load(imageUrl)
   .transform(new CropCircleTransformation(mContext))
   .into(holder.imageView);      

加载图片成圆角(RoundedCornersTransformation)
new RoundedCornersTransformation(this, 30, 0, RoundedCornersTransformation.CornerType.BOTTOM)
最后参数代表底部角为圆角

roundbottom.jpg

加载高斯模糊图

  Glide.with(context)
   .load(url)
   .placeholder(R.drawable.loading)
   .error(R.drawable.failed) 
   .crossFade(1000)
   .bitmapTransform(new BlurTransformation(context,23,4)) // “23”:设置模糊度(在0.0到25.0之间),默认”25";"4":图片缩放比例,默认“1”。
   .into(view);    

先加载缩略图再加载高清图片,并监听加载的进度

  private void loadImage(String image_url_thumbnail, String image_url) {
      RequestOptions requestOptions = glideImageView.requestOptions(R.color.black)
        .centerCrop()
        .skipMemoryCache(true) // 跳过内存缓存
        .diskCacheStrategy(DiskCacheStrategy.NONE); // 不缓存到SDCard中

      glideImageView.getImageLoader().setOnGlideImageViewListener(image_url, new OnGlideImageViewListener() {
      @Override
      public void onProgress(int percent, boolean isDone, GlideException exception) {
        progressView.setProgress(percent);
        progressView.setVisibility(isDone ? View.GONE : View.VISIBLE);
    }
});

glideImageView.getImageLoader().requestBuilder(image_url, requestOptions)
        .thumbnail(Glide.with(ImageActivity.this) // 加载缩略图
        .load(image_url_thumbnail)
        .apply(requestOptions))
        .transition(DrawableTransitionOptions.withCrossFade()) // 动画渐变加载
        .into(glideImageView);
 }

两种监听加载图片进度的Listener

  public interface OnGlideImageViewListener {
      void onProgress(int percent, boolean isDone, GlideException exception);
  }

  public interface OnProgressListener {
      void onProgress(String imageUrl, long bytesRead, long totalBytes, boolean isDone, GlideException exception);
  }
gif4.gif

方形:CropSquareTransformation
颜色覆盖:ColorFilterTransformation
置灰:GrayscaleTransformation

8. 基本配置

8.1 配置内存缓存

Glide会自动合理分配内存缓存,但是也可以自己手动分配。
方法一
通过MemorySizeCalculator设置

  @GlideModule
  public class CustomGlideModule extends AppGlideModule {
      @Override
      public void applyOptions(Context context, GlideBuilder builder) {
          MemorySizeCalculator calculator = new MemorySizeCalculator.Builder(context)
                  .setMemoryCacheScreens(2)
                  .build();
          builder.setMemoryCache(new LruResourceCache(calculator.getMemoryCacheSize()));
      }
  }

setMemoryCacheScreens设置MemoryCache应该能够容纳的像素值的设备屏幕数,说白了就是缓存多少屏图片,默认值是2。
方法二

  @GlideModule
  public class CustomGlideModule extends AppGlideModule {
      @Override
      public void applyOptions(Context context, GlideBuilder builder) {
          int memoryCacheSizeBytes = 1024 * 1024 * 20; // 20mb
          builder.setMemoryCache(new LruResourceCache(memoryCacheSizeBytes));
      }
  }

方法三

  @GlideModule
  public class YourAppGlideModule extends AppGlideModule {
    @Override
    public void applyOptions(Context context, GlideBuilder builder) {
      builder.setMemoryCache(new CustomGlideMemoryCache());
    }
  }

自己实现MemoryCache接口。
清楚内存缓存,在主线程调用:

  GlideApp.get(context).clearMemory();

在使用的时候,可以跳过内存缓存:

  GlideApp.with(getActivity())
          .load(url)
          .skipMemoryCache(true)
          .dontAnimate()
          .centerCrop()
          .into(imageView);
8.2 磁盘缓存

Glide使用DiskLruCacheWrapper作为默认的磁盘缓存,默认大小是250M,缓存文件放在APP的缓存文件夹下。

  @GlideModule
  public class CustomGlideModule extends AppGlideModule {
      @Override
      public void applyOptions(Context context, GlideBuilder builder) {
          int diskCacheSizeBytes = 1024 * 1024 * 100; // 100 MB
          builder.setDiskCache(new InternalCacheDiskCacheFactory(context, diskCacheSizeBytes));
  //        builder.setDiskCache(new InternalCacheDiskCacheFactory(context, "cacheFolderName", diskCacheSizeBytes));
  //        builder.setDiskCache(new ExternalCacheDiskCacheFactory(context));
      }
  }

用法如上,可以指定缓存在内部存储或外部存储,也可以指定缓存大小和文件夹。
自定义磁盘缓存

  @GlideModule
  public class CustomGlideModule extends AppGlideModule {
    @Override
    public void applyOptions(Context context, GlideBuilder builder) {
      builder.setDiskCache(new DiskCache.Factory() {
          @Override
          public DiskCache build() {
            return new YourAppCustomDiskCache();
          }
      });
    }
  }

自己实现DiskCache接口。
清楚磁盘缓存,在主线程调用:

  GlideApp.get(context).clearDiskCache();

加载图片时设置磁盘缓存策略:

  GlideApp.with(getActivity())
          .load(url)
          .diskCacheStrategy(DiskCacheStrategy.ALL)
          .dontAnimate()
          .centerCrop()
          .into(imageView);

默认的策略是DiskCacheStrategy.AUTOMATIC
DiskCacheStrategy有五个常量:

  • DiskCacheStrategy.ALL 使用DATA和RESOURCE缓存远程数据,仅使用RESOURCE来缓存本地数据。
  • DiskCacheStrategy.NONE 不使用磁盘缓存
  • DiskCacheStrategy.DATA 在资源解码前就将原始数据写入磁盘缓存
  • DiskCacheStrategy.RESOURCE 在资源解码后将数据写入磁盘缓存,即经过缩放等转换后的图片资源。
  • DiskCacheStrategy.AUTOMATIC 根据原始图片数据和资源编码策略来自动选择磁盘缓存策略。
8.3 禁止解析Manifest文件

主要针对V3升级到v4的用户,可以提升初始化速度,避免一些潜在错误。

  @GlideModule
  public class CustomGlideModule extends AppGlideModule {
    @Override
    public boolean isManifestParsingEnabled() {
      return false;
    }
  }
8.4 View尺寸

Glide对ImageView的width和height属性是这样解析的:

  • 如果width和height都大于0,则使用layout中的尺寸。
  • 如果width和height都是WRAP_CONTENT,则使用屏幕尺寸。
  • 如果width和height中至少有一个值<=0并且不是WRAP_CONTENT,那么就会在布局的时候添加一个OnPreDrawListener监听ImageView的尺寸

Glide对WRAP_CONTENT的支持并不好,所以尽量不要用。
那么如何在运行修改ImageView尺寸呢?
方法一 继承ImageViewTarget
我这里指定的View的类型是ImageView,资源类型是Bitmap,可根据需要修改,onResourceReady(Bitmap bitmap, Transition<? super Bitmap> transition)方法中可以通过bitmap获取图片的尺寸。

  public class CustomImageViewTarget extends ImageViewTarget<Bitmap> {

    private int width, height;

    public CustomImageViewTarget(ImageView view) {
        super(view);
    }

    public CustomImageViewTarget(ImageView view, int width, int height) {
        super(view);
        this.width = width;
        this.height = height;
    }

    @Override
    public void onResourceReady(Bitmap bitmap, Transition<? super Bitmap> transition) {
        super.onResourceReady(bitmap,transition);
    }

    @Override
    protected void setResource(@Nullable Bitmap resource) {
        view.setImageBitmap(resource);
    }

    @Override
    public void getSize(SizeReadyCallback cb) {
        if (width > 0 && height > 0) {
            cb.onSizeReady(width, height);
            return;
        }
        super.getSize(cb);
    }
}

使用:

  GlideApp.with(context)
          .asBitmap()
          .load(url)
          .dontAnimate()
          .placeholder(R.drawable.img_default)
          .into(new CustomImageViewTarget(imageview, 300, 300));

方法二 使用override()

  GlideApp.with(mContext)
          .load(url)
          .override(width,height)
          .into(view);
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,646评论 4 366
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,979评论 1 301
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 110,391评论 0 250
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,356评论 0 215
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,740评论 3 293
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,836评论 1 224
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,022评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,764评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,487评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,728评论 2 252
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,200评论 1 263
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,548评论 3 260
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,217评论 3 241
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,134评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,921评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,919评论 2 283
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,766评论 2 274

推荐阅读更多精彩内容