Glide 图片加载框架(Android)

常用图片加载框架:

Android上的图片加载框架非常成熟,常用图片加载框架UniversalImageLoader,Google推出的Volley,后来google所推荐的图片加载库Glide,Facebook推出的Fresco。易用性方面,Glide和Picasso应该都是完胜其他框架的,这两个框架大多数情况下加载图片都是一行代码就能解决的。

Glide用法

一、 Glide集成

添加依赖

//1、android studio 直接依赖
dependencies {
    compile 'com.github.bumptech.glide:glide:3.7.0'
}

//2、Mavern项目形式
<dependency>
  <groupId>com.github.bumptech.glide</groupId>
  <artifactId>glide</artifactId>
  <version>3.7.0</version>
</dependency>
<dependency>
  <groupId>com.google.android</groupId>
  <artifactId>support-v4</artifactId>
  <version>r7</version>
</dependency>

添加权限

<uses-permission android:name="android.permission.INTERNET" />

二、 Glide常用方法

1、加载图片到imageView

// 加载本地图片
File file = new File(getExternalCacheDir() + "/image.jpg");
Glide.with(this).load(file).into(imageView);

// 加载应用资源
int resource = R.drawable.image;
Glide.with(this).load(resource).into(imageView);

// 加载二进制流
byte[] image = getImageBytes();
Glide.with(this).load(image).into(imageView);

// 加载Uri对象
Uri imageUri = getImageUri();
Glide.with(this).load(imageUri).into(imageView);
 //加载 GIF图片  Glide内部会自动判断图片格式 图片地址:http://p1.pstatp.com/large/166200019850062839d3 
 //若现有图片为非gif图片,则直接加载错误占位图。
 Glide.with(this).load(url).asGif().diskCacheStrategy(DiskCacheStrategy.NONE).into(imageView);
Glide.with(this)
     .load(url)
     .asBitmap()//只加载静态图片,如果是git图片则只加载第一帧。
     .placeholder(R.drawable.loading)
     .error(R.drawable.error)
     .diskCacheStrategy(DiskCacheStrategy.NONE)
     .into(imageView);

2、加载占位图、加载失败的展位图、略缩图加载
Glide 的缩略图功能和占位图略有不同,占位图必须使用资源文件才行,而缩略图是动态的占位图可以从网络中加载。缩略图会在实际请求加载完成或者处理完之后才显示。在原始图片到达之后,缩略图不会取代原始图片,只会被抹除。

Glide 为缩略图提供了2种不同的加载方式,比较简单的方式是调用 thumbnail() 方法,参数是 float 类型,作为其倍数大小。例如,你传入 0.2f 作为参数,Glide 将会显示原始图片的20%的大小,如果原图是 1000x1000 的尺寸,那么缩略图将会是 200x200 的尺寸。为缩略图明显比原图小得多,所以我们需要确保 ImageView 的 ScaleType 设置的正确。

Glide.with(this)
.load(url).
placeholder(R.drawable.loading)// 加载展位图
.error(R.drawable.error)// 加载失败占位图
.diskCacheStrategy(DiskCacheStrategy.NONE)//关闭Glide的硬盘缓存机制
.into(imageView);

//略缩图加载
Glide.with( context )
    .load( url )
    .thumbnail( 0.2f )
    .into( imageView );
//使用 thumbnail() 方法来设置是简单粗暴的,但是如果缩略图需要通过网络加载相同的全尺寸图片,就不会很快的显示了。所以 Glide 提供了另一种防止去加载缩略图.
//与第一种方式不同的是,这里的第一个缩略图请求是完全独立于第二个原始请求的。该缩略图可以是不同的资源图片,同时也可以对缩略图做不同的转换,等等...
private void loadImageThumbnailRequest(){
    // setup Glide request without the into() method
    DrawableRequestBuilder<String> thumbnailRequest = Glide.with( context ).load( url );
    // pass the request as a a parameter to the thumbnail request
    Glide.with( context )
        .load( url )
        .thumbnail( thumbnailRequest )
        .into( imageView );
}

3、加载动画开关、
动画效果可以让图片加载变得更加的平滑,crossFade() 方法强制开启 Glide 默认的图片淡出淡入动画,当前版本3.7.0是默认开启的。crossFade() 还有一个重载方法 crossFade(int duration)。可以控制动画的持续时间,单位ms。动画默认的持续时间是300ms。既然可以添加动画,那肯定就可以设置没有任何淡出淡入效果,调用 dontAnimate(),Glide 可以自定义动画效果

