Search code examples
androidcanvasdrawingporter-duff

Black rect PorterDuff.Mode.CLEAR in ViewGroup


I created a view which can scroll horizontal(via ViewGroup). On the left side I have image(kind of a title) for row. When I scroll my view to left, my items move under row's title. Because I have transparent background on title, I see items which under this title. I want to remove part of items which under the title. I tried to draw rects, via Paint with PorterDuff.Mode.CLEAR, inside drawChild method, before drawing title. As result I had black rects under the title. I found some same questions, but in other context:

https://code.google.com/p/android/issues/detail?id=54105#c1

Android Paint PorterDuff.Mode.CLEAR drawing black color on my view

But I didn't find solution for this problem. As I understood some other bitmap has to be drew before using PorterDuff.Mode.CLEAR. In my case I can't do this. Can I erase(in my case make transparent) some rect at my view?

EDIT:

@Override
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
    if(isTitle(child)){
        Paint paint = new Paint();
        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));

        Rect rect=new Rect(child.getLeft(),child.getTop(),child.getRight(),child.getBottom());

        canvas.drawRect(rect,paint);
    }
    return super.drawChild(canvas, child, drawingTime);
}

Also I saw somewhere this method, but it didn't change my layout at all:

@Override
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
    if(isTitle(child)){
        Paint paint = new Paint();
        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));

        Bitmap b = Bitmap.createBitmap(child.getWidth(), child.getHeight(), Bitmap.Config.ARGB_8888);

        canvas.drawBitmap(b, child.getLeft(),child.getTop(), paint);
        b.recycle();
    }
    return super.drawChild(canvas, child, drawingTime);
}

EDIT 2. Tried saveLayer:

        Paint paint = new Paint();
        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));

        RectF rect=new RectF(child.getLeft(),child.getTop(),child.getRight(),child.getBottom());
        int a = canvas.saveLayer(rect, paint, Canvas.ALL_SAVE_FLAG);
        canvas.drawRect(rect,paint);
        canvas.restoreToCount(a);

EDIT 3

enter image description here

I have view which can scroll to left, right, up and down. This view contains 2 type of headers on top and on left side and I have items which can be different sizes. When I add all this items to view I add items first after that I add headers, in this case headers will be always drawn in front of items. And when user scroll left/top, items will be go to left/top side, but header will stay on their position and this item will be under the headers. When I had background for headers, it was ok, because user didn't see items under headers, but now I don't have background and user sees items under the headers. That's why I tried to use drawChild(which calls for every item) to erase part of items which under headers. But if I use

canvas.saveLayer(null, null, Canvas.ALL_SAVE_FLAG);                       
draw_whatever_you_want(canvas); 
canvas.drawRect(rect, clearPaint); 
canvas.restore();

I have to use it for all items. My code in drawChild

@Override
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
    if(!isTitle(child)){
        int left   = child.getLeft();
        int top    = child.getTop();
        int width  = getLeftHeaderWidth();
        int height = getTopHeaderHeight();
        if(left < width) {
            RectF rect = new RectF(0, 0, width, getHeight());
            canvas.saveLayer(null, null, Canvas.ALL_SAVE_FLAG);
            boolean result = super.drawChild(canvas, child, drawingTime);
            canvas.drawRect(rect, mClearPaint);
            canvas.restore();
            return result;
        } else if(top < height) {
            RectF rect = new RectF(0, 0, getWidth(), height);
            canvas.saveLayer(null, null, Canvas.ALL_SAVE_FLAG);
            boolean result = super.drawChild(canvas, child, drawingTime);
            canvas.drawRect(rect, mClearPaint);
            canvas.restore();
            return result;
        } else{
            return super.drawChild(canvas, child, drawingTime);
        }
    }else {
        return super.drawChild(canvas, child, drawingTime);
    }
}

Solution

  • i think what you are trying in @Override protected boolean drawChild is not a best thing, i would simply do the following:

    @Override
    protected void dispatchDraw(Canvas canvas) {
        canvas.save(); // it saves the current clip, if not use CLIP_SAVE_FLAG
        canvas.clipRect(childrenRect)
        super.dispatchDraw(canvas); // draw your clipped children
        canvas.restore(); // now no clip
        draw_your_headers(canvas);
    }