图片框架千千万,使用Glide最方便 (个人感觉,仅供参考)
之前使用过Picasso,一直都觉得很好用,直到有一次,在ListView中每个item都需要加载一张大图,滑动起来总有卡顿的效果,蛋痛,不知道是不是我使用的姿势没对,总感觉流畅性上面差了很多,换了Glide,内存开销小了,读取缓存更快了,页面也不卡顿了,再也不用担心这样那样的问题了。。。
送上大神对Picasso和Glide的PK博文,各有优势,自行选择
Glide不仅是Google的 “亲儿子”,更为重要的是,它支持加载GIF,这个很关键啊,,有木有
参见了郭神的Glide源码分析,在此记录下自己学习Glide的过程
使用
当前使用的是 Glide 3.7.0 稳定版 比较新的4.0 RC版应该也差不多
将Glide库引入到项目中,app->build.gradle文件中添加依赖
dependencies{ compile 'com.github.bumptech.glide:glide:3.7.0' }
或者在直接去github上下载相应的jar包导入工程libs目录下
Glide jar集中营 由衷感谢Glide开发团队的贡献
喜欢拿个挑哪个,导入后记得Add To Library
总是忘记添加权限的盆友(说的就是我自己)
<uses-permission android:name="android.permission.INTERNET" />
准备就绪,可以开始搞事情了
Glide.with(this)
.load(url)
.asBitmap()
.placeholder(R.mipmap.temp)
.error(R.mipmap.error)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.override(100, 100)
.into(imageView);
一句话完成了我能想得到的所有基本功能,接下来搞懂每个方法到底干了些什么事情
方法梳理
with()
方法,创建并返回了一个RequestManager实例,是Glide用于管理和启动请求的核心,入参包括 Context,Activity,Fragment 。需要注意的是with()
方法中传入的实例会决定Glide加载图片的生命周期,如果入参是Activity或者Fragment的实例,Glide加载图片的生命周期会跟随实例的生命周期执行、暂停或停止加载图片。如果入参是ApplicationContext,那么Glide加载图片的生命周期则跟随应用程序的生命周期,即只有当程序被杀掉的时候,图片加载才会停止。
load()
方法,指定代加载的图片资源,上面传入的url其实是一个String,即网络图片的地址,但Glide是可以加载各种各样的图片资源的,包括网络图片(通常为String,图片的全路径地址),本地图片(File文件),应用资源(R.drawable.xx或者R.mipmap.xx),二进制流(byte[]),Uri对象 and so on,load()
方法有很多重载,直接调用传入相应参数即可。
into()
方法,指定图片显示的View,当然也不仅仅只是指定View,它还有很多高级的用法,后面再说。
以上就是Glide基本使用的三步,一句话,三个方法就可以让你起飞
完成图片加载,上面三步就足够了,Glide的强大还在于提供了很多丰富的API接口,接着学习其他扩展方法,扩展方法的使用都是串接在 load()
和 into()
方法之间
asBitmap()
方法 用于指定图片格式,与之相应的还有 asGif()
asBitmap()
用于指定加载普通的静态图片,jpg、png等等
asGif()
用于指定加载gif动图
当不指定图片格式时,Glide内部会自动判断图片格式然后加载,但是指定图片格式后,加载不相应格式的图片会出现以下情况:
指定 asBitmap()
后加载动态图,只会显示静态图(动图第一帧)
指定 asGif()
后加载静态图,会加载 error()
方法指定的加载失败占位图
placeholder()
方法 用于指定加载过程中的占位图,它的入参可以应用资源(mipmap和drawable),也可以是Drawable绘制的占位。Glide有很长强大的缓存机制,已加载过的图片会直接从缓存读取,加载非常快速,所以可能指定的占位图来不及现实占位图片就加载完成,如果非要看占位效果,配合 diskCacheStrategy(DiskCacheStrategy.NONE)
方法味道更好。
error()
方法 指定异常占位图,因异常情况导致图片加载失败(网络不好,图片地址有误等等)时,就会加载异常占位图。
diskCacheStrategy()
方法 当传入参数为 DiskCacheStrategy.NONE
时可以禁用掉Glide的缓存功能。
override()
方法 指定图片大小,通常情况下,使用Glide是不需要指定图片大小的,因为Glide会自动根据加载图片View的尺寸来缓存图片,大大节省了内存的开销,牛逼吧。但是如果非得要重设图片大小,那就使用override()
吧。
多说两句
基本的扩展方法就学习完毕了,大多数情况都够用了,但是我想记录的还有一个 transform()
方法,不管其他人需不需要,反正我是真的很需要#####
其实针对transform()
,github上有大神已经搞出来了基于Glide图片裁剪相关的库,附上传送门 glide-transformations ,但还是想自己多学习和了解一下相关的内容
针对项目中各式各样的需求,基本的原图加载很多时候不能满足我们的需求,很多时候都需要对待加载的图片做各式各样的处理,如:现在大部分app中的用户头像都需要呈圆形展示,有些地方的图片列表需要带圆角展示,又或者有些图片需要高斯模糊等等。。。当然你可以直接使用自定义的圆形ImageView、圆角ImageView,但Glide中是支持自定义图形裁剪的,直接使用就好了。
Glide自带有两个裁剪方法
centerCrop() 方法
缩放图像让它填充到ImageView界限内并裁剪额外的部分(ImageView会被完全填充,但图像可能不会完整展示)
fitCenter() 方法
缩放图像让图像测量出的大小等于或小于ImageView的边界范围(图像会完全展示,但可能不会充满整个ImageView)
直接使用就可以了
Glide.with(this) .load(url) .asBitmap() .centerCrop() //.fitCenter() .placeholder(R.mipmap.temp) .error(R.mipmap.error) .diskCacheStrategy(DiskCacheStrategy.NONE) .override(100, 100) .into(imageView);
关于ImageView的scaleType属性和Glide的裁剪属性的关系可以参照这篇博文
Glide裁剪和ImageView的scaleType的关系
前面已经说了,可能在实际应用中还需要各种其他形状或者效果的裁剪,只需要自定义一个继承自BitmapTransform的类,然后根据自己的需要重写 transform()
方法,下面给出圆角和圆形参见的实例
圆形裁剪
public class CirCleTransform extends BitmapTransformation {
public CirCleTransform(Context context) {
super(context);
}
@Override
protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
return circleCrop(pool, toTransform);
}
private 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;
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();
}
}
圆角裁剪
public class RoundTransform extends BitmapTransformation {
public enum CornerType {
ALL,
TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT,
TOP, BOTTOM, LEFT, RIGHT,
OTHER_TOP_LEFT, OTHER_TOP_RIGHT, OTHER_BOTTOM_LEFT, OTHER_BOTTOM_RIGHT,
DIAGONAL_FROM_TOP_LEFT, DIAGONAL_FROM_TOP_RIGHT
}
private int mRadius;
private int mDiameter;
private int mMargin;
private CornerType mCornerType;
/**
* 构造函数 默认圆角半径4dp, 默认间距为0dp, 默认为全角模式
*
* @param context Context
*/
public RoundTransform(Context context) {
this(context, 5, 0, CornerType.ALL);
}
/**
* 构造函数
*
* @param context Context
* @param radius 圆角半径
* @param margin 边距
*/
public RoundTransform(Context context, int radius, int margin) {
this(context, radius, margin, CornerType.ALL);
}
public RoundTransform(Context context, int radius, int margin, CornerType cornerType) {
super(context);
this.mRadius = (int) Resources.getSystem().getDisplayMetrics().density * radius;
this.mMargin = margin;
this.mDiameter = mRadius * 2;
this.mCornerType = cornerType;
}
@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);
drawRoundRect(canvas, paint, source.getWidth(), source.getHeight());
return result;
}
private void drawRoundRect(Canvas canvas, Paint paint, int width, int height) {
float right = width - mMargin;
float bottom = height - mMargin;
switch (mCornerType) {
case ALL:
canvas.drawRoundRect(new RectF(mMargin, mMargin, right, bottom), mRadius, mRadius, paint);
break;
case TOP_LEFT:
drawTopLeftRoundRect(canvas, paint, right, bottom);
break;
case TOP_RIGHT:
drawTopRightRoundRect(canvas, paint, right, bottom);
break;
case BOTTOM_LEFT:
drawBottomLeftRoundRect(canvas, paint, right, bottom);
break;
case BOTTOM_RIGHT:
drawBottomRightRoundRect(canvas, paint, right, bottom);
break;
case TOP:
drawTopRoundRect(canvas, paint, right, bottom);
break;
case BOTTOM:
drawBottomRoundRect(canvas, paint, right, bottom);
break;
case LEFT:
drawLeftRoundRect(canvas, paint, right, bottom);
break;
case RIGHT:
drawRightRoundRect(canvas, paint, right, bottom);
break;
case OTHER_TOP_LEFT:
drawOtherTopLeftRoundRect(canvas, paint, right, bottom);
break;
case OTHER_TOP_RIGHT:
drawOtherTopRightRoundRect(canvas, paint, right, bottom);
break;
case OTHER_BOTTOM_LEFT:
drawOtherBottomLeftRoundRect(canvas, paint, right, bottom);
break;
case OTHER_BOTTOM_RIGHT:
drawOtherBottomRightRoundRect(canvas, paint, right, bottom);
break;
case DIAGONAL_FROM_TOP_LEFT:
drawDiagonalFromTopLeftRoundRect(canvas, paint, right, bottom);
break;
case DIAGONAL_FROM_TOP_RIGHT:
drawDiagonalFromTopRightRoundRect(canvas, paint, right, bottom);
break;
default:
canvas.drawRoundRect(new RectF(mMargin, mMargin, right, bottom), mRadius, mRadius, paint);
break;
}
}
@Override
public String getId() {
return "RoundedTransformation(radius=" + mRadius + ", margin=" + mMargin + ", diameter="
+ mDiameter + ", cornerType=" + mCornerType.name() + ")";
}
private void drawTopLeftRoundRect(Canvas canvas, Paint paint, float right, float bottom) {
canvas.drawRoundRect(new RectF(mMargin, mMargin, mMargin + mDiameter, mMargin + mDiameter),
mRadius, mRadius, paint);
canvas.drawRect(new RectF(mMargin, mMargin + mRadius, mMargin + mRadius, bottom), paint);
canvas.drawRect(new RectF(mMargin + mRadius, mMargin, right, bottom), paint);
}
private void drawTopRightRoundRect(Canvas canvas, Paint paint, float right, float bottom) {
canvas.drawRoundRect(new RectF(right - mDiameter, mMargin, right, mMargin + mDiameter), mRadius,
mRadius, paint);
canvas.drawRect(new RectF(mMargin, mMargin, right - mRadius, bottom), paint);
canvas.drawRect(new RectF(right - mRadius, mMargin + mRadius, right, bottom), paint);
}
private void drawBottomLeftRoundRect(Canvas canvas, Paint paint, float right, float bottom) {
canvas.drawRoundRect(new RectF(mMargin, bottom - mDiameter, mMargin + mDiameter, bottom),
mRadius, mRadius, paint);
canvas.drawRect(new RectF(mMargin, mMargin, mMargin + mDiameter, bottom - mRadius), paint);
canvas.drawRect(new RectF(mMargin + mRadius, mMargin, right, bottom), paint);
}
private void drawBottomRightRoundRect(Canvas canvas, Paint paint, float right, float bottom) {
canvas.drawRoundRect(new RectF(right - mDiameter, bottom - mDiameter, right, bottom), mRadius,
mRadius, paint);
canvas.drawRect(new RectF(mMargin, mMargin, right - mRadius, bottom), paint);
canvas.drawRect(new RectF(right - mRadius, mMargin, right, bottom - mRadius), paint);
}
private void drawTopRoundRect(Canvas canvas, Paint paint, float right, float bottom) {
canvas.drawRoundRect(new RectF(mMargin, mMargin, right, mMargin + mDiameter), mRadius, mRadius,
paint);
canvas.drawRect(new RectF(mMargin, mMargin + mRadius, right, bottom), paint);
}
private void drawBottomRoundRect(Canvas canvas, Paint paint, float right, float bottom) {
canvas.drawRoundRect(new RectF(mMargin, bottom - mDiameter, right, bottom), mRadius, mRadius,
paint);
canvas.drawRect(new RectF(mMargin, mMargin, right, bottom - mRadius), paint);
}
private void drawLeftRoundRect(Canvas canvas, Paint paint, float right, float bottom) {
canvas.drawRoundRect(new RectF(mMargin, mMargin, mMargin + mDiameter, bottom), mRadius, mRadius,
paint);
canvas.drawRect(new RectF(mMargin + mRadius, mMargin, right, bottom), paint);
}
private void drawRightRoundRect(Canvas canvas, Paint paint, float right, float bottom) {
canvas.drawRoundRect(new RectF(right - mDiameter, mMargin, right, bottom), mRadius, mRadius,
paint);
canvas.drawRect(new RectF(mMargin, mMargin, right - mRadius, bottom), paint);
}
private void drawOtherTopLeftRoundRect(Canvas canvas, Paint paint, float right, float bottom) {
canvas.drawRoundRect(new RectF(mMargin, bottom - mDiameter, right, bottom), mRadius, mRadius,
paint);
canvas.drawRoundRect(new RectF(right - mDiameter, mMargin, right, bottom), mRadius, mRadius,
paint);
canvas.drawRect(new RectF(mMargin, mMargin, right - mRadius, bottom - mRadius), paint);
}
private void drawOtherTopRightRoundRect(Canvas canvas, Paint paint, float right, float bottom) {
canvas.drawRoundRect(new RectF(mMargin, mMargin, mMargin + mDiameter, bottom), mRadius, mRadius,
paint);
canvas.drawRoundRect(new RectF(mMargin, bottom - mDiameter, right, bottom), mRadius, mRadius,
paint);
canvas.drawRect(new RectF(mMargin + mRadius, mMargin, right, bottom - mRadius), paint);
}
private void drawOtherBottomLeftRoundRect(Canvas canvas, Paint paint, float right, float bottom) {
canvas.drawRoundRect(new RectF(mMargin, mMargin, right, mMargin + mDiameter), mRadius, mRadius,
paint);
canvas.drawRoundRect(new RectF(right - mDiameter, mMargin, right, bottom), mRadius, mRadius,
paint);
canvas.drawRect(new RectF(mMargin, mMargin + mRadius, right - mRadius, bottom), paint);
}
private void drawOtherBottomRightRoundRect(Canvas canvas, Paint paint, float right,
float bottom) {
canvas.drawRoundRect(new RectF(mMargin, mMargin, right, mMargin + mDiameter), mRadius, mRadius,
paint);
canvas.drawRoundRect(new RectF(mMargin, mMargin, mMargin + mDiameter, bottom), mRadius, mRadius,
paint);
canvas.drawRect(new RectF(mMargin + mRadius, mMargin + mRadius, right, bottom), paint);
}
private void drawDiagonalFromTopLeftRoundRect(Canvas canvas, Paint paint, float right,
float bottom) {
canvas.drawRoundRect(new RectF(mMargin, mMargin, mMargin + mDiameter, mMargin + mDiameter),
mRadius, mRadius, paint);
canvas.drawRoundRect(new RectF(right - mDiameter, bottom - mDiameter, right, bottom), mRadius,
mRadius, paint);
canvas.drawRect(new RectF(mMargin, mMargin + mRadius, right - mDiameter, bottom), paint);
canvas.drawRect(new RectF(mMargin + mDiameter, mMargin, right, bottom - mRadius), paint);
}
private void drawDiagonalFromTopRightRoundRect(Canvas canvas, Paint paint, float right,
float bottom) {
canvas.drawRoundRect(new RectF(right - mDiameter, mMargin, right, mMargin + mDiameter), mRadius,
mRadius, paint);
canvas.drawRoundRect(new RectF(mMargin, bottom - mDiameter, mMargin + mDiameter, bottom),
mRadius, mRadius, paint);
canvas.drawRect(new RectF(mMargin, mMargin, right - mRadius, bottom - mRadius), paint);
canvas.drawRect(new RectF(mMargin + mRadius, mMargin + mRadius, right, bottom), paint);
}
}
圆角裁剪也是参照了 glide-transformations 库搞出来的,支持指定圆角的裁剪,只需传入相应的 CornerType 即可
问题小记 之前在使用自定义裁剪的时候出了一个这样的问题:
Glide.with(mContext).load(url).transform(new CircleTransform()).into(imageView);
这样加载圆形图片没有问题
Glide.with(mContext).load(url).transform(new RoundTransform(mContext)).into(imageView);
这样加载圆角图片就不起作用了
什么鬼?没搞懂啊,没有使用 centerCrop()
或者 fitCenter()
方法啊,闹哪样啊,研究了好久,发现这样使用 Glide.with(mContext).load(url).transform(new CenterCrop(), new RoundTransform(mContext)).into(imageView);
就起作用了,也没有搞懂为什么,先记录下来,后面弄懂了再来解释。
嫌麻烦可以直接使用 glide-transformations 库,里面提供了基本上我能想到的所有裁剪效果
至于Glide原理的话,相信郭神的那四篇文章已经足够啃好久好久了,附上传送门
Android图片加载框架最全解析(一),Glide的基本用法
Android图片加载框架全面解析(二),从源码角度理解Glide的执行流程
Android图片加载框架最全解析(三),深入探究Glide的缓存机制
Android图片加载框架最全解析(四),玩转Glide的回调与监听