Kingamajick Kingamajick - 6 months ago 48
Android Question

PorterDuff masking leaves opaque black background

I'm trying to mask a FrameLayout with a mask defined as a nine patch. However, although it works fine on 5.0+ on older versions (such as 4.4.4), the patch leaves an opaque black background. Is there anything that can be done to avoid this other than drawing to an off screen bitmap before rendering to the screen or reverting to software layers?

public class MaskedLayout extends FrameLayout {

private final static PorterDuffXfermode DST_IN = new PorterDuffXfermode(PorterDuff.Mode.DST_IN);
private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private NinePatchDrawable mMask;

private boolean mShowTail = true;
private boolean mReverseLayout;

public ChatBubbleLayout(Context context) {
this(context, null);
}

public ChatBubbleLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}

public ChatBubbleLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setWillNotDraw(false);
setLayerType(LAYER_TYPE_HARDWARE, mPaint);

mMask = createMask(R.drawable.mask);
}

private NinePatchDrawable createMask(@DrawableRes int res) {
final Bitmap maskBitmap = BitmapFactory.decodeResource(getResources(), res);
final NinePatch patch = new NinePatch(maskBitmap, maskBitmap.getNinePatchChunk(), "Mask");
return new NinePatchDrawable(getResources(), patch);
}

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);

if (w != oldw || h != oldh) {
mMask.setBounds(0, 0, w, h);
}
}

@Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
mMask.getPaint().setXfermode(DST_IN);
mMask.draw(canvas);
}
}

Answer

try this:

public class MaskedLayout extends FrameLayout {

    private NinePatchDrawable mMask;

    public MaskedLayout(Context context) {
        this(context, null);
    }

    public MaskedLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public MaskedLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mMask = (NinePatchDrawable) getResources().getDrawable(R.drawable.mask);
        mMask.getPaint().setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mMask.setBounds(0, 0, w, h);
    }

    @Override
    public void draw(Canvas canvas) {
        canvas.saveLayer(null, null, Canvas.ALL_SAVE_FLAG);
        super.draw(canvas);
        mMask.draw(canvas);
        canvas.restore();
    }
}