Glide的高级用法

这个礼拜学习了郭霖大神的Glide最全解析专题文章,特此在此做个总结。记录一些Glide的高级用法。

自定义GlideUrl加载图片

如果图片出于安全的考虑需要在图片地址后面加上token,这样导致同一张图片导致不同的地址,列如下面的地址:

http://url.image.com/test.jpg?token=adfnjkews8832734

这里的token可能随时都会变,这样就导致图片地址一直在变,这样就导致缓存失效,一发生改变就去网络加载图片。我们可以自己定义自己的GlideUrl来解决这个问题。

  public class MyGlideUrl extends GlideUrl {

        private String mUrl;

        public MyGlideUrl(String url) {
            super(url);
            mUrl = url;
        }


        @Override
        public String getCacheKey() {
            if(!TextUtils.isEmpty(mUrl)) {
                return mUrl.replace(findTokenParam(), "");
            }
            return "";
        }

        private String findTokenParam() {
            String tokenParam = "";
            int tokenKeyIndex = mUrl.contains("?token=") ? mUrl.indexOf("?token=") : mUrl.indexOf("&token=");
            if (tokenKeyIndex != -1) {
                int nextAndIndex = mUrl.indexOf("&", tokenKeyIndex + 1);
                if (nextAndIndex != -1) {
                    tokenParam = mUrl.substring(tokenKeyIndex + 1, nextAndIndex + 1);
                } else {
                    tokenParam = mUrl.substring(tokenKeyIndex);
                }
            }
            return tokenParam;
        }

    }

接下来就可以用下面代码来使用了

Glide.with(this)
     .load(new MyGlideUrl(url))
     .into(imageView);

使用Target来加载显示图片

来看下Target的继承关系:
说明
通常只需要在SimpleTarget和ViewTarget上自定义就可以。如下面用SimpleTarget来加载图片:

 SimpleTarget<GlideDrawable> simpleTarget=new SimpleTarget<GlideDrawable>() {
            @Override
            public void onResourceReady(GlideDrawable resource, GlideAnimation<? super GlideDrawable> glideAnimation) {
                avatar.setImageDrawable(resource);
            }
        };
        String url="https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=2334584121,1324775889&fm=27&gp=0.jpg";
        Glide.with(getActivity()).load(url)
                .into(simpleTarget);

这个效果和into(ImageView imageview)没什么区别,也可以用ViewTarget来实现给任意View加载图片。
先自定义一个View,代码如下:

public class MyView extends FrameLayout {
   private ViewTarget<MyView,GlideDrawable> viewTarget;

    public MyView(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        viewTarget=new ViewTarget<MyView, GlideDrawable>(this) {
            @Override
            public void onResourceReady(GlideDrawable resource, GlideAnimation<? super GlideDrawable> glideAnimation) {
                MyView myView=getView();
                myView.setDrawable(resource);
            }
        };

    }

    public ViewTarget<MyView,GlideDrawable> getViewTarget(){
        return viewTarget;
    }

    public void setDrawable(Drawable drawable){
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
            setBackground(drawable);
        }else {
            setBackgroundDrawable(drawable);
        }
    }
}

在进行如下代码应用:

  Glide.with(getActivity()).load(url)
                .into(myView.getViewTarget());

使用preload方法预加载图片

 Glide.with(this)
      .load(url)
      .diskCacheStrategy(DiskCacheStrategy.SOURCE)
      .preload();

注意的是我们使用了preload()方法,最好将缓存策略设置为DiskCacheStrategy.SOURCE。因为preload()默认预加载原图尺寸,而into()方法会根据控件大小去加载图片,如果不设置DiskCacheStrategy.SOURCE,很容易造成使用了preload()方法预加载图片,再用into()又去网络加载图片。

DiskCacheStrategy.SOURCE 只缓存原始图片 //预加载或者 仅仅下载图片使用
DiskCacheStrategy.RESULT 只缓存转换过得图片 //一般使用这个
DiskCacheStrategy.ALL 既缓存原始图片,也缓存转换过后的图片
DiskCacheStrategy.NONE 不缓存任何内容

