Glide 里面内置了一套图片改造机制,名叫 Transformation ;利用这套机制,我们可以轻松实现以下的效果:

调用的方法也很简单,就拿其中一个来举例:

RequestOptions requestOptions = new RequestOptions().transforms(new CenterCrop(), new CircleCrop());
Glide.with(this).load("图片url").apply(requestOptions).into(imageViewRes);

我们调用的 api 非常简单,但是我们需要知其所以然,因此我们有必要去深入源码去看下

源码分析

我们先来看下 CenterCropRoundedCorners 到底是怎么回事,然后我们会发现它们都是 Transformation 的子类,具体关系如下:

我们先来前面的几个类吧

Key

这是 Glide 用于生成加密签名的接口类,供子类去实现生成签名的方法:

public interface Key {/*** 添加一个加密的签名** @param messageDigest 用于提供信息加密算法的功能,如 MD5 或 SHA 算法*/void updateDiskCacheKey(MessageDigest messageDigest);@Overrideboolean equals(Object o);@Overrideint hashCode();
}
Transformation

Transformation(转换器),这个类可以说是 Glide 压缩裁剪图片的核心类,因为该类功能就是依据要求输出的宽高对原始资源进行压缩裁剪的转换

public interface Transformation<T> extends Key {/*** 转换原始资源并返回转换后的资源对象*/Resource<T> transform(@NonNull Context context, @NonNull Resource<T> resource,int outWidth, int outHeight);
}
BitmapTransformation

这个是 Transformation 的一个子类,它的功能就和它的名字一样,主要负责将图片资源转换为 Bitmap ,Glide 在这里处理了一些 Bitmap 复用的逻辑:

public abstract class BitmapTransformation implements Transformation<Bitmap> {@Overridepublic final Resource<Bitmap> transform(Context context,Resource<Bitmap> resource, int outWidth, int outHeight) {if (!Util.isValidDimensions(outWidth, outHeight)) {throw new IllegalArgumentException("Cannot apply transformation on width: " + outWidth + " or height: " + outHeight + " less than or equal to zero and not Target.SIZE_ORIGINAL");}//获取Glide的Bitmap复用池BitmapPool bitmapPool = Glide.get(context).getBitmapPool();//从包装类中取出BitmapBitmap toTransform = resource.get();//对比得知这次图片转换过程中的图片裁剪尺寸int targetWidth = outWidth == Target.SIZE_ORIGINAL ? toTransform.getWidth() : outWidth;int targetHeight = outHeight == Target.SIZE_ORIGINAL ? toTransform.getHeight() : outHeight;//得到图片转换后的Bitmap对象Bitmap transformed = transform(bitmapPool, toTransform, targetWidth, targetHeight);//将Bitmap对象包裹起来进行传递final Resource<Bitmap> result;if (toTransform.equals(transformed)) {result = resource;} else {result = BitmapResource.obtain(transformed, bitmapPool);}return result;}/*** 转换原始资源并返回转换后的Bitmap对象* 供子类去实现*/protected abstract Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight);
}

前面几个类都介绍完了,其实到这里大概也可以猜到了,CenterCropRoundedCorners 等,都是 BitmapTransformation 的子类,都是某种图片资源的压缩裁剪的具体实现:

public class CircleCrop extends BitmapTransformation {@Overrideprotected Bitmap transform(BitmapPool pool,Bitmap toTransform, int outWidth, int outHeight) {//核心方法return TransformationUtils.circleCrop(pool, toTransform, outWidth, outHeight);}@Overridepublic void updateDiskCacheKey(MessageDigest messageDigest) {messageDigest.update(ID_BYTES);}
}

去看源码的话,可以发现 CenterCropFitCenterCenterInside 的源码里面,最终都是调用了 TransformationUtils 里面的对应压缩裁剪方法来生成指定的 Bitmap 对象,基本可以认定 TransformationUtils 就是 Glide 用来压缩裁剪图片的工具类

TransformationUtils

从上面得知,TransformationUtils 就是 Glide 用来压缩裁剪图片的工具类,Glide 关于图片的压缩裁剪方法都是在这个工具类里面,建议大家去看下这里的源码,可以知道 Glide 对图片压缩裁剪的一些套路,这里就简单分析一两个方法吧,先看 circleCrop 里面的裁剪圆形图片的方法:

