Glide提供了多种默认的图形变换,如centerCrop(中心剪裁,同imageview的scaletype)等,可以通过在链式语法中加入transform(BitmapTransmation)来设置图片的变换。centerCrop实际上就是调用transform(new CenterCrop)。那先来看看这个centerCrop,直接继承自BitmapTransmation,核心方法就一个:
@SuppressWarnings("PMD.CompareObjectsWithEquals") @Override protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) { final Bitmap toReuse = pool.get(outWidth, outHeight, toTransform.getConfig() != null ? toTransform.getConfig() : Bitmap.Config.ARGB_8888); Bitmap transformed = TransformationUtils.centerCrop(toReuse, toTransform, outWidth, outHeight); if (toReuse != null && toReuse != transformed && !pool.put(toReuse)) { toReuse.recycle(); } return transformed; }
首先从Bitmap缓存池中取出一个目标宽高的可用的Bitmap(为了节省创建bitmap的开销,可能为null),然后交由TransformationUtils去完成图片变换,最后看这个bitmap能否回收。
看看这个执行变换的centerCrop:
public static Bitmap centerCrop(Bitmap recycled, Bitmap toCrop, int width, int height) { if (toCrop == null) { return null; } else if (toCrop.getWidth() == width && toCrop.getHeight() == height) { return toCrop; } // From ImageView/Bitmap.createScaledBitmap. final float scale; float dx = 0, dy = 0; Matrix m = new Matrix(); if (toCrop.getWidth() * height > width * toCrop.getHeight()) { scale = (float) height / (float) toCrop.getHeight(); dx = (width - toCrop.getWidth() * scale) * 0.5f; } else { scale = (float) width / (float) toCrop.getWidth(); dy = (height - toCrop.getHeight() * scale) * 0.5f; } m.setScale(scale, scale); m.postTranslate((int) (dx + 0.5f), (int) (dy + 0.5f)); final Bitmap result; if (recycled != null) { result = recycled; } else { result = Bitmap.createBitmap(width, height, getSafeConfig(toCrop)); } // We don't add or remove alpha, so keep the alpha setting of the Bitmap we were given. TransformationUtils.setAlpha(toCrop, result); Canvas canvas = new Canvas(result); Paint paint = new Paint(PAINT_FLAGS); canvas.drawBitmap(toCrop, m, paint); return result; }
主要借助了Matrix设置缩放比和原图中可用部分的起始坐标,然后drawBitmap把原图画到结果bitmap的画布上。
首先判断原图和目标图的宽高比,如果原图比较胖则取result.height/beforeHeight作为缩放比,接下来再计算原图中起始点的坐标就可以了。
我们想要的效果是
照葫芦画瓢,我们也来自定义一个CircleTrans继承BitmapTransmation,getId方法随便返回一个字符串作为本转换的唯一标识即可,主要是对transform的实现:
@Override protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) { //从缓存池中取出一个bitmap final Bitmap toReuse = pool.get(outWidth, outHeight, Bitmap.Config.ARGB_8888); final Bitmap result; if (toReuse != null) { result = toReuse; } else { result = Bitmap.createBitmap(outWidth, outHeight, Bitmap.Config.ARGB_8888); } //目标圆的半径 float radious=Math.min(outHeight,outWidth)/2; //计算缩放比和原图中的起始坐标 final float scale; float dx=0,dy=0; if (toTransform.getWidth()>toTransform.getHeight()){ scale=(float) outHeight/(float) toTransform.getHeight(); dx=(toTransform.getWidth()-toTransform.getHeight())*0.5f; } else { scale=(float) outWidth/(float) toTransform.getWidth(); dy=(toTransform.getHeight()-toTransform.getWidth())*0.5f; } Matrix matrix=new Matrix(); matrix.setScale(scale,scale); matrix.postTranslate((int)(dx+0.5f),(int)(dy+0.5f)); Canvas canvas = new Canvas(result); Paint paint = new Paint(); //后面参数表示边缘复制 BitmapShader shader = new BitmapShader(toTransform, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP); shader.setLocalMatrix(matrix); paint.setShader(shader); paint.setAntiAlias(true);//抗锯齿 //绘制,可以看到主要还是对画笔的设置,将原图按矩阵缩放并设置起始点后交给画笔。 canvas.drawCircle(outWidth/2, outHeight/2, radious, paint); //判断能否回收Bitmap if (toReuse != null && !pool.put(toReuse)) { toReuse.recycle(); } return result; }写的应该是比较清楚了,借助矩阵将原图缩放并设置起始位置,这时他在我们眼里相当于一个边长为min(outWidth,outHeight)的正方形,然后交给画笔作为圆形画到目标bitmap的画布上就可以了。
使用:
Glide.with(this) .load(path) .transform(new CircleTrans(this)) .into(iv);
效果很理想(宽match_parent,高200dp):