Glide是Google官方推荐使用的图片加载库。大哥已经表扬人家了,小弟们能不学习学习吗?如果你现在还在使用Picasso,那么先参考这篇文章对比一下Picasso和Glide。
添加依赖:
dependencies {
compile 'com.github.bumptech.glide:glide:3.7.0' # 添加Glide依赖
compile 'jp.wasabeef:glide-transformations:2.0.0' # 可选,添加Glide变换第三方实现依赖。如果你对显示的图片有什么模糊啊,圆角什么的变换,那就添加了吧,省很多事。
compile 'jp.co.cyberagent.android.gpuimage:gpuimage-library:1.3.0' # 可选,没用过,感兴趣GitHub上看看吧.
}
应付95%使用场合:
不说废话,直接上代码:
Glide
.with(context)
.load(imageUrl) # 也可以是来自resouceId、File、Uri代表的图片资源。
.placeHolder(resourceId) # 占位符,就是图片从开始请求到最后完全加载,这段时间显示的默认图片。
.error(erroResouceId) #请求图片发生异常的错误图片。
.into(targetImageView)
That's it!如果我没有估计错,这几行代码足够对付项目中大多数的图片加载需求了。对于ListView、GridView、RecyclerView等也不用担心,加载图片也就是这些代码,其它的什么条目不可见的时候自动取消加载图片的请求等等Glide都已经替我们解决好了。
还用一点我想大声对你说的就是Glide还可以加载gif。什么都不用改,只要你给的图片资源是gif人家就能放。有时候服务器那边脑子进水给你一个gif图片的url,但实际上是张静态图片,逻辑上这个应该算是加载错误图片资源,应该让error()显神威,不过Glide无法自动探知你就是想加载gif而其它类型都是错误,所以你得用asGif()明确告诉Glide:这里我就是要gif,其它类型的图片都是错误:
Glide
.with( context )
.load( gifUrl )
.asGif()
.error( R.drawable.full_cake )
.into( imageViewGif );
还有时候你只想显示静态图片,但你是在怕了服务器那边,搞不好又进水在需要静态图片的地方给了gif,没关系,使用asBitmap()如果来的是一张gif只会显示它的第一帧:
Glide
.with( context )
.load( gifUrl )
.asBitmap()
.into( imageViewGifAsBitmap );
注意一点的就是你给with()方法的context实例,Glide的生命周期会绑定到这个context的实例上,Glide在context不同生命周期回调中有不同的处理,例如图片请求在onStop()中会暂停,在onStart()中又会重新请求;gif在onStop()会暂停播放等等。所以Application、Activity、Fragment、Service等都可以提供context,你就要根据自己的情况慎重选择适合的context实例了。
Glide还可以播放本地视频,只是本地Android可以解码的视频类型哦!
String filePath = "/storage/emulated/0/Pictures/example_video.mp4";
Glide
.with( context )
.load( Uri.fromFile( new File( filePath ) ) )
.into( imageViewGifAsBitmap );
有时候一个页面有很多图片,但有主次之分。你希望主要的图片先被加载出来,之后在加载次要的图片。你可以通过指定请求优先级来实现这样的需求,有一点需要铭记在心的是:指定优先级只不过让优先级高的先请求,但由于图片大小,图片处理时间等等也需要时间,所以不能保证最后请求优先级高的一定最先显示。
先来看一下,Glide都有哪些请求优先级别:
Priority.LOW
Priority.NORMAL
Priority.HIGH
Priority.IMMEDIATE
见名知义,不解释了。最后用priority()指定请求优先级:
Glide
.with( context )
.load( imageUrl )
.priority( Priority.HIGH )
.into( imageViewHero );
默认情况下根据给定的ImageView大小来决定要加载的图片大小。当然,你也可以使用override(horizontalSize, verticalSize)指定要加载的图片大小:
Glide
.with(context)
.load(imageUrl)
.override(600, 200)
.into(imageViewResize);
不过这样做可能会破坏原图片的比例。所以你指定大小的时候,所以你还可以用fitCenter()或centerCrop()来缩放图片,用以达到比较好的视觉效果。
Glide
.with(context)
.load(imageUrl)
.override(600, 200)
.centerCrop() #缩放图片以满足ImageView的尺寸,超过ImageView的部分将会被裁剪掉,因此最终图片可能不完全显示。
.into(imageViewResizeCenterCrop);
Glide
.with(context)
.load(UsageExampleListViewAdapter.eatFoodyImages[0])
.override(600, 200)
.fitCenter() #保持原图片的比例进行缩放,直到可以在ImageView中尺寸区域内完全显示图片。图片能够完全显示,比例保持不变,但是可能图片无法完全覆盖ImageView的区域。
.into(imageViewResizeFitCenter);
图片从无到完全显示,Glide默认是有过渡动画的,还可用crossFade(int duration)来自定义过渡动画的毫秒数。当然如果你就是喜欢吓用户,想要突然间把图片加载出来了,可以用dontAnimate()取消过渡动画:
Glide
.with(context)
.load(UsageExampleListViewAdapter.eatFoodyImages[0])
.placeholder(R.mipmap.ic_launcher)
.error(R.mipmap.future_studio_launcher)
.dontAnimate()
.into(imageViewFade);
Glide缓存使用
不用说,Glide当然会在内存和本地缓存已经请求过的图片。但有时一个url对应的图片内容变动比较大,不需要把它缓存在内存或者本地。那么可以使用skipMemoryCache(true):
Glide
.with( context )
.load( imageUrl)
.skipMemoryCache( true )
.into( imageViewInternet );
当然这只是告诉Glide图片不要在内存中缓存,Glide在本地还会照样缓存请求过的图片的。可以用diskCacheStrategy()取消本地缓存。要明白一点的是Glide对于一张从网络请求来的图片,Glide除了会在本地存储原始尺寸的图片,还会缓存加载到ImageView的较小尺寸的图片。因此diskCacheStrategy()可以接受:
- DiskCacheStrategy.NONE 本地什么都不缓存
- DiskCacheStrategy.SOURCE 本地缓存原始的全尺寸图片
- DiskCacheStrategy.RESULT 本地缓存加载到ImageView上的较小尺寸的图片
- DiskCacheStrategy.ALL 默认缓存全部尺寸的图片
Glide
.with( context )
.load( imageUrl)
.diskCacheStrategy( DiskCacheStrategy.SOURCE )
.into( imageViewFile );
自定义变换:
有时候在图片显示之前,会需要对图片进行一些变换,最常出现的需求就是将图片模糊。自定义变换主要就是实现Transformation接口,要知道我们的Glide还可以比方gif和视频,因此实现这个接口难度会大很多。当然如果只是针对静态图片,实现BitmapTransformation会容易很多:
public class BlurTransformation extends BitmapTransformation {
private RenderScript rs;
public BlurTransformation(Context context) {
super( context );
rs = RenderScript.create( context );
}
@Override
protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
Bitmap blurredBitmap = toTransform.copy( Bitmap.Config.ARGB_8888, true );
Allocation input = Allocation.createFromBitmap(
rs,
blurredBitmap,
Allocation.MipmapControl.MIPMAP_FULL,
Allocation.USAGE_SHARED
);
Allocation output = Allocation.createTyped(rs, input.getType());
ScriptIntrinsicBlur script = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));
script.setInput(input);
script.setRadius(10);
script.forEach(output);
output.copyTo(blurredBitmap);
toTransform.recycle();
return blurredBitmap;
}
@Override
public String getId() {
return "blur";
}
}
我们在transform方法里可以拿到要显示的图片toTransform以及ta的尺寸(outWidth, outHeight),接下来就可以发挥想象力处理图片然后把处理后的图片返回就像。关于上面图片模糊具体可以看这篇文章。然后就可以用transform()、或者bitmapTransform()方法来使用我们自定义的变换了:
Glide
.with( context )
.load( imageUrl)
.transform( new BlurTransformation( context ) )
//.bitmapTransform( new BlurTransformation( context ) ) #这个方法接受的变换只针对bitmap。
.into( imageView1 );
如果你要进行多种变换,transform()和bitmapTransform()可接受多个参数:
Glide
.with( context )
.load( imageUrl )
.transform( new GreyscaleTransformation( context ), new BlurTransformation( context ) )
.into( imageView2 );
在最开始添加依赖时说过已经有好人实现了许多图片变换,看看这个glide-transformations项目。是不是感觉瞬间轻松许多。
自定义动画:
之前已经提到的crossFade()是个渐变动画,现在我们还想要自己定义的更帅的动画。可以使用animate()方法,ta可以接受一个动画的资源文件:
#R.anim.scale
<?xml version="1.0" encoding="utf-8"?>
<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>
Glide
.with( context )
.load( eatFoodyImages[0] )
.animate( android.R.anim.slide_in_left )
.into( imageView1 );ava
另外animate()还接受一个ViewPropertyAnimation.Animator实现:
ViewPropertyAnimation.Animator animationObject = 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();
}
};
只要实现一个animate方法就可以啦。animate方法中的view就是传入into方法里的ImageView对象:
Glide
.with( context )
.load( imageUrl )
.animate( animationObject )
.into( imageView2 );
我这里好像发现当调用了crossFade()方法,animate()方法定义的动画好像不会被调用,也就是没有效果。另外好像过渡动画只对ImageView对象有效果,当传入into()方法的ViewTarget类型对象,crossFade()和animate()好像都不会调用。这个还有待继续验证。
回调:SimpleTarget和ViewTarget:
Target接口代表Glide中资源最终被加载到的地方并且可以毁掉Glide中的生命周期方法。Target可以回调的生命周期方法有:
- onLoadStarted
- onResourceReady
- onLoadCleared
- onLoadFailed
典型的生命周期是:onLoadStarted -> onResourceReady 或者 onLoadFailed -> onLoadCleared。实际可能某些方法不会调用,例如加入直接从内存缓存中加载资源,onLoadStarted方法便不会被调用了。
这里介绍两个Target实现类,同时也代表着两个常见的需求。
有时候我们只是希望获得一张图片,而不想把它加载到什么地方上显示,对于这个需求使用SimpleTarget最合适不过了。
private SimpleTarget target = new SimpleTarget<Bitmap>() {
@Override
public void onResourceReady(Bitmap bitmap, GlideAnimation glideAnimation) {
//在这里我们就可以获得加载的资源了,当然这里是一个bitmap。
}
};
private void loadImageSimpleTarget() {
Glide
.with( context )
.load( imageUrl)
.asBitmap()
.into( target ); //使用Target。
}
这里有点要在强调一下,就是关于传给with()方法的context实例。要记得Glide的生命周期会和这个context实例的生命周期相绑定。所以可能会有这样的场景:在activityA中获得图片,你把这个activityA传给with()方法,然后还没获得图片,用户跳转到activityB,在activityB要显示这个图片。很显然activityA在回调onStop()的时候Glide也停止请求那张图片,所以在activityB中也就没有图片可显示啦。
还可以指定SimpleTarget获得的资源的尺寸:
private SimpleTarget target2 = new SimpleTarget<Bitmap>( 250, 250 ) {
@Override
public void onResourceReady(Bitmap bitmap, GlideAnimation glideAnimation) {
imageView2.setImageBitmap( bitmap );
}
};
另一个需求那就更常见了,而且也更迫切。当你自定义一个view的时候,同时这个view还不是继承ImageView但是ta也有显示图片的需要,怎么办?用ViewTarget来办:
//自定义的一个ViewGroup
public class CustomView extends FrameLayout {
ImageView iv;
TextView tv;
public void initialize(Context context) {
inflate( context, R.layout.custom_view_, this );
iv = (ImageView) findViewById( R.id.custom_view_image );
tv = (TextView) findViewById( R.id.custom_view_text );
}
public CustomView(Context context, AttributeSet attrs) {
super( context, attrs );
initialize( context );
}
public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
super( context, attrs, defStyleAttr );
initialize( context );
}
public void setImage(Drawable drawable) {
iv = (ImageView) findViewById( R.id.custom_view_image );
iv.setImageDrawable( drawable );
}
}
CustomView customView = (CustomView) findViewById( R.id.custom_view );
//定义一个ViewTarget,注意传入自定义View对象,还有ViewTarget的泛型。
viewTarget = new ViewTarget<CustomView, GlideDrawable>( customView ) {
@Override
public void onResourceReady(GlideDrawable resource, GlideAnimation<? super GlideDrawable> glideAnimation) {
//这里我们获得传入的自定义View,在这个自定义View中我们写了该View设置图片的方法。
this.view.setImage( resource.getCurrent() );
}
};
Glide
.with( context.getApplicationContext() )
.load( eatFoodyImages[2] )
.into( viewTarget ); //使用Target。
调试和错误处理:
可以使用ADB命令来查看资源请求时的日志:
adb shell setprop log.tag.GenericRequest DEBUG [还可选VERBOSE,INFO,WARN,ERROR日志级别]
虽然有error()帮我们处理请求图片出错时的情况,但有时我们也想自己作一些处理,最起码得看看发生了什么是吧。用listener()注册一个RequestListener便可以得到请求资源时的一些关键回调方法:
private RequestListener<String, GlideDrawable> requestListener = new RequestListener<String, GlideDrawable>() {
@Override
public boolean onException(Exception e, String model, Target<GlideDrawable> target, boolean isFirstResource) {
//获得异常你想怎么处理随你的便利。
return false; //如果返回true,Glide认为你已经处理好了这个异常,那么error()方法也就不会调用了。
}
@Override
public boolean onResourceReady(GlideDrawable resource, String model, Target<GlideDrawable> target, boolean isFromMemoryCache, boolean isFirstResource) {
return false;
}
};
Glide
.with( context )
.load(UsageExampleListViewAdapter.eatFoodyImages[0])
.listener( requestListener ) //注册监听器。
.error( R.drawable.cupcake )
.into( imageViewPlaceholder );
当然Glide还有其它的监听器也很有用,例如LifecycleListener就可以在Glide里监听Fragment和Activity的onStart()、onStop()和onDestroy()回调。
集成自己的网络库:
你可以使用自己的网络库,但这个细讲起来还比较复杂。这里介绍当你的工程使用的是OkHttp或者Volley的情况,稍微配置一下build.gradle,其它什么都不用做了:
dependencies {
// 其它配置
compile 'com.github.bumptech.glide:glide:3.7.0'
// 特别注意这个,就是ta让你可以使用OkHttp作为自己的网络请求库。
compile 'com.github.bumptech.glide:okhttp-integration:1.4.0@aar'
compile 'com.squareup.okhttp:okhttp:2.7.5'
// 针对Volley的配置 。
//compile 'com.github.bumptech.glide:volley-integration:1.4.0@aar'
//compile 'com.mcxiaoke.volley:library:1.0.8'
//针对OkHttp3的配置。
//compile 'com.github.bumptech.glide:okhttp3-integration:1.4.0@aar'
//compile 'com.squareup.okhttp3:okhttp:3.2.0'
}