Glide.with(context)
    .load(url)
    .crossFade()//或者使用 dontAnimate() 关闭动画
    .placeholder(R.drawable.place_image)
    .error(R.drawable.error_image)
    .into(imageView);

自定义动画:

<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:fillAfter="true">

    <scale
        android:duration="@android:integer/config_longAnimTime"
        android:fromXScale="0.1"
        android:fromYScale="0.1"
        android:pivotX="50%"
        android:pivotY="50%"
        android:toXScale="1"
        android:toYScale="1"/>
</set>

这是个 XML 动画缩放动画,图片刚开始小的,然后逐渐增大到原尺寸。我们现在要应用到 Glide 加载图片中去,调用 .animate() 方法传入 XML 动画的 id 即可。

Glide.with(context)
    .load(mUrl)
    .transform(new RoundTransformation(this , 20))
    .animate( R.anim.zoom_in )
    .into(mImageView);

这种加载方式用在常规的 ImageView 上是没有问题的,但如果使用的 Target 是一些自定义的时候就没法好好的实现了。这时候我们就可以通过传入实现了 ViewPropertyAnimation.Animator 接口的类对象来实现。

ViewPropertyAnimation.Animator animator = new ViewPropertyAnimation.Animator() {
    @Override
    public void animate(View view) {
        view.setAlpha( 0f );

        ObjectAnimator fadeAnim = ObjectAnimator.ofFloat( view, "alpha", 0f, 1f );
        fadeAnim.setDuration( 2500 );
        fadeAnim.start();
    }
};

然后,我们只需要在 Glide 请求中设置这个动画对象就ok

Glide.with(context)
    .load(mUrl)
    .animate( animator )
    .into(viewTarget);

animate(View view) 中你的动画对象方法中, 你可以做任何你想要对视图做的事情。

4、加载指定大小图片

Glide.with(this)
     .load(url)
     .placeholder(R.drawable.loading)
     .error(R.drawable.error)
     .diskCacheStrategy(DiskCacheStrategy.NONE)
     .override(100, 100)//指定图片大小
     .into(imageView);

5、缓存机制

//关闭框架的内存缓存机制
Glide.with(this)
     .load(url)
     .placeholder(R.drawable.loading)
     .error(R.drawable.error)
    .diskCacheStrategy(DiskCacheStrategy.NONE)
     .override(100, 100)//指定图片大小
     .into(imageView);

//关闭框架的硬盘缓存机制
Glide.with(this)
     .load(url)
     .diskCacheStrategy(DiskCacheStrategy.NONE)     //关闭硬盘缓存操作
     .into(imageView);

//其他参数表示:
//DiskCacheStrategy.NONE: 表示不缓存任何内容。
//DiskCacheStrategy.SOURCE: 表示只缓存原始图片。
//DiskCacheStrategy.RESULT: 表示只缓存转换过后的图片(默认选项)。
//DiskCacheStrategy.ALL : 表示既缓存原始图片,也缓存转换过后的图片。

6、当引用的 url 存在 token 时解决方法–>重写 Glide 的 GlideUrl 方法

public class MyGlideUrl extends GlideUrl {

    private String mUrl;

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

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

