Android Custom View doesn't handle transparency/alpha the right way

荒凉一梦 提交于 2019-12-10 03:21:21

问题


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:

Add transparency and get this image: UNDESIRED RESULT

They end up with this: DESIRED RESULT

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


回答1:


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:

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.




回答2:


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)



来源:https://stackoverflow.com/questions/20254558/android-custom-view-doesnt-handle-transparency-alpha-the-right-way

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!