You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 11:14:05