    private String findTokenParam() {
        String tokenParam = "";
        int tokenKeyIndex = mUrl.indexOf("?token=") >= 0 ? 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);

7、Glide预加载

//a、预加载代码
Glide.with(this)
     .load(url)
     .diskCacheStrategy(DiskCacheStrategy.SOURCE)
     .preload();

//preload() 有两种重载
 // 1、带有参数的重载,参数作用是设置预加载的图片大小;
//2、不带参数的表示加载的图片为原始尺寸;

//b、使用预加载的图片
Glide.with(this)
     .load(url)
     .diskCacheStrategy(DiskCacheStrategy.SOURCE)
     .into(imageView);

8、图片请求的优先级
同一时间加载多个图片,App 将难以避免这种情况。如果这个时候我们希望用户的体验更好,往往会选择先加载对于用户更加重要的图片。Glide 可以调用 .priority() 方法配合 Priority 枚举来设置图片加载的优先级。
等级划分:优先级并不是完全严格遵守的。Glide 将会用他们作为一个准则,尽可能的处理这些请求,但是不能保证所有的图片都会按照所有要求的顺序加载。
Priority.LOW
Priority.NORMAL
Priority.HIGH
Priority.IMMEDIAT

//设置 HIGH 优先级
Glide.with( context )
    .load( highPriorityImageUrl )
    .priority (Priority.HIGH )
    .into( imageView );
//设置 LOW 优先级
Glide.with( context )
    .load( lowPriorityImageUrl )
    .priority( Priority.LOW )
    .into( imageView );

9、Glide 图片下载、使用
使用 downloadOnly(int width, int height) 或 downloadOnly(Y target) 方法替代 into(view) 方法。

public void downloadImage(View view) {
    new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                String url = "http://cn.bing.com/az/hprichbg/rb/TOAD_ZH-CN7336795473_1920x1080.jpg";
                final Context context = getApplicationContext();
                FutureTarget<File> target = Glide.with(context)
                                                 .load(url)
                                                 .downloadOnly(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL);//指定下载尺寸,在子线程下载,一个参数的 downloadOnly(Y target) 方法 在主线程内进行下载
                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();
            }
        }
    }).start();
}
--------------------------------------------------
//使用下载完的图片
public void loadImage(View view) {
    String url = "http://cn.bing.com/az/hprichbg/rb/TOAD_ZH-CN7336795473_1920x1080.jpg";
    Glide.with(this)
            .load(url)
            .diskCacheStrategy(DiskCacheStrategy.SOURCE)
            .into(imageView);
}

9、监听Glide 加载的状态

public void loadImage(View view) {
    String url = "http://cn.bing.com/az/hprichbg/rb/TOAD_ZH-CN7336795473_1920x1080.jpg";
    Glide.with(this)
            .load(url)
            .listener(new RequestListener<String, GlideDrawable>() {
                @Override//加载失败
                public boolean onException(Exception e, String model, Target<GlideDrawable> target,
                    boolean isFirstResource) {
                    return false;
                }

                @Override//加载成功
                public boolean onResourceReady(GlideDrawable resource, String model,
                    Target<GlideDrawable> target, boolean isFromMemoryCache, boolean isFirstResource) {
                    return false;
                }
            })
            .into(imageView);
}

10、Glide 图形变换功能

//a、禁用图形变换功能
Glide.with(this)
     .load(url)
     .dontTransform()
     .into(imageView);
//b、修改方法设置图形变换大小
Glide.with(this)
     .load(url)
     .override(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL)
     .into(imageView);

// 按照原始的长宽比充满全屏
Glide.with(this)
     .load(url)
     .centerCrop()
     .into(imageView);
//   对原图的中心区域进行裁剪对图片进行相关设置。
Glide.with(this)
     .load(url)
     .fitCenter()
     .into(imageView);
 // 方法可组合使用、设置大小500*500 按原图中心裁剪
 String url = "http://cn.bing.com/az/hprichbg/rb/AvalancheCreek_ROW11173354624_1920x1080.jpg";
 Glide.with(this)
     .load(url)
     .override(500, 500)
     .centerCrop()
     .into(imageView);

效果:
在这里插入图片描述
11、复杂图像转换、需引入第三方框架

//添加依赖
dependencies {
    implementation 'jp.wasabeef:glide-transformations:3.3.0'
    // If you want to use the GPU Filters
    implementation 'jp.co.cyberagent.android.gpuimage:gpuimage-library:1.4.1'
}


//图片虚化
Glide.with(this)
     .load(url)
     .bitmapTransform(new BlurTransformation(this))
     .into(imageView);
    
//图片黑白化
Glide.with(this)
     .load(url)
     .bitmapTransform(new GrayscaleTransformation(this))
     .into(imageView);
//组合使用:
Glide.with(this)
     .load(url)
     .bitmapTransform(new BlurTransformation(this), new GrayscaleTransformation(this))
     .into(imageView);

三、Target(bitmap操作)