调用了预加载后再去加载图片就不会消耗流量加载的很快了。

Glide.with(this)
     .load(url)
     .diskCacheStrategy(DiskCacheStrategy.SOURCE)
     .into(imageView);

获取缓存图片文件地址

有时候我们需要获取缓存图片的地址,这时候就需要用到downloadOnly()方法了。downloadOnly()方法表示只会下载图片,而不会对图片进行加载。当图片下载完成之后,我们可以得到图片的存储路径。它有两个重载方法如下:

  • downloadOnly(int width, int height)
  • downloadOnly(Y target)

其中downloadOnly(int width, int height)是用于在子线程中下载图片的,而downloadOnly(Y target)是用于在主线程中下载图片的。
先来看一下downloadOnly(int width,int height)如何使用:

 final ExecutorService executorService = Executors.newSingleThreadExecutor();
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    String url = "https://cdn.duitang.com/uploads/item/201204/09/20120409130851_Emr2W.jpeg";
                    final Context context = getApplicationContext();
                    FutureTarget<File> target = Glide.with(context)
                            .load(url)
                            .downloadOnly(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL);
                    final File imageFile = target.get(); //这里会一直阻塞等待结果返回
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            Toast.makeText(context, imageFile.getPath(), Toast.LENGTH_LONG).show();
                        }
                    });
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                   executorService.shutdown();
                }
            }
        });

下面看一下 downloadOnly(Y target)的实现,其实 downloadOnly(int width, int height) 内部也是依靠Targent来实现的,所以这里我们自己来创建一个Target

public class ImageTarget implements Target<File> {

        @Override
        public void onStart() {
        }

        @Override
        public void onStop() {
        }

        @Override
        public void onDestroy() {
        }

        @Override
        public void onLoadStarted(Drawable placeholder) {
        }

        @Override
        public void onLoadFailed(Exception e, Drawable errorDrawable) {
        }

        @Override
        public void onResourceReady(File resource, GlideAnimation<? super File> glideAnimation) {
            Log.d("lx", resource.getPath());
        }

        @Override
        public void onLoadCleared(Drawable placeholder) {
        }

        @Override
        public void getSize(SizeReadyCallback cb) {
            cb.onSizeReady(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL);
        }

        @Override
        public void setRequest(Request request) {
        }

        @Override
        public Request getRequest() {
            return null;
        }
    }

实现Target接口需要重写很多方法,这些方法大多是数Glide加载图片生命周期的一些回调,我们可以不用管它们,其中只有两个方法是必须实现的,一个是getSize()方法,一个是onResourceReady()方法。Glide在开始加载图片之前会先计算图片的大小,然后回调到onSizeReady()方法当中,之后才会开始执行图片加载。而这里,计算图片大小的任务就交给我们了。只不过这是一个最简单的Target实现,我在getSize()方法中就直接回调了Target.SIZE_ORIGINAL,表示图片的原始尺寸。图片下载完成之后就会回调onResourceReady()方法。

  String url = "https://cdn.duitang.com/uploads/item/201204/09/20120409130851_Emr2W.jpeg";
    Glide.with(this)
            .load(url)
            .downloadOnly(new ImageTarget());

Glide图片监听

Glide图片监听可以使用listener()方法,基本用法如下:

   String url = "https://cdn.duitang.com/uploads/item/201204/09/20120409130851_Emr2W.jpeg";
    Glide.with(this)
            .load(url)
            .listener(new RequestListener<String, GlideDrawable>() {
                @Override
                public boolean onException(Exception e, String model, Target<GlideDrawable> target,
                    boolean isFirstResource) {
                    //这里返回true表示事件已经消化了,不会往下传递,返回false表示没有消耗
                    //如果设置为true error(int resid)设置异常占位图将会失效
                    return false;
                }

                @Override
                public boolean onResourceReady(GlideDrawable resource, String model,
                    Target<GlideDrawable> target, boolean isFromMemoryCache, boolean isFirstResource) {
                    //这里返回true表示事件已经消化了,不会往下传递,返回false表示没有消耗
                    //设置为ture了,就不会调用Target的onResourceReady()方法了
                    return false;
                }
            })
            .into(imageView);