 /*** 使用图片混合模式(PorterDuff.Mode.SRC_IN)将图片转为圆形并调整到指定的宽度/高度** @param pool     一个{@link BitmapPool} 对象,用于存储或返回准备转换的{@link Bitmap}对象* @param inBitmap 准备进行裁剪压缩的图片资源 {@link Bitmap}* @param width    转换后的目标宽度* @param height   转换后的目标高度* @return 圆形的  {@link Bitmap} 或者null*/public static Bitmap circleCrop(BitmapPool pool,Bitmap inBitmap,int destWidth, int destHeight) {//计算出圆形的半径int destMinEdge = Math.min(destWidth, destHeight);float radius = destMinEdge / 2f;int srcWidth = inBitmap.getWidth();int srcHeight = inBitmap.getHeight();//计算出最大缩小倍数float scaleXv = destMinEdge / (float) srcWidth;float scaleYv = destMinEdge / (float) srcHeight;float maxScale = Math.max(scaleXv, scaleYv);//计算出裁剪的中心区域float scaledWidth = maxScale * srcWidth;float scaledHeight = maxScale * srcHeight;float left = (destMinEdge - scaledWidth) / 2f;float top = (destMinEdge - scaledHeight) / 2f;RectF destRect = new RectF(left, top, left + scaledWidth, top + scaledHeight);//从池中取出可复用的Bitmap(使用inBitmap重新绘制该复用的Bitmap)Bitmap toTransform = getAlphaSafeBitmap(pool, inBitmap);//从池中取出一个可服用的Bitmap对象用于绘制toTransform//减少内存抖动Bitmap.Config outConfig = getAlphaSafeConfig(inBitmap);Bitmap result = pool.get(destMinEdge, destMinEdge, outConfig);result.setHasAlpha(true);//上锁BITMAP_DRAWABLE_LOCK.lock();try {Canvas canvas = new Canvas(result);//将Canvas转为圆形Canvascanvas.drawCircle(radius, radius, radius, CIRCLE_CROP_SHAPE_PAINT);//在圆形Canvas上面绘制图片canvas.drawBitmap(toTransform, null, destRect, CIRCLE_CROP_BITMAP_PAINT);//清空Canvasclear(canvas);} finally {BITMAP_DRAWABLE_LOCK.unlock();}if (!toTransform.equals(inBitmap)) {//存入进行复用pool.put(toTransform);}return result;}

核心地方都上面都写好注释了,应该蛮好理解的,因为这里就只是对图片进行区域抠图以及进行了一次图片混合模式,不过这里是不是有什么问题呢?

是不是少了一些操作?是的,这里并没有对原图进行压缩裁剪的操作!

这就意味着这里是针对传入的图片进行直接操作的,如果我们直接使用 circleCrop 的话,有相对大的 OOM 风险,因为很有可能是直接对下载的原图进行直接操作,没有经过任何的压缩裁剪处理!

这也是为什么一开始贴的代码里面,会有一个 CenterCrop 的原因:

RequestOptions requestOptions = new RequestOptions().transforms(new CenterCrop(), new CircleCrop());

如果无法确保操作图片的尺寸大小的情况下,建议配合具备压缩裁剪功能的工具类来使用,来看下 TransformationUtilscenterCrop 方法吧,CenterCrop 里面最终调用的核心方法就是这个:

 public static Bitmap centerCrop(BitmapPool pool,Bitmap inBitmap, int width,int height) {if (inBitmap.getWidth() == width && inBitmap.getHeight() == height) {return inBitmap;}//扩大或缩小的倍数final float scale;//x轴的平移距离final float dx;//y轴的平移距离final float dy;Matrix matrix = new Matrix();if (inBitmap.getWidth() * height > inBitmap.getHeight() * width) {scale = (float) height / (float) inBitmap.getHeight();dx = (width - inBitmap.getWidth() * scale) * 0.5f;dy = 0;} else {scale = (float) width / (float) inBitmap.getWidth();dx = 0;dy = (height - inBitmap.getHeight() * scale) * 0.5f;}//设置缩放(扩大)倍数matrix.setScale(scale, scale);//设置平移,确保绘制的是图片的中央部分matrix.postTranslate((int) (dx + 0.5f), (int) (dy + 0.5f));//取出复用的bitmap对象Bitmap result = pool.get(width, height, getNonNullConfig(inBitmap));//保持透明度一致TransformationUtils.setAlpha(inBitmap, result);//进行矩阵转换(也就是对图片进行压缩裁剪)applyMatrix(inBitmap, result, matrix);return result;}private static void applyMatrix(Bitmap inBitmap, Bitmap targetBitmap, Matrix matrix) {//上锁BITMAP_DRAWABLE_LOCK.lock();try {//新的画布Canvas canvas = new Canvas(targetBitmap);//在画布上绘制新的位图并使用matrix进行转换canvas.drawBitmap(inBitmap, matrix, DEFAULT_PAINT);//清空画布clear(canvas);} finally {BITMAP_DRAWABLE_LOCK.unlock();}}

可以看到 Glide 对图片进行压缩裁剪是通过 Matrix 来进行线性压缩的,这种压缩方法相对耗时,不过压缩过程一般都是在子线程里面,所以影响不怎么大

自定义Transformation

或者有人会觉得同时使用 CenterCropCircleCrop 会比较麻烦,那么可以自己去定义一个 Transformation , 针对静态图片,通过继承 BitmapTransformation 来实现自己的变换就可以了,比分说给圆形图片加个外边框:

public class CircleCropWithBorderCorp extends BitmapTransformation {private final Paint mPaint;public CircleCropWithBorderCorp(int dp) {mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);mPaint.setStyle(Paint.Style.STROKE);mPaint.setStrokeWidth(dp);mPaint.setColor(Color.RED);}@Overrideprotected Bitmap transform(BitmapPool pool,  Bitmap toTransform, int outWidth, int outHeight) {Bitmap bitmap = TransformationUtils.centerCrop(pool, toTransform, outWidth, outHeight);Bitmap result = TransformationUtils.circleCrop(pool, bitmap, outWidth, outHeight);int destMinEdge = Math.min(outWidth, outHeight);int radius = (int) (destMinEdge / 2f);Canvas canvas = new Canvas(result);canvas.drawCircle(radius, radius, radius - mPaint.getStrokeWidth() / 2, mPaint);return result;}
}

这样子就可以了,然后使用一下:

    @Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);ImageView show = findViewById(R.id.iv_show);RequestOptions options = new RequestOptions().transform(new CircleCropWithBorderCorp(10));Glide.with(this).load("图片url").apply(options).into(show);}

运行效果如下:

多个Transformation

这里还有一个疑问,通过自定义 transformation 可以将 CenterCropCircleCrop 的效果合并在一起,那么我们一开始配置:

RequestOptions requestOptions = new RequestOptions().transforms(new CenterCrop(), new CircleCrop());

它们是怎么合并到一起的?再去源码看下,可以发现:

public RequestOptions transforms(@NonNull Transformation<Bitmap>... transformations) {return transform(new MultiTransformation<>(transformations), /*isRequired=*/ true);
}

它们都是传给 MultiTransformation 来做构建参数,然后去查看 MultiTransformation 的源码的话,就可以看到下面的代码:

public class MultiTransformation<T> implements Transformation<T> {//省去部分代码public Resource<T> transform(Context context, Resource<T> resource, int outWidth, int outHeight) {Resource<T> previous = resource;//遍历全部到Transformation并不断迭代转换for (Transformation<T> transformation : transformations) {Resource<T> transformed = transformation.transform(context, previous, outWidth, outHeight);if (previous != null && !previous.equals(resource) && !previous.equals(transformed)) {previous.recycle();}previous = transformed;}return previous;}//省去部分代码
}

现在就很清楚了, MultiTransformation 是通过 for 循环对全部到 Transformation 进行迭代转换,从而将不同的 Transformation 的效果进行合并

Glide 的 transformation相关推荐

