如何使用Picasso创建MaskTransformation?蒙版图与底图均来自服务器
使用Picasso实现从服务器加载蒙版的MaskTransformation
嘿,要实现用服务器上的蒙版图片给Picasso加载的图片做蒙版变换,核心就是先拿到蒙版的Bitmap,再用图层混合模式实现效果。我给你两种可行的方案,你可以根据场景选择:
方案一:直接在Transformation里加载蒙版URL(注意线程问题)
这种方式是把蒙版URL传给自定义的Transformation,在构造方法里同步加载蒙版Bitmap。但要注意绝对不能在主线程调用,否则会阻塞UI导致ANR。
先写自定义的MaskTransformation类:
public class MaskTransformation implements Transformation { private Context context; private String maskUrl; private Bitmap maskBitmap; public MaskTransformation(Context context, String maskUrl) { this.context = context; this.maskUrl = maskUrl; // 同步加载蒙版图片,必须在子线程执行! try { maskBitmap = Picasso.with(context).load(maskUrl).get(); } catch (IOException e) { e.printStackTrace(); // 加载失败可返回原图或设置默认蒙版 } } @Override public Bitmap transform(Bitmap source) { if (maskBitmap == null) { // 蒙版加载失败,直接返回原图 return source; } // 将蒙版缩放到和目标图片一致的尺寸 Bitmap scaledMask = Bitmap.createScaledBitmap(maskBitmap, source.getWidth(), source.getHeight(), true); // 创建最终结果Bitmap Bitmap result = Bitmap.createBitmap(source.getWidth(), source.getHeight(), Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(result); Paint paint = new Paint(); // 用DST_IN模式实现蒙版:只保留源图和蒙版重叠的区域 paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN)); canvas.drawBitmap(source, 0, 0, null); canvas.drawBitmap(scaledMask, 0, 0, paint); // 清空Xfermode避免影响后续绘制 paint.setXfermode(null); // 回收无用Bitmap,避免内存浪费 source.recycle(); scaledMask.recycle(); return result; } @Override public String key() { // 返回唯一标识,让Picasso正确缓存变换后的图片 return "MaskTransformation:" + maskUrl; } }
使用时要放在子线程,比如用AsyncTask:
new AsyncTask<Void, Void, MaskTransformation>() { @Override protected MaskTransformation doInBackground(Void... voids) { return new MaskTransformation(MainActivity.this, "http://s1.picswalls.com/wallpapers/2014/07/24/maskImage.jpg"); } @Override protected void onPostExecute(MaskTransformation transformation) { Picasso.with(getApplicationContext()) .load("http://s1.picswalls.com/wallpapers/2014/07/24/awesome-pattern-wallpaper_113041209_88.jpg") .transform(transformation) .into(a); } }.execute();
方案二:先异步加载蒙版,再应用变换(更推荐)
这种方式符合Android线程规范,先异步加载蒙版Bitmap,成功后再创建Transformation处理目标图片,不会阻塞主线程,体验更好。
首先写一个基于Bitmap的MaskBitmapTransformation:
public class MaskBitmapTransformation implements Transformation { private Bitmap maskBitmap; public MaskBitmapTransformation(Bitmap maskBitmap) { this.maskBitmap = maskBitmap; } @Override public Bitmap transform(Bitmap source) { if (maskBitmap == null) { return source; } Bitmap scaledMask = Bitmap.createScaledBitmap(maskBitmap, source.getWidth(), source.getHeight(), true); Bitmap result = Bitmap.createBitmap(source.getWidth(), source.getHeight(), Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(result); Paint paint = new Paint(); paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN)); canvas.drawBitmap(source, 0, 0, null); canvas.drawBitmap(scaledMask, 0, 0, paint); paint.setXfermode(null); source.recycle(); scaledMask.recycle(); return result; } @Override public String key() { return "MaskBitmapTransformation:" + maskBitmap.hashCode(); } }
然后用Picasso的Target异步加载蒙版:
Picasso.with(getApplicationContext()) .load("http://s1.picswalls.com/wallpapers/2014/07/24/maskImage.jpg") .into(new Target() { @Override public void onBitmapLoaded(Bitmap maskBitmap, Picasso.LoadedFrom from) { // 蒙版加载成功,创建变换并加载目标图片 MaskBitmapTransformation transformation = new MaskBitmapTransformation(maskBitmap); Picasso.with(getApplicationContext()) .load("http://s1.picswalls.com/wallpapers/2014/07/24/awesome-pattern-wallpaper_113041209_88.jpg") .transform(transformation) .into(a); } @Override public void onBitmapFailed(Drawable errorDrawable) { // 处理蒙版加载失败,比如直接显示原图 Picasso.with(getApplicationContext()) .load("http://s1.picswalls.com/wallpapers/2014/07/24/awesome-pattern-wallpaper_113041209_88.jpg") .into(a); } @Override public void onPrepareLoad(Drawable placeHolderDrawable) { // 可设置加载中的占位图 } });
几个注意点
- 记得回收无用Bitmap,避免内存泄漏;
key()方法必须返回唯一值,确保Picasso能正确缓存变换后的图片;- 蒙版图片建议用带透明通道的PNG,这样蒙版效果才会正确;
- 方案二更适合大多数场景,不会阻塞主线程,用户体验更流畅。
内容的提问来源于stack exchange,提问作者pruthwiraj.kadam
相关产品推荐
相关产品推荐

