4

Here is a simplified description: Imagine that I'm given a View class that draws a picture of a wall and I want to draw it with a window cut out. Assume that I extend that View class and override its dispatchDraw() method to do the following. First draw the background if any to be seen through the window. Next I want to mask out the rectangular window region somehow and then call super.dispatchDraw(). Finally I want to remove the mask and draw a person standing at the window so that they are painted over both the background and the wall. How can I do this?

Here is some code that seems close to what I need:

@Override
protected void dispatchDraw(Canvas into) {
    int w = into.getWidth();
    int h = into.getHeight();
    int w3 = w / 3;
    int h3 = h / 3;
    into.drawLine(0, 0, w, h, mPaint);
    Region protect = new Region(w / 3, h / 3, 2 * w / 3, 2 * h / 3);
    into.clipRegion(protect, Op.INTERSECT);
    into.drawColor(Color.MAGENTA); // or super.dispatchDraw() here.
}

That gives me this:

enter image description here

Which is sort of the opposite of what I want. Notice the Region named "protect" in the code above. I want the magenta fill to happen everywhere except in that region. Specifically, what I want to see is this:

enter image description here

In the analogy of the window, I should then be in a position to remove the restriction and draw a person or something in the normal way that overlaps both the window and the wall.

EDIT: Here is a simplified working version of the answer by Rajesh CP. I also added a red "foreground" stripe drawn over everything at the end to show that I can remove the restriction as well as add them. Thanks Rajesh!

public class MaskTest extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(new ClippView(getApplicationContext()));
    }

    private class ClippView extends View {
        private Paint line_paint = new Paint();
        private Paint strip_paint = new Paint();

        public ClippView(Context context) {
            super(context);
            line_paint.setColor(Color.GRAY);
            line_paint.setStrokeWidth(20);
            strip_paint.setColor(Color.RED);
        }

        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            int w = canvas.getWidth();
            int h = canvas.getHeight();
            int w3 = w / 3;
            int h3 = h / 3;
            // This line represents some unknown background that we are drawing over.
            canvas.drawLine(0, 0, w, h, line_paint);
            // 'protect' represents some area to not paint over until desired.
            Region protect = new Region(w3, h3, 2 * w / 3, 2 * h / 3);
            canvas.clipRegion(protect, Op.DIFFERENCE);
            // Paint magenta over the entire canvas, avoiding the protected region.
            canvas.drawColor(Color.MAGENTA);
            // Remove the protected region.
            Region all = new Region(0, 0, w, h);
            canvas.clipRegion(all, Op.UNION);
            // Draw a wide foreground strip over the entire canvas.
            canvas.drawRect(w / 2 - w / 20, 0, w / 2 + w / 20, h, strip_paint);
        }
    }
}
Melinda Green
  • 2,100
  • 1
  • 21
  • 32
  • Posting your code would be much appreciated – Triode Apr 04 '13 at 06:16
  • 1
    @Rajesh CP, there is no code to share at this point. I have been fooling with Canvas.clipPath(Path, Op) method which seems like the right idea but I've not found the magic pattern to specify a rectangle or other shape to protect a region of the canvas from painting operations until I want to paint into/over it. – Melinda Green Apr 05 '13 at 07:52

2 Answers2

6
public class ClippView extends View{
    private Paint paint= new Paint();

    public ClippView(Context context) {
        super(context);
    }


    private Region protect;
    /*
     * (non-Javadoc)
     * @see android.view.View#onDraw(android.graphics.Canvas)
     * @since Apr 12, 2013
     * @author rajeshcp
     */
    @Override
    protected void onDraw(Canvas canvas) { 
        super.onDraw(canvas);
        // view.buildDrawingCache();      


        int w = canvas.getWidth();
        int h = canvas.getHeight();
        int w3 = w / 3;
        int h3 = h / 3;
        canvas.drawLine(0, 0, w, h, paint);
        protect = (protect == null) ? new Region(w3, h3, 2 * w / 3, 2 * h / 3) : protect;
        canvas.clipRegion(protect, Op.DIFFERENCE);
        canvas.drawColor(Color.MAGENTA);


    }

}

Do this I think this is what you want.enter image description here

Triode
  • 11,309
  • 2
  • 38
  • 48
  • Those links get me as far as I was able to otherwise but is still missing the key I am looking for. None of the logical "Op" values do what I want. Your links may well help me write simpler code using rectangles instead of arbitrary Path objects but the main problem remains. See the sample code and images that I added to my question. – Melinda Green Apr 06 '13 at 06:30
  • I think that my big mistake was in overriding dispatchDraw() instead of onDraw(). A quick test shows not what you give but something starting to look like what I hoped. I'll give it more attention tomorrow but I think you got me back on track. – Melinda Green Apr 12 '13 at 09:07
  • Thanks, Rajesh! I've added a simplified version of your code at the end of the problem description. – Melinda Green Apr 17 '13 at 00:06
-1

Why not draw four rectangles as the mask like this View Mask

SceLus
  • 1,117
  • 13
  • 18