Glide图片变换功能

Glide内置有两种图片变换功能,一个是个是CenterCrop,一个是FitCenter。
如下

Glide.with(this)
     .load(url)
     .centerCrop()
     .into(imageView);

Glide.with(this)
     .load(url)
     .fitCenter()
     .into(imageView);

centerCrop裁剪图片中心区域充满控件显示,fitCenter会按图片最短边缩放图片。
我们也可以自己定义自己需要的图片变换,如圆形、黑白、高斯模糊。需要继承 BitmapTransformation并重写transform()方法。并实现自己的逻辑。如下实现一个圆形变换。

public class CircleBitmapTransformation extends BitmapTransformation {

    public CircleBitmapTransformation(Context context) {
        super(context);
    }

    public CircleBitmapTransformation(BitmapPool bitmapPool) {
        super(bitmapPool);
    }

    @Override
    public String getId() {
    //图形变换唯一id,于其他图片变换做区分,这里返回完整类名就好
        return "com.liuxin.gliddemo.CircleCrop";
    }

  /*
   *图片变换逻辑处理
   *@params pool Glide中的一个Bitmap缓存池,用于对Bitmap对象进行重用
   *@params toTransform  原始图片Bitmap对象
   *@params outWidth 变换后的图片宽度
   *@params outHeight 变换的图片高度
   */
    @Override
    protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
        //取原始图片的宽高最小值为圆的直径
        int diameter = Math.min(toTransform.getWidth(), toTransform.getHeight());
        //从Bitmap缓存池中获取Bitmap
        final Bitmap bitmap = pool.get(outWidth, outHeight, Bitmap.Config.ARGB_8888);
        final Bitmap result;

        if (bitmap != null) {
            result = bitmap;
        } else {
           //如果为空就创建一个
            result = Bitmap.createBitmap(diameter, diameter, Bitmap.Config.ARGB_8888);
        }
        //要把一个长方形的图片截取成一个圆形图片,我们从中间画圆截取
        //需要位移的x
        int dx = (toTransform.getWidth() - diameter) / 2;
        //需要位移的y
        int dy = (toTransform.getHeight() - diameter) / 2;
        //以Bitmap 对象创建一个画布
        Canvas canvas = new Canvas(result);
        Paint paint = new Paint();
        //创建图片纹理
        BitmapShader shader = new BitmapShader(toTransform, BitmapShader.TileMode.CLAMP, 
                                            BitmapShader.TileMode.CLAMP);
        //不为0移动画布,准备纹理
        if (dx != 0 || dy != 0) {
            Matrix matrix = new Matrix();
            matrix.setTranslate(-dx, -dy);
            shader.setLocalMatrix(matrix);
        }
        paint.setShader(shader);
        paint.setAntiAlias(true);
        float radius = diameter / 2f;
        //画一个圆形纹理
        canvas.drawCircle(radius, radius, radius, paint);
        //把bitmap对象放入到Bitmap缓存池中  
        if (bitmap != null && !pool.put(bitmap)) {
            bitmap.recycle();
        }
        //返回结果
        return result;
    }
}

用以下代码进行图片变换

Glide.with(this)
     .load(url)
     .transform(new CircleBitmapTransformation(this))
     .into(imageView);

更多图片变换 https://github.com/wasabeef/glide-transformations。这个开源库实现了很多图片变换功能。

自定义图片缓存路径

默认情况下Glide图片缓存在当前应用的私有目录下,我们可以通过如下配置自定义图片的缓存路径

public class GlideCache implements GlideModule {
    @Override
    public void applyOptions(Context context, GlideBuilder builder) {
       //配置自己的参数
        Logger.e("GlideCache=========applyOptions");

        //设置图片的显示格式ARGB_8888(指图片大小为32bit)
        builder.setDecodeFormat(DecodeFormat.PREFER_ARGB_8888);
        //设置磁盘缓存目录(和创建的缓存目录相同)
        String downloadDirectoryPath = FileCacheUtil.getInstance().getPicCacheDir();
        //设置缓存的大小为100M
        int cacheSize = 200 * 1000 * 1000;
        builder.setDiskCache(new DiskLruCacheFactory(downloadDirectoryPath, cacheSize));
    }

