前面几篇文章介绍了Glide的基本使用,源码分析,和高级使用,这篇文章继续介绍|Glide的高级使用方法,自定义图片转换,其实Glide是默认的实现了几种图片转换的。比如DrawableRequestBuilder类的fitCenter()和centerCrop()方法其实就是对图片进行了转换。在介绍如何自定义图片转换前,先来看看Glide自身提供的图片转换的使用。
1.首先准备一个布局文件,在布局文件中展示不同的图片转换方式的显示效果
2.在Activity中,使用默认的图片转换方式
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool;
import com.bumptech.glide.load.model.GlideUrl;
import com.bumptech.glide.load.resource.bitmap.BitmapTransformation;
import com.bumptech.glide.load.resource.drawable.GlideDrawable;
import com.bumptech.glide.request.FutureTarget;
import com.bumptech.glide.request.Request;
import com.bumptech.glide.request.RequestListener;
import com.bumptech.glide.request.animation.GlideAnimation;
import com.bumptech.glide.request.target.SimpleTarget;
import com.bumptech.glide.request.target.SizeReadyCallback;
import com.bumptech.glide.request.target.Target;
import com.bumptech.glide.request.target.ViewTarget;
import java.io.File;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import test.cn.example.com.androidskill.MyApplication;
import test.cn.example.com.androidskill.R;
import test.cn.example.com.util.LogUtil;
import test.cn.example.com.util.ToastUtils;
public class GlideDemoActivity2 extends AppCompatActivity implements View.OnClickListener {
private ImageView iv;
private String cropUrl = "https://up.sc.enterdesk.com/edpic/ad/52/78/ad5278b2b0f1b4c83c786e82165631ce.jpg";
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
private LinearLayout ll_root;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_glide_demo2);
ll_root = findViewById(R.id.ll_root);
findViewById(R.id.btn_8).setOnClickListener(this);
findViewById(R.id.btn_9).setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.btn_8:
reset();
LogUtil.i("iv.getScaleType()= "+iv.getScaleType());
Glide.with(this).load(cropUrl)
.skipMemoryCache(true)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.fitCenter()
.into(iv);
break;
case R.id.btn_9:
reset();
LogUtil.i("iv.getScaleType()= "+iv.getScaleType());
Glide.with(this).load(cropUrl)
.skipMemoryCache(true)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.centerCrop()
.into(iv);
break;
}
}
private void reset() {
ll_root.removeView(iv);
iv = new ImageView(this);
iv.setLayoutParams(layoutParams);
ll_root.addView(iv);
}
}
点击默认裁剪按钮,会出现如下图效果:
可以看到,图片是完全展示的。
如果点击CENTERCROP按钮,采用centerCrop()方法其实就是对图片进行了转换,可以看到如下的显示效果:
可以看到对图片采用不同的转换方式,就出现不同的效果。
fitCenter(): 图像裁剪,图像将会完全显示,但可能不会填满整个 ImageView。
centerCrop():图片裁剪,ImageView 可能会完全填充,但图像可能不会完整显示。
之所以说fitCenter()是默认的处理方式,是因为,ImageView默认的ScaleType是FIT_CENTER。在Glide的源码中,会有一个根据传入的ImageView的ScaleType来决定对图片进行何种图片转换的处理,具体的代码逻辑如下:
public class GenericRequestBuilder implements Cloneable {
...
public Target into(ImageView view) {
Util.assertMainThread();
if (view == null) {
throw new IllegalArgumentException("You must pass in a non null View");
}
if (!isTransformationSet && view.getScaleType() != null) {
//获取view的scaleType,然后进行不同的处理。由于ImageView默认是FIT_CENTER,所以,默认的图片处理逻辑就执行applyFitCenter()方法,
//这个applyFitCenter()中又调用了fitCenter()方法,所以说fitCenter()是默认的图片转换方式
//关键代码
switch (view.getScaleType()) {
case CENTER_CROP:
applyCenterCrop();
break;
case FIT_CENTER:
case FIT_START:
case FIT_END:
applyFitCenter();
break;
//$CASES-OMITTED$
default:
// Do nothing.
}
}
return into(glide.buildImageViewTarget(view, transcodeClass));
}
...
@Override
void applyFitCenter() {
fitCenter();
}
...
}
在GenericRequestBuilder的into方法中,关键代码处,获取view的scaleType,然后进行不同的处理。由于ImageView默认是FIT_CENTER,所以,默认的图片处理逻辑就执行applyFitCenter()方法,这个applyFitCenter()中又调用了fitCenter()方法,所以说fitCenter()是默认的图片转换方式。
看了Glide提供的图片转换的效果,下面继续看下其源码中是如何实现的,只有知道了实现原理,就可以依葫芦画瓢,写出自定义的图片转换方式。下面看看 fitCenter() 方法的具体实现:
public class DrawableRequestBuilderextends GenericRequestBuilder
implements BitmapOptions, DrawableOptions {
...
public DrawableRequestBuilder fitCenter() {
return transform(glide.getDrawableFitCenter());
}
...
@Override
public DrawableRequestBuilder transform(Transformation... transformation) {
super.transform(transformation);
return this;
}
...
}
这个方法内部其实是调用了transform()方法,所以,其实Glide的内部的图片转换其实是通过transform()方法来完成的,下面接着看,分itCenter()方法内部调用transform()方法时,传入的参数glide.getDrawableFitCenter()其实就是一个GifBitmapWrapperTransformation类型的对象。下面看看GifBitmapWrapperTransformation类的具体实现。
public class GifBitmapWrapperTransformation implements Transformation {
private final Transformation bitmapTransformation;
private final Transformation gifDataTransformation;
public GifBitmapWrapperTransformation(BitmapPool bitmapPool, Transformation bitmapTransformation) {
this(bitmapTransformation, new GifDrawableTransformation(bitmapTransformation, bitmapPool));
}
GifBitmapWrapperTransformation(Transformation bitmapTransformation,
Transformation gifDataTransformation) {
this.bitmapTransformation = bitmapTransformation;
this.gifDataTransformation = gifDataTransformation;
}
@Override
public Resource transform(Resource resource, int outWidth, int outHeight) {
Resource bitmapResource = resource.get().getBitmapResource();
Resource gifResource = resource.get().getGifResource();
if (bitmapResource != null && bitmapTransformation != null) {
Resource transformed = bitmapTransformation.transform(bitmapResource, outWidth, outHeight);
if (!bitmapResource.equals(transformed)) {
GifBitmapWrapper gifBitmap = new GifBitmapWrapper(transformed, resource.get().getGifResource());
return new GifBitmapWrapperResource(gifBitmap);
}
} else if (gifResource != null && gifDataTransformation != null) {
Resource transformed = gifDataTransformation.transform(gifResource, outWidth, outHeight);
if (!gifResource.equals(transformed)) {
GifBitmapWrapper gifBitmap = new GifBitmapWrapper(resource.get().getBitmapResource(), transformed);
return new GifBitmapWrapperResource(gifBitmap);
}
}
return resource;
}
@Override
public String getId() {
return bitmapTransformation.getId();
}
}
可以看到,GifBitmapWrapperTransformation 类是实现了Transformation接口,里面有transform()和getId()两个方法是需要实现,
transform()方法中的逻辑就是具体的图片转换的逻辑,getId()方法中返回的是这个转换的id,也就是这个转换的唯一标识符,一般用全类名作为返回值即可。到这里就弄明白了,DrawableRequestBuilder的fitCenter()方法内部,其实是调用了transform()方法,并将GifBitmapWrapperTransformation 的实例传入,最终经过层层调用,会调用到GifBitmapWrapperTransformation 类的transform()来完成图片的具体转换逻辑,并将转换后的图片绘制出来并返回。弄清楚了Glide内部自身提供的图片转换原理后,下面就可以依葫芦画瓢,写一个自定义的图片转换类来完成图片的四个角是指定的角度的功能。在写自定义的Transformation之前,在介绍一个类,BitmapTransformation ,这个类,已经对Transformation进行了部分实现。所以,可以自定义一个Transformation继承BitmapTransformation 。
public abstract class BitmapTransformation implements Transformation {
...
}
可以直接继承这个类,写一个自定义的图片转换Transformation。下面来实现一个将图片的四个角,转换成指定的角度的BitmapTransformation 。
class RoundCornerCrop extends BitmapTransformation {
private int radius;
private int margin;
public RoundCornerCrop(Context context) {
super(context);
}
public RoundCornerCrop(Context context,int radius,int margin){
this(context);
this.radius = radius;
this.margin = margin;
}
@Override
protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
LogUtil.i(""+toTransform);
int width = toTransform.getWidth();
int height = toTransform.getHeight();
Bitmap bitmap = pool.get(width,height,Bitmap.Config.ARGB_8888);
if(null == bitmap){
bitmap = Bitmap.createBitmap(width,height,Bitmap.Config.ARGB_8888);
}
LogUtil.i(""+bitmap);
bitmap.setHasAlpha(true);
bitmap.setDensity(toTransform.getDensity());
Canvas canvas = new Canvas(bitmap);
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setShader(new BitmapShader(toTransform, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP));
float right = width - margin;
float bottom = height - margin;
canvas.drawRoundRect(new RectF(margin,margin,right,bottom),radius,radius,paint);
return bitmap;
}
@Override
public String getId() {
return "test.cn.example.com.androidskill.optimize.bitmap.GlideDemoActivity2.RoundCornerCrop";
}
}
完成了这个自定义的RoundCornerCrop这个图片转换的Transformation,下面就使用它,看看转换的效果:
在上面的GlideDemoActivity2 类中,添加R.id.btn_11的点击事件,然后在点击事件中,做如下处理
case R.id.btn_11:
reset();
Glide.with(this).load(cropUrl)
.skipMemoryCache(true)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.bitmapTransform(new RoundCornerCrop(this,100,0))
.into(iv);
点击“下图中的"自定义图片裁剪(rondcorner)"按钮,可以看到如下图转换后的图片效果:
可以看到,图片的四个顶角都变成了圆角了。
当然,平时项目开发中,使用圆形图片的地方很多,下面也实现了一个圆形图片的自定义Transformation
public class CircleCrop extends BitmapTransformation {
public CircleCrop(Context context) {
super(context);
}
public CircleCrop(BitmapPool bitmapPool) {
super(bitmapPool);
}
@Override
public String getId() {
return "test.cn.example.com.androidskill.optimize.bitmap.GlideDemoActivity2.CircleCrop";
}
@Override
protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
int diameter = Math.min(toTransform.getWidth(), toTransform.getHeight());
final Bitmap toReuse = pool.get(outWidth, outHeight, Bitmap.Config.ARGB_8888);
final Bitmap result;
if (toReuse != null) {
result = toReuse;
} else {
result = Bitmap.createBitmap(diameter, diameter, Bitmap.Config.ARGB_8888);
}
int dx = (toTransform.getWidth() - diameter) / 2;
int dy = (toTransform.getHeight() - diameter) / 2;
Canvas canvas = new Canvas(result);
Paint paint = new Paint();
BitmapShader shader = new BitmapShader(toTransform, BitmapShader.TileMode.CLAMP,
BitmapShader.TileMode.CLAMP);
if (dx != 0 || dy != 0) {
Matrix matrix = new Matrix();
matrix.setTranslate(-dx, -dy);
shader.setLocalMatrix(matrix);
}
paint.setShader(shader);
paint.setAntiAlias(true);
float radius = diameter / 2f;
canvas.drawCircle(radius, radius, radius, paint);
if (toReuse != null && !pool.put(toReuse)) {
toReuse.recycle();
}
return result;
}
}
在上面的GlideDemoActivity2 类中,添加R.id.btn_12的点击事件,然后在点击事件中,做如下处理
case R.id.btn_11:
reset();
Glide.with(this).load(cropUrl)
.skipMemoryCache(true)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.listener(new RequestListener() {
@Override
public boolean onException(Exception e, String model, Target target, boolean isFirstResource) {
LogUtil.i("图片加载失败 "+((e!=null)?e.getMessage():""));
return false;
}
@Override
public boolean onResourceReady(GlideDrawable resource, String model, Target target, boolean isFromMemoryCache, boolean isFirstResource) {
LogUtil.i("图片加载成功");
return false;
}
})
.bitmapTransform(new CircleCrop(this))
.into(iv);
break;
点击“下图中的"自定义图片裁剪(circle)"按钮,可以看到如下图转换后的图片效果:
可以看到,图片变成了圆形图片了。多种图片的Transformation效果是可以叠加使用的。
当然,图片变换的效果还有很多,比如,模糊处理,黑白处理等,github上已经有人实现了很多的效果,大家项目中如果用到,可以使用
这个库:
glide-transformations的项目主页地址是 https://github.com/wasabeef/glide-transformations
具体用法,大家自己按照示例使用即可。
参考:
Android图片加载框架最全解析(五),Glide强大的图片变换功能
glide-transformations