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

Android开发:如何使用Canvas为Bitmap添加遮罩?

解决Android中用Canvas给Bitmap加遮罩并通过MaskedDrawable设置给ImageView的问题

嘿,我来帮你搞定这个遮罩的实现!首先得明确:你提到的MaskedDrawable应该是自定义的Drawable类(系统SDK里没有这个类),核心思路是用Canvas绘制遮罩形状,再通过PorterDuffXfermode把原图和遮罩混合起来。下面一步步给你讲怎么改代码:

第一步:先实现自定义的MaskedDrawable

如果还没写这个类,先补上——它负责把原图和遮罩Bitmap混合,实现遮罩效果:

public class MaskedDrawable extends Drawable {
    private final Paint mPaint;
    private final Bitmap mOriginalBitmap;
    private final Bitmap mMaskBitmap;

    // 构造方法:传入原图和遮罩Bitmap
    public MaskedDrawable(Bitmap originalBitmap, Bitmap maskBitmap) {
        mOriginalBitmap = originalBitmap;
        mMaskBitmap = maskBitmap;

        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        // 关键:用DST_IN模式,只保留原图和遮罩重叠的部分
        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
    }

    @Override
    public void draw(@NonNull Canvas canvas) {
        // 先绘制原图
        canvas.drawBitmap(mOriginalBitmap, 0, 0, null);
        // 再绘制遮罩,通过Xfermode实现遮罩效果
        canvas.drawBitmap(mMaskBitmap, 0, 0, mPaint);
    }

    @Override
    public void setAlpha(int alpha) {
        mPaint.setAlpha(alpha);
    }

    @Override
    public void setColorFilter(@Nullable ColorFilter colorFilter) {
        mPaint.setColorFilter(colorFilter);
    }

    @Override
    public int getOpacity() {
        return PixelFormat.TRANSLUCENT;
    }

    @Override
    public int getIntrinsicWidth() {
        return mOriginalBitmap.getWidth();
    }

    @Override
    public int getIntrinsicHeight() {
        return mOriginalBitmap.getHeight();
    }
}

第二步:修改Parse回调里的代码,生成遮罩并设置Drawable

回到你从Parse获取Bitmap的success方法里,我们用Canvas动态绘制一个遮罩(这里以圆形遮罩为例,你可以改成圆角矩形、自定义形状等),然后实例化MaskedDrawable并设置给ImageView:

final WeakReference<ImageView> weakImageView = new WeakReference<>(imageViewToMask);
fetchFromParse(getContext(), new Success() {
    @Override
    public void success(Bitmap image) {
        ImageView targetImageView = weakImageView.get();
        if (targetImageView == null || image.isRecycled()) {
            // 避免ImageView已被回收或Bitmap失效的情况
            if (!image.isRecycled()) {
                image.recycle();
            }
            return;
        }

        Context context = getContext();
        // 1. 生成遮罩Bitmap:这里做圆形遮罩,大小和原图一致(你可以按需调整)
        int maskWidth = image.getWidth();
        int maskHeight = image.getHeight();
        Bitmap maskBitmap = Bitmap.createBitmap(maskWidth, maskHeight, Bitmap.Config.ARGB_8888);
        Canvas maskCanvas = new Canvas(maskBitmap);

        // 用Canvas画圆形遮罩(如果要圆角矩形,就用drawRoundRect)
        Paint maskPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        maskPaint.setColor(Color.BLACK); // 颜色不影响,关键是Alpha通道为不透明
        maskCanvas.drawCircle(maskWidth / 2f, maskHeight / 2f, Math.min(maskWidth, maskHeight)/2f, maskPaint);

        // 2. 实例化MaskedDrawable
        MaskedDrawable maskedDrawable = new MaskedDrawable(image, maskBitmap);

        // 3. 设置给ImageView
        targetImageView.setImageDrawable(maskedDrawable);

        // 注意:如果不需要单独保留maskBitmap,可以在这里recycle,但因为Drawable持有引用,建议等Drawable销毁后再处理,或者用LruCache管理Bitmap
    }
});

一些注意事项

  • 内存优化:Bitmap容易占内存,建议用LruCache缓存原图和遮罩Bitmap,避免频繁创建;不用的时候记得调用recycle()释放内存(注意不要在Drawable还在使用时回收)。
  • 遮罩形状自定义:如果要做圆角矩形,把drawCircle换成drawRoundRect;如果是不规则形状,可以从资源文件加载遮罩Bitmap(比如一张透明背景、黑色形状的PNG),不用动态绘制。
  • WeakReference判空:你已经做了ImageView的弱引用判空,还要加上Bitmap是否被回收的判断,避免空指针或崩溃。

内容的提问来源于stack exchange,提问作者Rob

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 04:10:37