  1. Android平滑图片加载和缓存库Glide使用详解

    在图片加载库烂大街的今天,选择一个适合自己使用的图片加载库已经成为了每一个Android开发者的必经之路.现在市面上知名的图片加载库有UIL,Picasso,Volley ImageLoader,Fr ...

  2. android Glide简单使用

    今天,简单讲讲Android里Glide的简单使用. Android框架系列: 一.android EventBus的简单使用 二.android Glide简单使用 对于Glide这个加载图片的框架 ...

  3. Android 强大的图片加载缓存— Glide

    在图片加载库烂大街的今天,选择一个适合自己使用的图片加载库已经成为了每一个Android开发者的必经之路.现在市面上知名的图片加载库有UIL,Picasso,Volley ImageLoader,Fr ...

  4. Android 图片框架原理——Glide源码分析

    目录 前言 一.With() 二.load() 三.into() 1. buildImageViewTarget() 2.关注1 :buildRequest 构建 3.关注2:runRequest 执 ...

  5. Android网络加载框架Glide的使用

    前言 Glide是Google推荐的用于Android平台的图片加载框架,Glide和Picasso有90%的相似度,可以说Glide是Picasso的克隆版本,只是在细节上存在不少区别.还不是很了解 ...

  6. Android图片加载框架最全解析(二),从源码的角度理解Glide的执行流程

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/53939176 本文同步发表于我的微信公众号,扫一扫文章底部的二维码或在微信搜索 郭 ...