    @Override
    public void registerComponents(Context context, Glide glide) {
       //配置注册自己的组件
        Logger.e("GlideCache=========registerComponents");
    }
}

除了上面的配置还需要在配置清单中application节点内进行配置:

 <meta-data android:name="com.liuxin.glidedemo.utils.GlideCache"
            android:value="GlideModule"/>

替换网络加载模块实现带进度条加载图片

默认情况Glide使用的是原始的HttpURLConnection进行网络请求,我们怎么替换成OkHttp进行网络请求呢
可以通过自定义组件来实现。
通过如下步骤实现:
第一步实现DataFetcher

public class OkHttpFetcher implements DataFetcher<InputStream> {

    private final OkHttpClient client;
    private final GlideUrl url;
    private InputStream stream;
    private ResponseBody responseBody;
    private volatile boolean isCancelled;

    public OkHttpFetcher(OkHttpClient client, GlideUrl url) {
        this.client = client;
        this.url = url;
    }

    @Override
    public InputStream loadData(Priority priority) throws Exception {
        Request.Builder requestBuilder = new Request.Builder()
                .url(url.toStringUrl());
        for (Map.Entry<String, String> headerEntry : url.getHeaders().entrySet()) {
            String key = headerEntry.getKey();
            requestBuilder.addHeader(key, headerEntry.getValue());
        }
        Request request = requestBuilder.build();
        if (isCancelled) {
            return null;
        }
        Response response = client.newCall(request).execute();
        responseBody = response.body();
        if (!response.isSuccessful() || responseBody == null) {
            throw new IOException("Request failed with code: " + response.code());
        }
        stream = ContentLengthInputStream.obtain(responseBody.byteStream(),
                responseBody.contentLength());
        return stream;
    }

