9

I'm drawing a custom view. In this view I use two different paint and path objects to paint to the canvas. I'm basically drawing two shapes that overlap. After I add alpha, the part of the view that is overlapped is darker than the rest of the image. This is undesired, but I'm not sure how to fix it.

This is a clipping of my code to show how I'm using alpha in my NewButtonView.java

Paint paint = new Paint();
int color = 0x33ffffff;
int borderColor = 0xFF000000;

paint.setColor(color);
paint.setAntiAlias(true);
paint.setStrokeWidth(strokeWidth);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStrokeCap(Paint.Cap.ROUND);
paint.setStyle(Paint.Style.FILL);

About 31 minutes into this Google I/O video... they show my desired effect.

They basically show this image: enter image description here

Add transparency and get this image: UNDESIRED RESULT

enter image description here

They end up with this: DESIRED RESULT

enter image description here

Does anyone have any idea on how to get this desired affect?

Italo Borssatto
  • 15,044
  • 7
  • 62
  • 88
EGHDK
  • 17,818
  • 45
  • 129
  • 204
  • 3
    are you sure it is the alpha that you want to change? seems like your desired effect has zero transparency still, it is just lightened. – Scott Nov 27 '13 at 22:53
  • Yes, I need transparency because I am using a textured background that has to be seen. – EGHDK Nov 27 '13 at 23:09
  • 1
    My best thought then is to see if there is a way to draw the overlapping shapes as one shape (or combine them as one after they are drawn). Adding alpha to the composite shape should result they way you want – Scott Nov 27 '13 at 23:12
  • Yeah, its just that I'm drawing two circles, overlapping each other, and drawing that type of "binocular shape" seems unnecessary when I can just draw two circles. – EGHDK Nov 27 '13 at 23:18
  • In the video, they mentioned `Canvas.saveLayerAlpha()` method. Did you try to use it? – Italo Borssatto Dec 06 '13 at 17:06
  • 1
    I might be barking up the wrong tree, but shouldn't you be setting the alpha on the container rather than the buttons? – Garett Dec 09 '13 at 21:05
  • @EGHDK Just saw another one of your questions: [Drawing two shapes with transparency in Android/Java](http://stackoverflow.com/questions/20009242/drawing-two-shapes-with-transparency-in-android-java). If you had explained your actual problem in detail, or linked to this question in your post, I could have given you a better solution. In any case, good luck. – Vikram Dec 10 '13 at 23:03

2 Answers2

15

As mentioned in the video, you would use Canvas#saveLayerAlpha(....) for this. You can also get a similar effect without using it. I'll discuss that later on.

Let's create a sample view:

public class SampleView extends View {

    // Declare Paint objects
    Paint paintColor, paintBorder;

    public SampleView(Context context) {
        super(context);

        // Initialize and set up Paint objects
        paintColor = new Paint();
        paintBorder = new Paint();

        paintColor.setAntiAlias(true);
        paintBorder.setAntiAlias(true);

        paintBorder.setColor(Color.BLACK);
        paintBorder.setStyle(Style.STROKE);
        paintBorder.setStrokeWidth(10);

        // Just a random image to 'see' the difference
        setBackground(getResources().getDrawable(R.drawable.hor_lines));
    }

    @Override 
    protected void onDraw(Canvas canvas) {

        // Save layer alpha for Rect that covers the view : alpha is 90 / 255
        canvas.saveLayerAlpha(0, 0, getWidth(), getHeight(), 90, 
                                             Canvas.HAS_ALPHA_LAYER_SAVE_FLAG);

        // Draw first circle, and then the border
        paintColor.setColor(Color.RED);
        canvas.drawCircle(getWidth() / 3, getHeight() / 2, 
                                           getWidth() / 4 - 20, paintColor);

        canvas.drawCircle(getWidth() / 3, getHeight() / 2, 
                                           getWidth() / 4 - 15, paintBorder);

        // Draw second circle, and then the border
        paintColor.setColor(Color.BLUE);
        canvas.drawCircle(2 * getWidth() / 3, getHeight() / 2, 
                                           getWidth() / 4 - 20, paintColor);
        canvas.drawCircle(2 * getWidth() / 3, getHeight() / 2, 
                                           getWidth() / 4 - 15, paintBorder);

        // Finally, restore the canvas
        canvas.restore();
    }
}

What happens:

  1. An off-screen bitmap is allocated when saveLayerAlpha(....) is called.

  2. All drawing operations happen on this bitmap.

  3. When canvas.restore() is called, this bitmap is transferred to the on-screen canvas, and the alpha value we supplied in saveLayerAlpha(....) is applied to the off-screen bitmap.

(I think) The following is an equivalent way of creating this effect without using saveLayerAlpha(....):

public class SView extends View {

    Paint paintColor, paintBorder, paintAlpha;

    Bitmap toDrawOn;

    public SView(Context context) {
        super(context);

        paintAlpha = new Paint();

        paintAlpha.setColor(Color.parseColor("#90FFFFFF"));
        paintAlpha.setAntiAlias(true);

        ....
        ....

    }

    @Override
    protected void onDraw(Canvas canvas) {

        if (toDrawOn == null) {

            // Create a new Bitmap
            toDrawOn = Bitmap.createBitmap(getWidth(), getHeight(), 
                                                    Config.ARGB_8888);

            // Create a new Canvas; drawing operations 
            // will happen on 'toDrawOn'
            Canvas offScreen = new Canvas(toDrawOn);

            // First circle
            paintColor.setColor(Color.RED);
            offScreenCanvas.drawCircle(getWidth() / 3, getHeight() / 2, 
                                           getWidth() / 4 - 20, paintColor);
            offScreenCanvas.drawCircle(getWidth() / 3, getHeight() / 2, 
                                           getWidth() / 4 - 15, paintBorder);

            // Second circle
            paintColor.setColor(Color.BLUE);
            offScreenCanvas.drawCircle(2 * getWidth() / 3, getHeight() / 2, 
                                           getWidth() / 4 - 20, paintColor);
            offScreenCanvas.drawCircle(2 * getWidth() / 3, getHeight() / 2, 
                                           getWidth() / 4 - 15, paintBorder);

            // Draw bitmap 'toDrawOn' to canvas using 'paintAlpha'
            canvas.drawBitmap(toDrawOn, 0, 0, paintAlpha);

        } else {

            // 'toDrawOn' is not null; draw it
            canvas.drawBitmap(toDrawOn, 0, 0, paintAlpha);
        }
    }
}

Output:

enter image description here

Just for reference, the base container in the image above is a LinearLayout with background set to this jpeg: Link.

And, the drawable used as the background of SampleView:

// Just a random image to 'see' the difference
setBackground(getResources().getDrawable(R.drawable.hor_lines));

is taken from: here.

Vikram
  • 51,313
  • 11
  • 93
  • 122
0

You could draw everything in a bitmap with full alpha, and then change the bitmap's alpha

(Sorry, this is more a comment than an answer but stack overflow won't allow me to post comments)

Basile Perrenoud
  • 4,039
  • 3
  • 29
  • 52