  7. 深入解析Glide源码

    Glide 是 Google的开源项目, Glide具有获取.解码和展示视频剧照.图片.动画等功能,它还有灵活的API,这些API使开发者能够将Glide应用在几乎任何网络协议栈里.创建Glide的主 ...

  8. 从源码的角度理解Glide的执行流程

    转自:http://blog.csdn.net/guolin_blog/article/details/53939176 在本系列的上一篇文章中,我们学习了Glide的基本用法,体验了这个图片加载框架 ...

  9. Glide源码--执行流程

    Glide的基本使用 看一下郭神的博客,Android图片加载框架最全解析(八),带你全面了解Glide 4的用法 图片加载机制的基本流程 我们从这行代码看起 Glide.with(this).loa ...

  10. Android中UIL框架特点,聊聊Android优秀的图片加载缓存的开源框架?UIL、Glide、Picasso...

    今天总结下有关Android的图片开源框架UIL.Glide.Picasso.当然不止这些还有okhttp.xutlis.afinal.andbase.volley等等,今天主要是对于Glide使用进 ...

最新文章

  1. Lucene:基于Java的全文检索引擎简介(转载)
  2. js 让浏览器全屏模式的方法launchFullscreen
  3. 蓝桥杯 - 翻硬币(贪心)
  4. 兄弟割席:HTML5标准制定组织分裂
  5. ReactJs和React Native的那些事
  6. 22 CO配置-控制-产品成本控制-成本对象控制-检查制造订单 (PP) 的评估变式
  7. Linux下性能测量和调试诊断工具Systemtap
  8. 吴恩达深度学习4.2练习_Convolutional Neural Networks_Residual Networks
  9. 大众1.4t可以一直加92号汽油吗?有哪些需要注意的问题?
  10. Android Studio开发学习 - 1. 添加Activity
  11. [Git]解决Permission denied, please try again问题
  12. 省钱兄(APP、h5版本)任务悬赏点赞平台uniapp前端源码模板
  13. 一台电脑有几个计算机用户账户,多人共用一台电脑设置多个使用账户的方法
  14. 汉字转拼音 java_Java汉字转拼音工具类完整代码实例
  15. 关于商业智能BI,今天只谈这五点
  16. win2003 启用了Internet Explorer增强的安全配置
  17. 风暴孵化:手游代理加盟水很深,如何避免入坑?
  18. java基础/java调用shell命令和脚本
  19. 墨者学院—SQL过滤字符后手工注入漏洞测试
  20. GBJ2510-ASEMI电机专用25A整流桥GBJ2510

热门文章

  1. html 调用es2015模块,现在,在项目中直接部署ES2015+代码吧!
  2. python批量下载bilibili视频_python批量提取哔哩哔哩bilibili视频
  3. 苹果6p计算机在哪里设置方法,苹果手机怎么设置铃声【图文教程,不用电脑,1分钟完成】...
  4. MTK手机软件系统工程和配置简介
  5. 巨象指纹浏览器可以帮助用户做些什么?
  6. This is probably not a problem with npm. There is likely additional logging output above.
  7. DDG-1000下水
  8. 监听电源键的单击或长按事件
  9. Java基础篇--集合(collection)
  10. 深圳海伊视讯布控球成功对接海康萤石云平台安装调式