View with round corners not smooth

3.5k Views Asked by At

Have a look at my code below.

ShapeDrawable shapeDrawable = new ShapeDrawable(new RectShape());
    shapeDrawable.getPaint().setColor(Color.parseColor("#5a2705"));
    shapeDrawable.getPaint().setStyle(Style.STROKE);
    shapeDrawable.getPaint().setAntiAlias(true);
    shapeDrawable.getPaint().setStrokeWidth(2);
    shapeDrawable.getPaint().setPathEffect(new CornerPathEffect(10));

I am applying this as background to my LinearLayout, but the edges are not smooth. How can I fix this?

Here is the screenshot of how it looks.

enter image description here

7

There are 7 best solutions below

0
On

You can try creating a separate xml file with a layout of the rounded rectangle. Such as:

<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <solid android:color="color_here"/>
    <stroke android:width="5dp" android:color="color_here"/>
    <corners android:radius="2dp"/>
</shape>

You can tune this to your liking and use this XML file as a background in your main XML.

You can also try using 9Patch which should already come with your SDK

0
On

You may try using selector like this . This solution is the best you just need to set this selector to the background of the layout.

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <solid android:color="@color/white" />
    <stroke android:width="1dp" android:color="@color/grey" />
    <padding android:left="10dp" android:top="10dp" android:right="10dp" android:bottom="10dp" />
    <corners android:radius="15dp" />
         <gradient
          android:angle="90"
          android:startColor="@color/white"
          android:centerColor="@color/grey"
          android:endColor="@color/white"
          android:type="linear" />
</shape>

Choose color of your own. Cheers!

1
On

To cater such background requirement I prefer using 9-patch drawable. Below are the pointer to get going with creating and using 9-patch drawable resources:

  1. Developer Doc: http://developer.android.com/tools/help/draw9patch.html

  2. Another explanation: http://radleymarx.com/blog/simple-guide-to-9-patch/

  3. Online tool to create 9-patch images. (Note: Modify the file extension to *.9.png while saving the image)

2
On

Using a programmatically-created shape drawable as a View background results in the outer half of your stroke width getting cropped off (for reasons I don't know). Look closely at your image and you'll see that your stroke is only 1 pixel wide, even though you requested 2. That is why the corners look ugly. This effect will be much more apparent if you try a bigger stroke and radius such as 10 and 40, respectively.

Either use an XML drawable, which doesn't seem to have this problem, like in Harshit Jain's answer, or do the following if you must (or prefer to) use a programmatic solution.

Solution: Use a layer list to inset the rectangle by the amount that is being clipped (half the stroke width), like this:

float strokeWidth = 2;

ShapeDrawable shapeDrawable = new ShapeDrawable(new RectShape());
shapeDrawable.getPaint().setColor(Color.parseColor("#5a2705"));
shapeDrawable.getPaint().setStyle(Style.STROKE);
shapeDrawable.getPaint().setAntiAlias(true);
shapeDrawable.getPaint().setStrokeWidth(strokeWidth);
shapeDrawable.getPaint().setPathEffect(new CornerPathEffect(10));

Drawable[] layers = {shapeDrawable};
LayerDrawable layerDrawable = new LayerDrawable(layers);

int halfStrokeWidth = (int)(strokeWidth/2);
layerDrawable.setLayerInset(0, halfStrokeWidth, halfStrokeWidth, halfStrokeWidth, halfStrokeWidth);

Then use the layerDrawable as your background. Here is a screen shot of the result of the above code:

enter image description here

0
On

I have solved this issue in a very simple way,

I was looking for a code which can be done programmatically and not via xml,

Here is the code:

GradientDrawable shape = new GradientDrawable();
    shape.setCornerRadius(10);
    shape.setColor(Color.WHITE);
    shape.setStroke(2, Color.parseColor("#996633"));

You can set this like this : view.setBackgroundDrawable(shape);

The answer posted by @Tenfour04 might also solve the issue but i havent tried as such.

Hope it helps someone.

1
On

You might want to consider checking this out.

Rounded Image Views by Vince

0
On

I've encountered this problem recently and came up with a different solution. IMHO the best solution is to create your own Shape implementation and use it to create a ShapeDrawable.

Below is a simple implementation of rounded rectangle, that will allow you to inset it's border.

class InsetRoundRectShape(
    private var radiusArray: FloatArray = floatArrayOf(0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f),
    private var inset: RectF = RectF()
): RectShape() {

    private var innerRect: RectF = RectF()
    private var path: Path = Path()

    constructor(radius: Float, inset: RectF): this(floatArrayOf(radius, radius, radius, radius, radius, radius, radius, radius), inset)
    constructor(radius: Float, inset: Float): this(radius, RectF(inset, inset, inset, inset))

    init {
        if (radiusArray.size < 8) {
            throw ArrayIndexOutOfBoundsException("radius array must have >= 8 values")
        }
    }

    override fun draw(canvas: Canvas, paint: Paint) {
        canvas.drawPath(path, paint)
    }

    override fun getOutline(outline: Outline) {
        super.getOutline(outline)

        val radius = radiusArray[0]
        if(radiusArray.any { it != radius }) {
            outline.setConvexPath(path)
            return
        }

        val r = rect()
        outline.setRoundRect(ceil(r.left).toInt(), ceil(r.top).toInt(), floor(r.right).toInt(), floor(r.bottom).toInt(), radius)
    }

    override fun onResize(w: Float, h: Float) {
        super.onResize(w, h)
        val r = rect()
        path.reset()
        innerRect.set(r.left + inset.left, r.top + inset.top, r.right - inset.right, r.bottom - inset.bottom)
        if(innerRect.width() <= w && innerRect.height() <= h) {
            path.addRoundRect(innerRect, radiusArray, Path.Direction.CCW)
        }
    }

    override fun clone(): InsetRoundRectShape {
        val shape = super.clone() as InsetRoundRectShape
        shape.radiusArray = radiusArray.clone()
        shape.inset = RectF(inset)
        shape.path = Path(path)
        return shape
    }
}

Create ShapeDrawable like this

//Inset has to be half of strokeWidth
ShapeDrawable(InsetRoundRectShape(10f, 4f)).apply {
    this.paint.color = Color.BLUE
    this.paint.style = Paint.Style.STROKE
    this.paint.strokeWidth = 8.dp
    this.invalidateSelf()
}