Search code examples
androidandroid-custom-drawable

Custom Drawable border overlaps with App Bar Layout Header


This is how my custom drawable looks by default.

enter image description here

But when scrolled, it overlaps with the AppBarLayout.

enter image description here

The code for the Drawable goes like this:

@Override
public void draw(@NonNull Canvas canvas) {

    // get drawable dimensions
    Rect bounds = getBounds();

    float width = bounds.right - bounds.left;
    float height = bounds.bottom - bounds.top;
    float w2 = width / 2;
    float h2 = height / 2;
    float radius = Math.min(w2, h2) - mStrokeWidth / 2;

    mPath.reset();
    mPath.addCircle(width / 2, height / 2, radius, Path.Direction.CW);
    canvas.clipPath(mPath);

    // draw background gradient

    float barHeight = height / themeColors.length;
    mRectF.left = 0;
    mRectF.top = 0;
    mRectF.right = width;
    mRectF.bottom = height;
    for (int i = 0; i < themeColors.length; i++) {
        mPaint.setColor(themeColors[i]);

        canvas.drawRect(0, i * barHeight, width, (i + 1) * barHeight, mPaint);
    }

    mRectF.set(0, 0, width, height);
    canvas.clipRect(mRectF, Region.Op.REPLACE);

    if (mStrokeWidth != 0)
        canvas.drawCircle(width / 2, height / 2, width / 2 - mStrokeWidth / 2, mStrokePaint);

}

Support Library Version: 25.3.1, 26.1.0

What I've tried: - Different Region values for clipping path instead of REPLACE - Clipping path rectangle first and then clipping circle.

How do I fix this ?


Solution

  • I am posting my solution as answer.

    The reason it was getting overlapped was because the canvas was getting clipped twice without saving it.

    I removed this statement:

    canvas.clipRect(mRectF, Region.Op.REPLACE);
    

    and before clipping the canvas for the first time i saved its state using

    canvas.save();
    canvas.clipPath(mPath);
    

    and then when i was drawing the stroke, i need the original canvas so i restored it

    canvas.restore();
    if (mStrokeWidth != 0)
        canvas.drawCircle(width / 2, height / 2, width / 2 - mStrokeWidth / 2, mStrokePaint);
    

    This fixed the issue.

    Final Drawable Code:

    @Override
    public void draw(@NonNull Canvas canvas) {
    
        // get drawable dimensions
        Rect bounds = getBounds();
    
        float width = bounds.right - bounds.left;
        float height = bounds.bottom - bounds.top;
        float w2 = width / 2;
        float h2 = height / 2;
        float radius = Math.min(w2, h2) - mStrokeWidth / 2;
    
        mPath.reset();
        mPath.addCircle(width / 2, height / 2, radius, Path.Direction.CW);
        canvas.save();
        canvas.clipPath(mPath);
    
        // draw background gradient
    
        float barHeight = height / themeColors.length;
        mRectF.left = 0;
        mRectF.top = 0;
        mRectF.right = width;
        mRectF.bottom = height;
        for (int i = 0; i < themeColors.length; i++) {
            mPaint.setColor(themeColors[i]);
    
            canvas.drawRect(0, i * barHeight, width, (i + 1) * barHeight, mPaint);
        }
    
        mRectF.set(0, 0, width, height);
        //canvas.clipRect(mRectF, Region.Op.REPLACE);
        canvas.restore();
    
        if (mStrokeWidth != 0)
            canvas.drawCircle(width / 2, height / 2, width / 2 - mStrokeWidth / 2, mStrokePaint);
    
    }