1、获取bitmap
到现在为止,我们所涉及到的代码都是直接加载图片到 ImageView 中。Glide 隐藏做了所有的网络请求和后台的线程处理,图片准备好之后切回到 UI 线程刷新 ImageView。也就是说 ImageView 在我们代码的链式结构中成为了最后一步,但是如果我们需要获取到 Bitmap 本身
的话我们就需要用到 Target 了。Target 其实就是整个图片的加载的生命周期,所以我们就可以通过它在图片加载完成之后获取到 Bitmap。
其实对于 Target 可以简单的理解为回调,本身就是一个 interface,Glide本身也为我们提供了很多 Target
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-baF8iq2w-1597399451183)(https://upload-images.jianshu.io/upload_images/595349-38406b7143d16e38.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/358/format/webp#pic_center)]
2、SimpleTarget

private SimpleTarget<Bitmap> mSimpleTarget = new SimpleTarget<Bitmap>() {
    @Override
    public void onResourceReady(Bitmap resource, GlideAnimation<? super Bitmap> animation) {
        mImageView.setImageBitmap(resource);
    }
};

private void loadImageSimpleTarget() {
    Glide.with( thi s)
        .load( mUrl )
        .asBitmap()
        .into( mSimpleTarget );
}

首先创建了一个 SimpleTarget 的对象并且实现了 onResourceReady() 方法,图片加载完之后会调用该方法,参数就有我们需要的 Bitmap 。而使用 SimpleTarget 的对象的时候就像使用 ImageView 一样,作为参数传给 into() 方法就行了,Glide 会内部去处理并返回结果给任何一个对象。这里我们为了防止加载 Gif 、 Video 或者一些位置资源时与 mSimpleTarget 冲突,所以我们调用了 asBitmap() 方法,使其只能返回 Bitmap 对象。

**改变图片大小:**加入原尺寸 1000x1000 的图片,我们显示的时候只需要是 500x500 的尺寸来节省时间和内存,你可以在 SimpleTarget 的回调声明中指定图片的大小。

private SimpleTarget<Bitmap> mSimpleTarget = new SimpleTarget<Bitmap>(500,500) {
    @Override
    public void onResourceReady(Bitmap resource, GlideAnimation<? super Bitmap> animation) {
        mImageView.setImageBitmap(resource);
    }
};

从代码中可以看到 SimpleTarget 的对象的声明没有使用匿名对象,而是单独的声明了一个变量,这里是故意这么做的,如果使用匿名内部类的方式创建 SimpleTarget 的对象,这样会增大该对象在 Glide 完成图片请求之前就被回收的可能性。

还记得前面说过 with() 方法传入 Activity 或者 Fragment 时 Glide 的图片加载会与他们的生命周期关联起来,但是如果我们使用 Target 的话,这个 Target 就有可能独立于他们的生命周期以外,这时候我们就需要使用 context.getApplicationContext() 的上下文了,这样只有在应用完全停止时 Glide 才会杀死这个图片请求。代码如下

Glide.with(mContext.getApplicationContext())
        .load(mUrl)
        .asBitmap()
        .into(target);

3、ViewTarget:自定义view 加载图片
当我们使用 Custom View 时,Glide 并不支持加载图片到自定义 view 中的,使用 ViewTarget 更容易实现。

public class CustomView extends FrameLayout {
    private ImageView mImageView;

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

    public CustomView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        mImageView = new ImageView(getContext());
        addView(mImageView , LayoutParams.MATCH_PARENT , LayoutParams.MATCH_PARENT);
    }

    public void setImage(Drawable drawable){
        mImageView.setImageDrawable(drawable);
    }
}

上面这个例子就没有办法直接使用 .into() ,如果我们使用 ViewTarget 实现!

public void loadImageTarget(Context context){
    CustomView mCustomView = (CustomView) findViewById(R.id.custom_view);

    ViewTarget viewTarget = new ViewTarget<CustomView,GlideDrawable>( mCustomView ) {
        @Override
        public void onResourceReady(GlideDrawable resource, GlideAnimation<? super GlideDrawable> glideAnimation) {
            this.view.setImage(resource);
        }
    };

    Glide.with(context)
            .load(mUrl)
            .into(viewTarget);
}

在 target 的 onResourceReady 回调方法中使用自定义 view 自己的方法去设置图片,可以看到在创建 ViewTarget 的时候传入了 CustomView 的对象。
4、Transformations篇:对图片进行处理操作(图片切圆角,灰阶处理等等)
需求我们通过 Transformations 操作 bitmap 来实现,我们可以修改图片的任意属性:尺寸,范围,颜色,像素位置等等。其实我们之前已经提到过两个 Transformation 了,即 fitCenter 和 centerCrop ,这两个是 Glide 已经实现的。
实现自己的 Transformation ,我们需要创建一个类去实现 Transformation 接口,但是要实现这个方法还是比较复杂的,接口中 transform 方法提供的参数 Resource resource 不是那么好处理的。如果你只是想要对图片(不是 Gif 和 video)做常规的 bitmap 转换,推荐使用抽象类 BitmapTransformation。它简化了很多的实现,这应该能覆盖 95% 的应用场景啦。
下面的代码实现了对图片切圆角的操作,其中 getId() 方法描述了这个 Transformation 的唯一标识,为避免意外我们需要确保它是唯一的。

