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
相关产品推荐
相关产品推荐

