Ricardo
Ricardo

Reputation: 708

Custom View: build outline with rounded corners for its shadow

I want to be able to add a shadow to my custom view when I set the elevation parameter in the layout xml. I'm able to add the shadow but it's being applied as a square rather than a rounded corner shape. I don't know where to add the code to make it rounded. Where should it be and how?

This is how it looks right now:

enter image description here

This is the custom View:

    public class RoundedFrameLayout extends FrameLayout {
    private final static float CORNER_RADIUS = 15.0f;

    Bitmap maskBitmap;
    private Paint paint, maskPaint;
    private float cornerRadius;

    DisplayMetrics metrics;

    public RoundedFrameLayout(Context context) {
        super(context);
        init(context, null, 0);
    }

    public RoundedFrameLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs, 0);
    }

    public RoundedFrameLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(context, attrs, defStyle);
    }

    private void init(Context context, AttributeSet attrs, int defStyle) {

        metrics = context.getResources().getDisplayMetrics();
        cornerRadius = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, CORNER_RADIUS, metrics);

        paint = new Paint(Paint.ANTI_ALIAS_FLAG);

        maskPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
        maskPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));

        setWillNotDraw(false);
    }

    @Override
    public void draw(Canvas canvas) {

        cornerRadius = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, (float) canvas.getWidth() * 0.05f, metrics);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {

            setOutlineProvider(new CustomOutline(canvas.getWidth(), canvas.getHeight()));
        }

        Bitmap offscreenBitmap = Bitmap.createBitmap(canvas.getWidth(), canvas.getHeight(), Bitmap.Config.ARGB_8888);
        Canvas offscreenCanvas = new Canvas(offscreenBitmap);

        super.draw(offscreenCanvas);

        maskBitmap = createMask(canvas.getWidth(), canvas.getHeight());

        offscreenCanvas.drawBitmap(maskBitmap, 0f, 0f, maskPaint);

        canvas.drawBitmap(offscreenBitmap, 0f, 0f, paint);
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    private class CustomOutline extends ViewOutlineProvider {

        int width;
        int height;

        CustomOutline(int width, int height) {

            this.width = width;
            this.height = height;
        }

        @Override
        public void getOutline(View view, Outline outline) {

            outline.setRect(0, 0, width, height);
        }
    }

    private Bitmap createMask(int width, int height) {

        Bitmap mask = Bitmap.createBitmap(width, height, Bitmap.Config.ALPHA_8);
        Canvas canvas = new Canvas(mask);

        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setColor(Color.WHITE);

        canvas.drawRect(0, 0, width, height, paint);

        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
        canvas.drawRoundRect(new RectF(0, 0, width, height), cornerRadius, cornerRadius, paint);

        return mask;
    }
}

Upvotes: 3

Views: 2075

Answers (1)

Ricardo
Ricardo

Reputation: 708

I just noticed that I just needed to call a different method in the ViewOutlineProvider custom class:

    @Override
    public void getOutline(View view, Outline outline) {

        outline.setRoundRect(0, 0, width, height, radius);
    }

Now it's working fine.

Upvotes: 3

Related Questions