Android Picasso 转换模块深度剖析
本人掘金号,欢迎点击关注:掘金号地址
本人公众号,欢迎点击关注:公众号地址
一、引言
在 Android 开发中,图片加载库 Picasso 凭借其简洁易用的 API 和高效的性能备受开发者青睐。其中,转换模块作为 Picasso 的重要组成部分,承担着对图片进行各种处理和转换的核心功能。无论是对图片进行缩放、裁剪、旋转,还是调整色彩、添加滤镜等操作,都依赖于转换模块的实现。本文将深入 Android Picasso 转换模块的源码,详细解析其工作原理和实现细节,帮助开发者更好地理解和运用该模块。
二、转换模块概述
2.1 模块功能
Picasso 的转换模块主要实现以下功能:
- 图片尺寸调整:根据目标视图的大小或开发者指定的尺寸,对图片进行缩放和裁剪,以适配不同的显示需求。
- 图片变换操作:包括旋转、翻转等操作,满足多样化的图片展示效果。
- 图片格式转换:在必要时对图片的格式进行转换,例如将位图转换为特定格式以优化存储或显示。
- 图片效果处理:实现一些基本的图片效果处理,如调整色彩、添加滤镜等,增强图片的视觉效果。
2.2 主要类和接口
在转换模块中,涉及到多个关键类和接口:
- Transformation:这是一个接口,定义了图片转换的抽象方法,开发者可以通过实现该接口来自定义图片转换逻辑。
- ResizeTransformation:用于实现图片尺寸调整的转换类,继承自
Transformation接口。 - RotateTransformation:负责图片旋转操作的转换类,同样继承自
Transformation接口。 - CenterCrop 和 Fit:这两个类也是实现了
Transformation接口,分别用于实现图片的中心裁剪和适配显示效果。
三、Transformation 接口分析
3.1 接口定义
// Transformation 接口定义了图片转换的抽象方法
public interface Transformation {
// 执行图片转换操作的方法,传入原始 Bitmap 返回转换后的 Bitmap
Bitmap transform(Picasso picasso, Bitmap source, int width, int height);
// 获取转换的唯一标识,用于缓存等场景判断转换是否相同
String key();
}
Transformation 接口定义了两个方法,transform 方法是核心方法,用于执行具体的图片转换逻辑,它接收 Picasso 实例、原始 Bitmap 以及目标宽度和高度作为参数,并返回转换后的 Bitmap;key 方法用于获取该转换的唯一标识,以便在缓存等场景中判断两个转换是否相同。
3.2 接口实现示例
// 自定义一个简单的灰度转换实现类,实现 Transformation 接口
public class GrayscaleTransformation implements Transformation {
@Override
public Bitmap transform(Picasso picasso, Bitmap source, int width, int height) {
// 创建一个与原始 Bitmap 相同宽度和高度的 Bitmap 对象,用于存储转换后的结果
Bitmap result = Bitmap.createBitmap(source.getWidth(), source.getHeight(), Bitmap.Config.ARGB_8888);
// 创建 Canvas 对象,用于在 result Bitmap 上绘制
Canvas canvas = new Canvas(result);
// 创建 Paint 对象,用于设置绘制的颜色和效果等属性
Paint paint = new Paint();
// 创建 ColorMatrix 对象,用于进行颜色转换操作
ColorMatrix colorMatrix = new ColorMatrix();
// 设置灰度转换的颜色矩阵,将 RGB 三个通道的值设置为相同,实现灰度效果
colorMatrix.setSaturation(0);
// 创建 ColorMatrixColorFilter 对象,将颜色矩阵应用到 Paint 对象上
ColorMatrixColorFilter colorFilter = new ColorMatrixColorFilter(colorMatrix);
paint.setColorFilter(colorFilter);
// 在 Canvas 上绘制原始 Bitmap,应用颜色转换效果
canvas.drawBitmap(source, 0, 0, paint);
// 释放原始 Bitmap 的资源,避免内存泄漏
source.recycle();
return result;
}
@Override
public String key() {
return "GrayscaleTransformation";
}
}
在上述示例中,GrayscaleTransformation 类实现了 Transformation 接口,在 transform 方法中,通过创建 ColorMatrix 并设置其饱和度为 0,实现了将图片转换为灰度图的效果;key 方法返回该转换的唯一标识字符串。
四、ResizeTransformation 类分析
4.1 类的定义和基本属性
// ResizeTransformation 类用于实现图片尺寸调整,继承自 Transformation 接口
class ResizeTransformation implements Transformation {
// 目标宽度
private final int targetWidth;
// 目标高度
private final int targetHeight;
// 是否保持宽高比
private final boolean centerInside;
// 图片的原始宽高比
private float aspectRatio;
// 构造函数,初始化目标宽度、高度、是否保持宽高比等属性
public ResizeTransformation(int targetWidth, int targetHeight, boolean centerInside) {
this.targetWidth = targetWidth;
this.targetHeight = targetHeight;
this.centerInside = centerInside;
}
}
ResizeTransformation 类包含了目标宽度、目标高度、是否保持宽高比等属性,通过构造函数进行初始化,这些属性用于控制图片尺寸调整的具体方式。
4.2 图片尺寸调整逻辑
@Override
public Bitmap transform(Picasso picasso, Bitmap source, int width, int height) {
// 获取原始图片的宽度和高度
int sourceWidth = source.getWidth();
int sourceHeight = source.getHeight();
// 计算宽高比
aspectRatio = (float) sourceWidth / (float) sourceHeight;
// 如果目标宽度或高度为 0,则根据原始宽高比和另一个非零的目标尺寸计算相应的尺寸
if (targetWidth == 0) {
targetWidth = (int) (targetHeight * aspectRatio);
} else if (targetHeight == 0) {
targetHeight = (int) (targetWidth / aspectRatio);
}
// 根据是否保持宽高比和目标尺寸,计算最终的缩放尺寸
int scaledWidth;
int scaledHeight;
if (centerInside) {
if (sourceWidth > targetWidth || sourceHeight > targetHeight) {
if (aspectRatio > 1f) {
scaledWidth = targetWidth;
scaledHeight = (int) (scaledWidth / aspectRatio);
} else {
scaledHeight = targetHeight;
scaledWidth = (int) (scaledHeight * aspectRatio);
}
} else {
scaledWidth = sourceWidth;
scaledHeight = sourceHeight;
}
} else {
if (aspectRatio > 1f) {
scaledWidth = targetWidth;
scaledHeight = (int) (scaledWidth / aspectRatio);
} else {
scaledHeight = targetHeight;
scaledWidth = (int) (scaledHeight * aspectRatio);
}
}
// 创建一个新的 Bitmap,用于存储调整尺寸后的图片
Bitmap result = Bitmap.createScaledBitmap(source, scaledWidth, scaledHeight, true);
// 释放原始 Bitmap 的资源
source.recycle();
return result;
}
在 transform 方法中,首先获取原始图片的宽高并计算宽高比,然后根据目标宽度和高度以及是否保持宽高比的条件,计算出最终的缩放尺寸。最后通过 Bitmap.createScaledBitmap 方法创建一个新的 Bitmap,将原始图片缩放至指定尺寸,并释放原始 Bitmap 的资源。
五、RotateTransformation 类分析
5.1 类的定义和基本属性
// RotateTransformation 类用于实现图片旋转操作,继承自 Transformation 接口
class RotateTransformation implements Transformation {
// 旋转角度
private final float degrees;
// 旋转中心的 x 坐标,若为 -1 则表示使用图片中心
private final float pivotX;
// 旋转中心的 y 坐标,若为 -1 则表示使用图片中心
private final float pivotY;
// 构造函数,初始化旋转角度、旋转中心坐标等属性
public RotateTransformation(float degrees, float pivotX, float pivotY) {
this.degrees = degrees;
this.pivotX = pivotX;
this.pivotY = pivotY;
}
}
RotateTransformation 类包含了旋转角度、旋转中心坐标等属性,通过构造函数进行初始化,这些属性决定了图片旋转的具体方式。
5.2 图片旋转逻辑
@Override
public Bitmap transform(Picasso picasso, Bitmap source, int width, int height) {
// 创建一个与原始 Bitmap 相同宽度和高度的 Bitmap 对象,用于存储旋转后的结果
Bitmap result = Bitmap.createBitmap(source.getWidth(), source.getHeight(), Bitmap.Config.ARGB_8888);
// 创建 Canvas 对象,用于在 result Bitmap 上绘制
Canvas canvas = new Canvas(result);
// 创建 Paint 对象,用于设置绘制的颜色和效果等属性
Paint paint = new Paint();
// 计算旋转中心坐标,如果为 -1 则使用图片中心
float x = pivotX == -1? source.getWidth() / 2f : pivotX;
float y = pivotY == -1? source.getHeight() / 2f : pivotY;
// 将画布围绕指定的旋转中心旋转指定的角度
canvas.rotate(degrees, x, y);
// 在 Canvas 上绘制原始 Bitmap
canvas.drawBitmap(source, 0, 0, paint);
// 释放原始 Bitmap 的资源
source.recycle();
return result;
}
在 transform 方法中,首先创建一个新的 Bitmap 和 Canvas,然后根据旋转中心坐标的设置计算出实际的旋转中心。接着使用 canvas.rotate 方法将画布围绕指定中心旋转指定角度,最后在旋转后的画布上绘制原始 Bitmap,并释放原始 Bitmap 的资源。
六、CenterCrop 类分析
6.1 类的定义和实现
// CenterCrop 类用于实现图片的中心裁剪,实现了 Transformation 接口
public class CenterCrop implements Transformation {
@Override
public Bitmap transform(Picasso picasso, Bitmap source, int width, int height) {
int sourceWidth = source.getWidth();
int sourceHeight = source.getHeight();
// 计算裁剪的起始 x 坐标
int x = (sourceWidth - width) / 2;
// 计算裁剪的起始 y 坐标
int y = (sourceHeight - height) / 2;
// 创建一个矩形区域,用于指定裁剪的范围
Rect srcRect = new Rect(x, y, x + width, y + height);
// 创建一个与目标尺寸相同的 Bitmap,用于存储裁剪后的结果
Bitmap result = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
// 创建 Canvas 对象,用于在 result Bitmap 上绘制
Canvas canvas = new Canvas(result);
// 创建 Paint 对象,用于设置绘制的颜色和效果等属性
Paint paint = new Paint();
// 在 Canvas 上绘制原始 Bitmap 的指定区域,实现中心裁剪效果
canvas.drawBitmap(source, srcRect, new Rect(0, 0, width, height), paint);
// 释放原始 Bitmap 的资源
source.recycle();
return result;
}
@Override
public String key() {
return "CenterCrop";
}
}
CenterCrop 类的 transform 方法中,通过计算裁剪的起始坐标,确定一个矩形区域,然后将原始 Bitmap 的该区域绘制到一个新的与目标尺寸相同的 Bitmap 上,从而实现中心裁剪的效果。key 方法返回该转换的唯一标识字符串。
七、Fit 类分析
7.1 类的定义和实现
// Fit 类用于实现图片的适配显示,实现了 Transformation 接口
public class Fit implements Transformation {
@Override
public Bitmap transform(Picasso picasso, Bitmap source, int width, int height) {
int sourceWidth = source.getWidth();
int sourceHeight = source.getHeight();
// 计算宽高比
float aspectRatio = (float) sourceWidth / (float) sourceHeight;
// 根据目标宽度和高度以及宽高比,计算缩放后的宽度和高度
int scaledWidth;
int scaledHeight;
if (width * aspectRatio > height) {
scaledWidth = (int) (height * aspectRatio);
scaledHeight = height;
} else {
scaledWidth = width;
scaledHeight = (int) (width / aspectRatio);
}
// 创建一个新的 Bitmap,用于存储适配后的图片
Bitmap result = Bitmap.createScaledBitmap(source, scaledWidth, scaledHeight, true);
// 释放原始 Bitmap 的资源
source.recycle();
return result;
}
@Override
public String key() {
return "Fit";
}
}
Fit 类的 transform 方法根据目标宽度和高度以及原始图片的宽高比,计算出缩放后的尺寸,然后通过 Bitmap.createScaledBitmap 方法创建一个新的 Bitmap,将原始图片缩放至适配尺寸,并释放原始 Bitmap 的资源。key 方法返回该转换的唯一标识字符串。
八、转换模块的工作流程
8.1 转换请求的发起
当开发者在使用 Picasso 加载图片时,通过 transform 方法或其他相关 API 传入自定义的 Transformation 实现类或使用 Picasso 内置的转换类(如 CenterCrop、Fit 等),从而发起图片转换请求。例如:
Picasso.get()
.load("https://example.com/image.jpg")
.transform(new GrayscaleTransformation()) // 传入自定义的灰度转换类
.into(imageView);
在上述代码中,通过 transform 方法传入了自定义的 GrayscaleTransformation 类,告诉 Picasso 在加载图片后要进行灰度转换操作。
8.2 转换操作的执行
在图片加载完成后,Picasso 会根据传入的转换类调用相应的 transform 方法。以 ResizeTransformation 为例,其执行过程如下:
- 首先获取原始图片的宽度和高度,计算宽高比。
- 根据目标宽度和高度以及是否保持宽高比的条件,计算出最终的缩放尺寸。
- 使用
Bitmap.createScaledBitmap方法创建一个新的Bitmap,将原始图片缩放至指定尺寸。 - 释放原始
Bitmap的资源,返回转换后的Bitmap。
对于其他转换类,如 RotateTransformation、CenterCrop 和 Fit 等,也是按照类似的流程,根据各自的逻辑在 transform 方法中对原始图片进行相应的转换操作。
8.3 转换结果的处理
转换后的 Bitmap 会被传递给后续的流程,如显示在 ImageView 中或进行进一步的处理。如果开启了缓存功能,转换后的 Bitmap 及其对应的转换标识(通过 key 方法获取)会被存入缓存中,以便下次相同的转换请求可以直接从缓存中获取,提高图片加载和转换的效率。
九、转换模块的优化策略
9.1 缓存优化
- 转换结果缓存:Picasso 会根据转换类的
key方法返回的唯一标识,将转换后的Bitmap存入缓存中。在下次遇到相同的转换请求时,可以直接从缓存中获取,避免重复的转换操作。例如,对于多次请求相同图片的灰度转换,由于GrayscaleTransformation的key方法返回固定的字符串,第二次及以后的请求可以直接从缓存中获取转换后的灰度图。
// 假设第一次请求图片并进行灰度转换
Picasso.get()
.load("https://example.com/image.jpg")
.transform(new GrayscaleTransformation())
.into(imageView1);
// 第二次请求相同图片的灰度转换,此时会从缓存中获取
Picasso.get()
.load("https://example.com/image.jpg")
.transform(new GrayscaleTransformation())
.into(imageView2);
- 缓存淘汰策略:当缓存空间不足时,Picasso 会采用一定的缓存淘汰策略,如 LRU(最近最少使用)算法,删除最近最少使用的缓存项,以腾出空间存储新的转换结果。
9.2 性能优化
- 减少内存占用:在进行图片转换操作时,及时释放原始
Bitmap的资源,避免内存泄漏和过多的内存占用。例如,在每个transform方法的最后都调用source.recycle()方法释放原始Bitmap。 - 优化转换算法:对于一些复杂的转换操作,如图片的缩放和旋转,可以采用更高效的算法来减少计算量和时间开销。例如,在进行缩放时,可以使用一些优化的插值算法来提高缩放后的图片质量和计算效率。
9.3 兼容性优化
- 不同设备和系统版本适配:考虑到 Android 设备的多样性和系统版本的差异,转换模块需要确保在各种设备和系统上都能正常工作。例如,在处理不同分辨率和屏幕密度的设备时,要准确计算图片的尺寸调整和转换参数,以保证图片在不同设备上都能正确显示。
- 图片格式兼容性:支持多种常见的图片格式,如 JPEG、PNG