Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Oval shape clipped when created programmatically

Tags:

android

I've got a clipping problem.

First, I tried to display an oval shape with Xml only. I had the following code:

res/drawable/circle.xml

<?xml version="1.0" encoding="utf-8"?>
<shape
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="oval" >
    <size
        android:width="240dp"
        android:height="240dp" />
    <solid
        android:color="#FFFFFF" />
    <stroke
        android:width="2dp"
        android:color="#EEEEEE" />
</shape>    

*res/layout/test.xml

....
<RelativeLayout
    android:id="@+id/circle_layout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_weight="1" >

    <ImageView
        android:src="@drawable/circle"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true" />

</RelativeLayout>
....

It works perfectly, and gave me this: Screenshot

The problem is, for various reason, I must do the same progammatically.

I've got this code:

RelativeLayout layout = (RelativeLayout) findViewById(R.id.circle_layout);

ShapeDrawable drawable = new ShapeDrawable(new OvalShape());
drawable.getPaint().setColor(Color.parseColor("#EEEEEE"));
drawable.getPaint().setStyle(Style.STROKE);
drawable.getPaint().setStrokeWidth(dpToPx(2));
drawable.getPaint().setAntiAlias(true);
drawable.setIntrinsicHeight(dpToPx(240));
drawable.setIntrinsicWidth(dpToPx(240));

ImageView iv = new ImageView(this);
iv.setImageDrawable(drawable);

RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.WRAP_CONTENT,
RelativeLayout.LayoutParams.WRAP_CONTENT);
lp.addRule(RelativeLayout.CENTER_IN_PARENT);
iv.setLayoutParams(lp);

layout.addView(iv);

dpToPx function is:

private float scale = 0;

private float getScale() {
    if (scale == 0)
        scale  = this.getResources().getDisplayMetrics().densityDpi / 160f;
    return scale;
}

public float dpToPx(float dp) {
    return dp * getScale();
}

... this should gave me the same thing right? Well it gave a slightly larger circle, with top, right, bottom and left edges clipped. Here's a screenshot (same region of the screen than the previous one): Screehshot

Someone has an idea on what and why?

Thank you.

Edit:

if I set the stoke width to a higher value (12dp), I've got this: Screenshot

like image 277
Stéphane Avatar asked Sep 09 '13 07:09

Stéphane


1 Answers

I found the solution on this page: http://www.betaful.com/2012/01/programmatic-shapes-in-android/

In Android, when you draw with a stroke, it draws the center of the stroke at the boundaries of the shape you are drawing. As you can see, the stroke is getting cropped by the boundaries of the image. Luckily, you can perform transformations on a canvas. What we want to do is transform our stroke shape to be slightly smaller than the boundary – and there’s a Matrix operation for that!

matrix.setRectToRect(new RectF(0, 0, canvas.getClipBounds().right, canvas.getClipBounds().bottom),
new RectF(strokeWidth/2, strokeWidth/2, canvas.getClipBounds().right - strokeWidth/2,
    canvas.getClipBounds().bottom - strokeWidth/2),
Matrix.ScaleToFit.FILL);

Edit

... Better and easier solution find in the comment section of the link:

As you can see in the Android docs, a shape Drawable in resources is actually mapped to a GradientDrawable, not a ShapeDrawable:

And so, I've got the following code working perfectly:

GradientDrawable drawable = new GradientDrawable();
drawable.setColor(Color.TRANSPARENT);
drawable.setShape(GradientDrawable.OVAL);
drawable.setStroke((int)dpToPx(2), Color.parseColor("#EEEEEE"));
drawable.setSize((int)dpToPx(240), (int)dpToPx(240));
like image 144
Stéphane Avatar answered Sep 19 '22 17:09

Stéphane