public class RoundTransformation extends BitmapTransformation {
    private float radius = 0f;

    public RoundTransformation(Context context) {
        this(context, 4);
    }

    public RoundTransformation(Context context, int px) {
        super(context);
        this.radius = px;
    }

    @Override
    protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
        return roundCrop(pool, toTransform);
    }

    private Bitmap roundCrop(BitmapPool pool, Bitmap source) {
        if (source == null)
            return null;

        Bitmap result = pool.get(source.getWidth(), source.getHeight(), Bitmap.Config.ARGB_8888);
        if (result == null) {
            result = Bitmap.createBitmap(source.getWidth(), source.getHeight(), Bitmap.Config.ARGB_8888);
        }

        Canvas canvas = new Canvas(result);
        Paint paint = new Paint();
        paint.setShader(new BitmapShader(source, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP));
        paint.setAntiAlias(true);
        RectF rectF = new RectF(0f, 0f, source.getWidth(), source.getHeight());
        canvas.drawRoundRect(rectF, radius, radius, paint);
        return result;
    }

    @Override
    public String getId() {
        return getClass().getName() + Math.round(radius);
    }

}

调用 .transform() 方法,将自定义的 Transformation 的对象作为参数传递进去就可以使用你的 Transformation 了,这里也可以使用 .bitmaoTransform() 但是它只能用于 bitmap 的转换。

Glide.with(context)
    .load(mUrl)
    .transform(new RoundTransformation(context , 20))
    //.bitmapTransform( new RoundTransformation(context , 20) )
    .into(mImageView);

如果我们需要同时执行多个 Transformation 的话,我们不能使用链式的形式多次调用 .transform() 或 .bitmapTransform() 方法,即使你调用了,之前的配置就会被覆盖掉!我们可以直接传递多个转换对象给 .transform() 或 .bitmapTransform() 。

Glide.with(context)
    .load(mUrl)
    .transform(new RoundTransformation(context , 20)new RotateTransformation(context , 90f))
    .into(mImageView);

这段代码中我们把一个图片切圆角,然后做了顺时针旋转90度处理。

旋转处理代码:

public class RotateTransformation extends BitmapTransformation {

    private float rotateRotationAngle = 0f;

    public RotateTransformation(Context context, float rotateRotationAngle) {
        super( context );
        this.rotateRotationAngle = rotateRotationAngle;
    }

    @Override
    protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
        Matrix matrix = new Matrix();

        matrix.postRotate(rotateRotationAngle);

        return Bitmap.createBitmap(toTransform, 0, 0, toTransform.getWidth(), toTransform.getHeight(), matrix, true);
    }

    @Override
    public String getId() {
        return getClass().getName() + Math.round(rotateRotationAngle);
    }
}

注:这里需要注意一点 .centerCrop() 和 .fitCenter() 也都是 Transformation 所以也是遵循同时使用多个 Transformation 的规则的,即:当你使用了自定义转换后你就不能使用 .centerCrop() 或 .fitCenter() 了。

这里有一个 GLide Transformations 的库,它提供了很多 Transformation 的实现,非常值得去看,不必重复造轮子对吧!
glide-transformations
这个库有两个不同的版本,扩展版本包含了更多的 Transformation ,它是通过设备的 GPU 来计算处理的,需要有额外的依赖,所以这两个版本的设置有一点不同。还是根据需要再决定使用那个版本吧!

四、 Modules篇

Glide 的 Module 是一个可以全局改变 Glide 的行为的东西,为了定制 Glide 的行为我们要去实现 interface GlideModule 来写我们自己的代码。
modeles
参考:
《Android图片加载框架最全解析》
《Glide使用总结》
《Google推荐——Glide使用详解》

五、Glide生命周期原理

5.1Glide怎么实现页面生命周期?

5.2 Glide为什么对Fragment做缓存?

5.3 Glide如何监听网络变化?

5.4 Glide如何监测内存?

参考:Glide生命周期原理

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值