Glide和Picasso是目前Android开发中很流行的图片加载库,Glide库和Picasso库也有极大的相似性,这篇文章就通过对比的方式描述一下这个两个功能强大的优秀库的使用。
1、picasso
picasso是Square公司开源的一个Android图形缓存库,不仅实现了图片异步加载的功能,还解决了android中加载图片时需要解决的一些常见问题:
2、 Glide
Glide 是一个高效、开源、 Android设备上的媒体管理框架,Glide具有获取、解码和展示视频剧照、图片、动画等功能,它还有灵活的API,这些API使开发者能够将Glide应用在几乎任何网络协议栈里。创建Glide的主要目的有两个,一个是实现平滑的图片列表滚动效果(滚动流畅),另一个是支持远程图片的获取、大小调整和展示。有以下特点:
Picasso和Glide都在jcenter上,使用时在项目中添加依赖
1、glide的应用:
TransitionOptions
决定图片加载完成如何从占位符图片(或者之前的图片)过渡,有三种TransitionOptions
1. 添加依赖库:
compile 'com.github.bumptech.glide:glide:3.8.0'
compile 'com.android.support:support-v4:25.4.0'
2.添加依赖后,开始使用
/**
* Glide加载网络图片
*
* @param imgUrl 图片地址
*/
private void GlideShowImg(String imgUrl) {
Glide.with(this) //this 是上下文 activity/fragment
.load(imgUrl)//根据地址下载图片
.listener(setRequestListner())//设置监听
.override(600, 600)//Glide加载图片大小是自动调整的,他根据ImageView的尺寸自动调整加载的图片大小,
// 并且缓存的时候也是按图片大小缓存,每种尺寸都会保留一份缓存,如果图片不会自动适配到 ImageView,调用 override(horizontalSize, verticalSize) 。
// 这将在图片显示到 ImageView之前重新改变图片大小
// .dontAnimate()//不使用glide默认的渐入渐出的动画
.fitCenter()//缩放
// .transform(new GlideRoundTransform(this,20))//显示圆角图片
// .transform(new GlideRotateTransform(this,90))//显示旋转后的图片
// .transform(new GlideCircleTransform(this))//显示圆形图片
// .transform(new GlideRoundTransform(this,20),new GlideRotateTransform(this,90))//圆角且旋转后的显示
// .animate(R.anim.sacle_rotate_anim)//以自定义动画的方式显示
.placeholder(R.mipmap.icon_default)//默认显示图片
.error(R.mipmap.icon_error)//图片加载错误显示的图片
.into(imageView);//显示
}
public RequestListener setRequestListner() {
//设置错误监听
RequestListener errorListener = new RequestListener() {
@Override
public boolean onException(Exception e, String model, Target target, boolean isFirstResource) {
//图片加载异常的回调
Log.e("onException", e.toString() + " model:" + model + " isFirstResource: " + isFirstResource);
imageView.setImageResource(R.mipmap.icon_error);
return false;
}
@Override
public boolean onResourceReady(GlideDrawable resource, String model, Target target, boolean isFromMemoryCache, boolean isFirstResource) {
//图片加载成功的回调
Log.e("onResourceReady", "isFromMemoryCache:" + isFromMemoryCache + " model:" + model + " isFirstResource: " + isFirstResource);
return false;
}
};
return errorListener;
}
代码的注释都很明确,再详细说明一下:
1. Glide可以加载以下五种图片资源,图片的加载以String 型的url为例,一下一句代码就可以实现:
DrawableTypeRequest load(String string)
DrawableTypeRequest load(Uri uri)
DrawableTypeRequest load(File file)
DrawableTypeRequest load(Integer resourceId)
DrawableTypeRequest load(URL url)
Glide.with(context).load(imageUrl).into(imageView);
2.当加载图片失败时,通过error(Drawable drawable)方法设置加载失败后的图片显示:
Glide.with(context).load(imageUrl).error(R.mipmap.ic_launcher).into(imageView);
3.当图片加载失败后,为了方便找失败的原因可以设置监听事件,通过事件中的回调方法快速准确的定位问题。
Glide.with(context).load(imageUrl)..listener(RequestListener).error(R.mipmap.ic_launcher).into(imageView);
3.1. 网络权限原因导致的错误
firstRequestSuccess.png
3.2.请求超时导致的错误
error_msg.png
3.3第一次请求成功后的监听
firstRequestSuccess.png
3.4第二次请求成功
secondSuccess.png
从这张截图可以看出第一次请求是直接从网络拿到的图片,当请求成功以后就放在缓存中,下次加载就直接从缓存中取图片。
4.Glide缓存
Glide.with( context ).load(imageUrl).skipMemoryCache(true).into(imageView);//跳过内存缓存
Glide.with( context ).load(imageUrl).diskCacheStrategy(DiskCacheStrategy.NONE).into( imageView);//跳过硬盘缓存
DiskCacheStrategy.NONE
什么都不缓存DiskCacheStrategy.SOURCE
仅仅只缓存原来的全分辨率的图像DiskCacheStrategy.RESULT
仅仅缓存最终的图像,即降低分辨率后的或者是转换后的(默认行为)DiskCacheStrategy.ALL
缓存所有版本的图像
想要指定缓存目录,新建一个类继承GlideModule
,实现他的applyOptions(Context context, GlideBuilder builder)
方法中写指定的目录及缓存大小
public class CustomCachingGlideModule implements GlideModule {
@Override
public void applyOptions(Context context, GlideBuilder builder) {
// 设置磁盘缓存为100M,缓存在内部缓存目录
int cacheSize100MegaBytes = 104857600;
builder.setDiskCache(new InternalCacheDiskCacheFactory(context, "glide_cache", cacheSize100MegaBytes));
//builder.setDiskCache(
//new ExternalCacheDiskCacheFactory(context, "glide_cache",cacheSize100MegaBytes));
// 20%大的内存缓存作为 Glide 的默认值
MemorySizeCalculator calculator = new MemorySizeCalculator(context);
int defaultMemoryCacheSize = calculator.getMemoryCacheSize();
int defaultBitmapPoolSize = calculator.getBitmapPoolSize();
int customMemoryCacheSize = (int) (1.2 * defaultMemoryCacheSize);
int customBitmapPoolSize = (int) (1.2 * defaultBitmapPoolSize);
builder.setMemoryCache(new LruResourceCache(customMemoryCacheSize));
builder.setBitmapPool(new LruBitmapPool(customBitmapPoolSize));
}
@Override
public void registerComponents(Context context, Glide glide) {
// nothing to do here
}
}
然后再使用Glide加载图片的时候加上缓存策略(eg: .diskCacheStrategy(DiskCacheStrategy.ALL
// 缓存所有尺寸的图片)就可以在你设置的缓存目录中看到缓存的图片了。
5. 图片的缩放,centerCrop()
和 fitCenter()
:
5.1 使用centerCrop是利用图片图填充ImageView设置的大小,如果ImageView的Height是match_parent则图片就会被拉伸填充
Glide.with(context).load(imageUrl).centerCrop().into(imageView);
5.2 使用fitCenter即缩放图像让图像都测量出来等于或小于 ImageView 的边界范围,该图像将会完全显示,但可能不会填满整个ImageView。
Glide.with(context).load(imageUrl).fitCenter().into(imageView);
6.自定义图片转换 transform
自定义转换,实现一些特殊的展示样式,比如显示为圆角或者圆形。可以通过创建一个新的类实现了 Transformation 接口,然后在transform()里实现过如果我们只是做图片的转换可以直接用Glide封装好的BitmapTransformation抽象类。图像转换操作只需要在transform里实现。getId() 方法描述了这个转换的唯一标识符。Glide 使用该键作为缓存系统的一部分,为了避免意外的问题,你要确保它是唯一的 。
下面看是圆角、圆形跟选转的示例代码:
6.1 圆形的代码示例
public class GlideCircleTransform extends BitmapTransformation {
public GlideCircleTransform(Context context) {
super(context);
}
@Override protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
return circleCrop(pool, toTransform);
}
private static Bitmap circleCrop(BitmapPool pool, Bitmap source) {
if (source == null) return null;
int size = Math.min(source.getWidth(), source.getHeight());
int x = (source.getWidth() - size) / 2;
int y = (source.getHeight() - size) / 2;
// TODO this could be acquired from the pool too
Bitmap squared = Bitmap.createBitmap(source, x, y, size, size);
Bitmap result = pool.get(size, size, Bitmap.Config.ARGB_8888);
if (result == null) {
result = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
}
Canvas canvas = new Canvas(result);
Paint paint = new Paint();
paint.setShader(new BitmapShader(squared, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP));
paint.setAntiAlias(true);
float r = size / 2f;
canvas.drawCircle(r, r, r, paint);
return result;
}
@Override public String getId() {
return getClass().getName();
}
6.2圆角的代码示例:
/**
* 将图像转换为四个角有弧度的图像
*/
public class GlideRoundTransform extends BitmapTransformation {
private float radius = 0f;
public GlideRoundTransform(Context context) {
this(context, 100);
}
public GlideRoundTransform(Context context, int dp) {
super(context);
this.radius = Resources.getSystem().getDisplayMetrics().density * dp;
}
@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);
Log.e("11aa", radius + "");
return result;
}
@Override
public String getId() {
return getClass().getName() + Math.round(radius);
}
}
6.3旋转示例代码:
/**
* 将图像做旋转操作
*/
public class GlideRotateTransform extends BitmapTransformation {
private float rotateAngle = 0f;
public GlideRotateTransform(Context context) {
this(context, 90);
}
public GlideRotateTransform(Context context, float rotateAngle) {
super(context);
this.rotateAngle = rotateAngle;
}
@Override
protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
Matrix matrix = new Matrix();
matrix.postRotate(rotateAngle);
return Bitmap.createBitmap(toTransform, 0, 0, toTransform.getWidth(), toTransform.getHeight(), matrix, true);
}
@Override
public String getId() {
return getClass().getName() + rotateAngle;
}
}
需要注意的一点transform()如果多次调用,后面的效果会覆盖前面的,所以不要多次调用。若需要实现组合效果,transform可以接受任意长度的参数,所以可以组合使用:
public DrawableRequestBuilder transform(BitmapTransformation... transformations) {
return bitmapTransform(transformations);
}
//应用:
.transform(new GlideRoundTransform(this,20),new GlideRotateTransform(this,90))//圆角且旋转后的显示
下面说说Glide4.0
的新的使用方式,在Glide4.0
版及以上版本,Glide
使用了annotation processor
来生成API
,允许应用修改RequestBuilder
、RequestOptions
和任意的包含在单一流式API
库中的方法。这是V4的特性,运用注解后使用起来更方便,Glidev4
中的Glide.with().load()
后没有之前版本的fitCenter
和placeholder
这样的方法,但是GlideApp
有,可以直接在builder
中使用。GlideApp
可以代替之前版本的Glide
开头。
dependencies {
compile 'com.github.bumptech.glide:glide:4.3.1'
annotationProcessor 'com.github.bumptech.glide:compiler:4.3.1'
}
在项目中实现 AppGlideModule
,注意这个必须有注解,否则添加的方法失效。如果是library
就实现LibraryGlideModule
@GlideModule
public class CustomGlideModule extends AppGlideModule {}
也可以添加新的方法、修改已有的方法或者添加对其他类型格式的支持,只要在扩展中使用加了注解的静态方法。
eg: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);
GlideType
用来支持新的格式。在刚才的 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 requestBuilder) {
requestBuilder
.transition(new DrawableTransitionOptions())
.apply(DECODE_TYPE_GIF);
}
}
编译工程,打开build
目录中的GlideRequests
,可以看见自动生成了一个方法:
public class GlideRequests extends RequestManager {
/**
* @see CustomGlideExtension#asGIF(RequestBuilder)
*/
public GlideRequest asGIF() {
GlideRequest requestBuilder = this.as(GifDrawable.class);
CustomGlideExtension.asGIF(requestBuilder);
return requestBuilder;
}
}
引用自定义的GlideType
:
GlideApp.with(fragment)
.asGIF()
.load(url)
.into(imageView);
Glide
中的大多请求参数都可以通过RequestOptions
类和apply()
方法来设置。Placeholders
占位符
、Transformations
变换、Caching Strategies
缓存策略、组件特定参数:编码质量,解码参数等。
eg:要将图片的显示方式设为CenterCrop
import static com.bumptech.glide.request.RequestOptions.centerCropTransform;
Glide.with(fragment)
.load(url)
.apply(centerCropTransform(context))
.into(imageView);
但是其实完全可以在layout
文件中设置ImageView
为android:scaleType="centerCrop"
,Glide
会自动根据这个属性设置图片的显示方式。apply
方法可以调用多次,但是如果两次apply
存在冲突的设置,会以最后一次为准。
TransitionOptions
决定图片加载完成如何从占位符图片(或者之前的图片)过渡,有三种TransitionOptions
GenericTransitionOptions
通用型DrawableTransitionOptions
BitmapTransitionOptions
如果要使用自定义的动画,可以使用GenericTransitionOptions.with(int viewAnimationId)
或者BitmapTransitionOptions.withCrossFade(int animationId, int duration)
或者DrawableTransitionOptions.withCrossFade(int animationId, int duration)
Glide.with(fragment)
.load(url)
.transition(DrawableTransitionOptions.withCrossFade())
.into(view);
注意
TransitionOptions
是和你要加载的资源的类型绑定的,也就是说,如果你请求一张位图(Bitmap),你就需要使用BitmapTransitionOptions
,而不是DrawableTransitionOptions
因此,你请求的这张位图,你需要用简单的淡入,而不能用交叉淡入DrawableTransitionOptions.withCrossFade()
。
如果既不是Bitmap也不是Drawable可以使用GenericTransitionOptions
作用:
- 指定加载类型。
asBitmap()
、asGif()
、asDrawable()
、asFile()
。- 指定要加载url/model。
- 指定要加载到那个View。
- 指定要应用的
RequestOption
- 指定要应用的
TransitionOption
- 指定要加载的缩略图
5.1 初始化RequestBuild
RequestBuilder requestBuilder = Glide.with(fragment);
默认得到一个Drawable RequestBuilder
,如果要指定类型为Bitmap
,可以这样写:
RequestBuilder requestBuilder = Glide.with(fragment).asBitmap();
5.2 应用 RequestOptions
RequestBuilder requestBuilder = Glide.with(fragment);
requestBuilder.apply(requestOptions);
requestBuilder.transition(transitionOptions);
5.3 RequestBuilder
也可以重复使用:
RequestBuilder 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);
}
Glide
会自动读取ImageView
的缩放类型,所以一般在layout文件指定scaleType
即可。Glide
支持在java
代码中设置这些缩放类型:
- CenterCrop 缩放宽和高都到达View的边界,有一个参数在边界上,另一个参数可能在边界上,也可能超过边界
- CenterInside 如果宽和高都在View的边界内,那就不缩放,否则缩放宽和高都进入View的边界,有一个参数在边界上,另一个参数可能在边界上,也可能在边界内
- CircleCrop 圆形且结合了CenterCrop的特性
- FitCenter 缩放宽和高都进入View的边界,有一个参数在边界上,另一个参数可能在边界上,也可能在边界内
- RoundedCorners 圆角
有三种用法:
6.1 使用RequestOptions
RequestOptions options = new RequestOptions();
options.centerCrop();
Glide.with(fragment)
.load(url)
.apply(options)
.into(imageView);
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);
6.4 多个变换
Glide.with(fragment)
.load(url)
.transform(new MultiTransformation(new FitCenter(), new YourCustomTransformation())
.into(imageView);
1. 首先也是添加依赖:
compile 'com.squareup.picasso:picasso:2.5.2'
2. 以网络图片加载为例介绍下picasso的基本用法:
/**
* @param imgUrl 用picasso加载图片
*/
private void picassoShowImg(String imgUrl) {
Picasso.with(this)
.load(imgUrl)
.placeholder(R.mipmap.icon_default)//加载过程中的图片显示
// .noPlaceholder()//加载过程中不显示默认图片
// .noFade()//Picasso的默认图片加载方式有一个淡入的效果,如果调用了noFade(),加载的图片将直接显示在ImageView上(根据需求添加此代码)
.error(R.mipmap.icon_error)
.resize(400, 400)
.onlyScaleDown()//如果我们调用了resize(x,y)方法的话,Picasso一般会重新计算以改变图片的加载质量,
// 比如一张小图变成一张大图进行展示的时候,但是如果原图是比重新resize的新图规格大的时候,就可以调用onlyScaleDown()来直接进行展示而不再重新计算
.centerInside()//图片会被完整的展示,可能图片不会填充满ImageView`,也有可能会被拉伸或者挤压
// .centerCrop()//图片会被剪切,但是图片质量看着没有什么区别
// .fit()//对图片的大小及ImageView进行测量,计算出最佳的大小及最佳的图片质量来进行图片展示,减少内存,并对视图没有影响;
.priority(Picasso.Priority.HIGH)//图片加载的优先级,分为HIGH, MEDIUM, 和 LOW,所有的加载默认优先级为MEDIUM;
.into(imageView);
}
2.1 load()
还有以下几种重载
1、load(Uri uri)
2、load(File file)
3、load(int resourceId)
3.加载过程中的图片显示:
Picasso.with(this)
.load(imgUrl)
.placeholder(R.mipmap.icon_default)//加载过程中的图片显示
.into(imageView);
4、加载失败后错误图片显示:
Picasso.with(this)
.load(imgUrl)
.error(R.mipmap.icon_error)//加载失败错误图片显示
.into(imageView);
5、图片填充样式
5.1 centerInside()
按比例裁减图片,图片可以完全显示,但如果图片比View小,则无法充满整个View,必须与resize()
方法同时使用。
Picasso.with(this)
.load(imgUrl)
.resize(320, 640)
.centerInside()
.into(imageView);
5.2 centerCrop()
按比例裁减图片,使其居中显示,充满View,会造成图片显示不全,必须与resize()
方法同时使用。
Picasso.with(this)
.load(imgUrl)
.resize(320, 640)
.centerCrop()
.into(imageView);
5.3 fit()
该属性会根据Image View的大小充满整个View,不考虑比例,可能造成图片的拉伸或者缩小。
Picasso.with(this)
.load(imgUrl)
.resize(320, 640)
.centerCrop()
.into(imageView);
5.4 onlyScaleDown()
这里面使用的测试图片的大小是12401563,如果resize的宽高大于图片的原始宽高,则resize不起作用,采用图片原始宽高显示。
Picasso.with(this)
.load(imgUrl)
.resize(320, 640)
.onlyScaleDown()
.into(imageView);
6. noFade()
取消图片的过渡显示效果。
Picasso.with(this)
.load(imgUrl)
.resize(320, 640)
.noFade()
.into(imageView);
7rotate()
图片旋转。
Picasso.with(this)
.load(imgUrl)
.rotate(45)//以(0,0)为中心顺时针旋转45度
// .rotate(45,30,30)//以(30,30)为中心顺时针旋转45度
.into(imageView);
picasso_rotate_45.png
8、priority()
优先级
Picasso.with(this)
.load(imgUrl)
.priority(Picasso.Priority.HIGH)//分为HIGH, NORMAL, 和 LOW,默认优先级为NORMAL;
.into(imageView);
9、缓存策略
Picasso.with(this)
.load(imgUrl)
.priority(Picasso.Priority.HIGH)//分为HIGH, NORMAL, 和 LOW,默认优先级为NORMAL;
.into(imageView);
小礼物走一走,来简书关注我
作者:紫依卓兰
链接:https://www.jianshu.com/p/c4e23613af49
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。