    @Override
    public void cleanup() {
        try {
            if (stream != null) {
                stream.close();
            }
            if (responseBody != null) {
                responseBody.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public String getId() {
        return url.getCacheKey();
    }

    @Override
    public void cancel() {
        isCancelled = true;
    }
}

第二步实现ModelLoader

public class OkHttpGlideUrlLoader implements ModelLoader<GlideUrl, InputStream> {

    private OkHttpClient okHttpClient;

    public static class Factory implements ModelLoaderFactory<GlideUrl, InputStream> {

        private OkHttpClient client;

        public Factory() {
        }

        public Factory(OkHttpClient client) {
            this.client = client;
        }

        private synchronized OkHttpClient getOkHttpClient() {
            if (client == null) {
                client = new OkHttpClient();
            }
            return client;
        }

        @Override
        public ModelLoader<GlideUrl, InputStream> build(Context context, GenericLoaderFactory factories) {
            return new OkHttpGlideUrlLoader(getOkHttpClient());
        }

        @Override
        public void teardown() {
        }
    }

    public OkHttpGlideUrlLoader(OkHttpClient client) {
        this.okHttpClient = client;
    }

    @Override
    public DataFetcher<InputStream> getResourceFetcher(GlideUrl model, int width, int height) {
        return new OkHttpFetcher(okHttpClient, model);
    }
}

第三步注册到glide中去

public class GlideCache implements GlideModule {
    @Override
    public void applyOptions(Context context, GlideBuilder builder) {
       //配置自己的参数
        Logger.e("GlideCache=========applyOptions");

        //设置图片的显示格式ARGB_8888(指图片大小为32bit)
        builder.setDecodeFormat(DecodeFormat.PREFER_ARGB_8888);
        //设置磁盘缓存目录(和创建的缓存目录相同)
        String downloadDirectoryPath = FileCacheUtil.getInstance().getPicCacheDir();
        //设置缓存的大小为100M
        int cacheSize = 200 * 1000 * 1000;
        builder.setDiskCache(new DiskLruCacheFactory(downloadDirectoryPath, cacheSize));
    }

    @Override
    public void registerComponents(Context context, Glide glide) {
       //配置注册自己的组件
        Logger.e("GlideCache=========registerComponents");
         glide.register(GlideUrl.class, InputStream.class, new OkHttpGlideUrlLoader.Factory());
    }
}

接下来实现带进度条下载图片,可以通过OkHttp拦截器来实现,实现如下:
第一步先写一个进度条接口

public interface ProgressListener {

    void onProgress(int progress);

}

第二步实现进度逻辑处理

public class ProgressResponseBody extends ResponseBody {

    private static final String TAG = "ProgressResponseBody";

    private BufferedSource bufferedSource;

    private ResponseBody responseBody;

    private ProgressListener listener;

    public ProgressResponseBody(String url, ResponseBody responseBody) {
        this.responseBody = responseBody;
        listener = ProgressInterceptor.LISTENER_MAP.get(url);
    }

    @Override
    public MediaType contentType() {
        return responseBody.contentType();
    }

    @Override
    public long contentLength() {
        return responseBody.contentLength();
    }

    @Override 
    public BufferedSource source() {
        if (bufferedSource == null) {
            bufferedSource = Okio.buffer(new ProgressSource(responseBody.source()));
        }
        return bufferedSource;
    }

    private class ProgressSource extends ForwardingSource {

        long totalBytesRead = 0;

        int currentProgress;

        ProgressSource(Source source) {
            super(source);
        }

        @Override 
        public long read(Buffer sink, long byteCount) throws IOException {
            long bytesRead = super.read(sink, byteCount);
            long fullLength = responseBody.contentLength();
            if (bytesRead == -1) {
                totalBytesRead = fullLength;
            } else {
                totalBytesRead += bytesRead;
            }
            int progress = (int) (100f * totalBytesRead / fullLength);
            Log.d(TAG, "download progress is " + progress);
            if (listener != null && progress != currentProgress) {
                listener.onProgress(progress);
            }
            if (listener != null && totalBytesRead == fullLength) {
                listener = null;
            }
            currentProgress = progress;
            return bytesRead;
        }
    }

}

第三部实现进度条拦截器

public class ProgressInterceptor implements Interceptor { 

    static final Map<String, ProgressListener> LISTENER_MAP = new HashMap<>();

    public static void addListener(String url, ProgressListener listener) {
        LISTENER_MAP.put(url, listener); 
    } 

    public static void removeListener(String url) { 
        LISTENER_MAP.remove(url); 
    } 

    @Override 
    public Response intercept(Chain chain) throws IOException { 
        Request request = chain.request(); 
        Response response = chain.proceed(request); 
        String url = request.url().toString(); 
        ResponseBody body = response.body(); 
        Response newResponse = response.newBuilder().body(new ProgressResponseBody(url, body)).build();
        return newResponse;
    } 

}

最后在配置中添加

    @Override 
    public void registerComponents(Context context, Glide glide) { 
        OkHttpClient.Builder builder = new OkHttpClient.Builder(); 
        builder.addInterceptor(new ProgressInterceptor()); 
        OkHttpClient okHttpClient = builder.build(); 
        glide.register(GlideUrl.class, InputStream.class, new OkHttpGlideUrlLoader.Factory(okHttpClient));
    } 

到这一步就可以,使用方法:

   ProgressInterceptor.addListener(url, new ProgressListener() {
            @Override
            public void onProgress(int progress) {
                Logger.i(TAG,"progress"+progress);
            }
        });
         Glide.with(this)
             .load(url)
             .listener(new RequestListener<String, GlideDrawable>() {
                @Override
                public boolean onException(Exception e, String model, Target<GlideDrawable> target,
                    boolean isFirstResource) {
                    //这里返回true表示事件已经消化了,不会往下传递,返回false表示没有消耗
                    //如果设置为true error(int resid)设置异常占位图将会失效
                    return false;
                }

                @Override
                public boolean onResourceReady(GlideDrawable resource, String model,
                    Target<GlideDrawable> target, boolean isFromMemoryCache, boolean isFirstResource) {
                    //这里返回true表示事件已经消化了,不会往下传递,返回false表示没有消耗
                    //设置为ture了,就不会调用Target的onResourceReady()方法了
                    ProgressInterceptor.removeListener(url);
                    return false;
                }
            })
             .into(imageview);
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值