Android Recipe #1, image with rounded corners

I would like to try and start a new series of articles focused on giving you small recipes you can use in your applications to achieve very specific visual effects. For this first installment, I will show you how to draw a bitmap with rounded corners. Many people have asked me how to achieve this effect and I often see developers use a much more complicated solution than is necessary.

I wrote a simple application to illustrate this effect. You can download an APK for your Android device and download the source code. This is what the application looks like:

roundrect-950x1585

To generate the rounded images I simply wrote a custom Drawable that draws a rounded rectangle using Canvas.drawRoundRect(). The trick is to use a Paint with a BitmapShader to fill the rounded rectangle with a texture instead of a simple color. Here is what the code looks like:

BitmapShader shader;
shader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);

Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setShader(shader);

RectF rect = new RectF(0.0f, 0.0f, width, height);

// rect contains the bounds of the shape
// radius is the radius in pixels of the rounded corners
// paint contains the shader that will texture the shape
canvas.drawRoundRect(rect, radius, radius, paint);

The sample application goes a little further and fakes a vignette effect by combining the BitmapShader with a RadialGradient.

50 Responses to “Android Recipe #1, image with rounded corners”

  1. Stan says:

    You can do the same with a layout but you need to disable the hardware acceleration for this view (a bug ?) :

    public class LinearLayoutCorner extends LinearLayout {
    /** Used locally to tag Logs */
    @SuppressWarnings(“unused”)
    private static final String TAG = LinearLayoutCorner.class.getSimpleName();
    private final Path mPath = new Path();

    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    public LinearLayoutCorner(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    init();
    }

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

    public LinearLayoutCorner(Context context) {
    super(context);
    init();
    }

    @SuppressLint(“NewApi”)
    private void init() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
    // we have to remove the hardware acceleration if we want the clip
    setLayerType(View.LAYER_TYPE_SOFTWARE, null);
    }
    setBackgroundResource(R.drawable.bg_classement);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);
    mPath.reset();
    float round = getResources().getDimension(R.dimen.home_page_corner_angle);
    mPath.addRoundRect(new RectF(getPaddingLeft(), getPaddingTop(), w – getPaddingRight(), h – getPaddingBottom()), round, round, Direction.CW);
    }

    @Override
    protected void dispatchDraw(Canvas canvas) {
    canvas.clipPath(mPath);
    super.dispatchDraw(canvas);
    }
    }

  2. Romain Guy says:

    This solution is more expensive because it relies on path clipping; drawing a rounded rectangles directly is going to be more efficient. It also doesn’t provide nice results because path clipping does not support antialiasing and you get jagged edges. Note that the hardware accelerated renderer does not support path clipping at the moment but when it does, it will also be more expensive than just drawing the rounded rect.

  3. Stan says:

    But it’s the only solution to make a round corner on listview for clipping the textview too, right ?
    If I want something like that for example : http://i.stack.imgur.com/jq4ez.jpg (The first “I” of “Item 1″ must be clipped when I’m scrolling)

  4. Romain Guy says:

    A much cheaper way is to draw a 9patch on top of the ListView.

  5. Stan says:

    Something like that : http://i.stack.imgur.com/TyMNG.png ? Unfortunately the result is not the same …

  6. Romain Guy says:

    No, a 9patch you draw *over* the ListView. That 9patch contains the outer area of the rounded rect, not the rounded rect itself.

  7. Renaud FAVIER says:

    Can’t set a StreamDrawable as a view background before 4.1, any workaround beside path clipping ?

  8. Simon says:

    Hi, nice tip.
    But the .apk you provide us won’t install on a real device (package error)

  9. Fritte7 says:

    Hello, i have an question. Why don’t use xml to define corners and use them on background “drawable” ?
    In list adapter, use an imageview with background’s corner … ?

  10. David Caunt says:

    Simon – It’s working just fine on my device.

    Nice photos, as always, Romain.

  11. Marco says:

    How do I display the images in the correct proportions? I see in your code that you’re using LayoutParams, but it doesn’t seem to work.

  12. Zak says:

    Great stuff, would it be possible with a 9patch for the mask instead?

  13. Daniel Cachapa says:

    Thanks for the post. This will be useful.
    I noticed though that scrolling on the apk you provided isn’t very smooth. Any ideas on why that is and how to improve on it?

    [Tried to post this on Google+ but couldn’t and for some reason you’re not on my circles anymore and can’t re-add]

  14. Romain Guy says:

    @Simon: my APK is built for API level 17. Build from source if you don’t have such a device.

    @Fritte7: No, it won’t work. The image won’t be clipped by a rounded rect, there will just be a rounded rect behind it.

    @Marco: The code sample I provide calculates the aspect ratio.

    @Daniel: The sample uses large images but that shouldn’t matter for the GPU. Are you running the sample in software?

  15. Daniel Cachapa says:

    As far as I’m aware, it should be running with HW acceleration.
    I’m running stock Android and have not selected any of the developer options save for activating the adb.

    To be clear, the scrolling isn’t exactly jerky, but it’s clearly struggling and not as smooth as, say, scrolling the contact list in the People app.

  16. Could we use PorterDuffXfermode to create a Path of rounded-rectangle and use a DST_IN to paint the Path? What would be the performance difference between this approach and using a BitmapShader to draw a rounded-rectangle?

  17. Romain Guy says:

    You can but it takes two drawing passes instead of one. It also doesn’t work on opaque backgrounds.

  18. Tim says:

    Nice. Can I request a drop shadow as recipe 2?

  19. Kyle says:

    Re: Tim- I’d like to see that too.

  20. Romain Guy says:

    @Tim: I’ll keep that in mind but these articles take a lot of the little spare time I have so I’m more likely to write about stuff I want :) Note that I try to choose topics that I know matter to a lot of developers.

  21. George.M says:

    Really appreciate this new series

  22. Sakthi says:

    view.setBackground method throws an error .. How do i make this work in a pre Jelly Bean device .. ?

  23. Sakthi says:

    I used setbackgrounddrawable and it worked .. Thanks for this great recipe .. ;)

  24. Android developer says:

    Romain Guy , I’ve posted some issues I’ve found on your sample here:
    http://stackoverflow.com/questions/14109187/android-using-a-rounded-corners-drawable#comment19523400_14109187

    Please spare some time reading them …

  25. Dear Romain Guy,

    thanks a lot for this tutorial and for your source code.

    I tried to use your Drawable for an ImageView, but this does only work if the ImageView has a fixed layout_width and layout_height. Using wrap_content results in showing nothing at all.

    Do you know a way to fix this? I think the sizes of your drawable are clear (bitmap width and height minus margin).

    Best regards,
    Marco Schmitz

  26. Well, I solved this problem :)

    1) in the constructor store the bitmap in a local variable (e.g. private Bitmap bmp;)

    2) override 2 more methods:
    @Override
    public int getIntrinsicWidth() {
    return bmp.getWidth();
    }

    @Override
    public int getIntrinsicHeight() {
    return bmp.getHeight();
    }

    Best regards,
    Marco

    PS: I took a closer look at ImageView sources. There was something with intrisic width an height according to drawables…

  27. Chris Jenkins says:

    I am struggling to center align the image, im using:

    Matrix m = new Matrix();
    mBitmapShader.getLocalMatrix(m);
    m.setTranslate(-bitmap.getWidth()/2, -bitmap.getHeight()/2);
    mBitmapShader.setLocalMatrix(m);
    mPaint.setShader(mBitmapShader);

    But it doesnt center the bit map.. any better ways of doing this?

  28. James Wald says:

    How could this be adapted to work with a TransitionDrawable? I’m using a “fade-in” image cache. The final image is not always rounded. I need all of the bitmaps in the drawable to be rounded.

  29. Ben Coulston says:

    How’s the memory efficiency of an implementation? I know in the past I’ve strayed away from drawing a rounded bitmap because it would essentially keep 2 bitmaps in memory for every bitmap I was trying to display.

    From quickly glancing over your implementation, I see a drawable and bitmap that would be in memory for each imageview. Seems inefficient, especially if you were applying this to a large gridview of bitmaps.

    What are your thoughts on this?

  30. Romain Guy says:

    Ben, this implementation does not use 2 bitmaps in memory. That’s the whole point. The drawable simply draws a rounded rectangle and uses only a few bytes of memory. The only memory usage in this solution is that of the bitmap which you need anyway.

  31. Ben Coulston says:

    Exactly! This solution didn’t use the two bitmaps. Solutions I’ve run across in the past did! That’s why I was curious on the amount of memory usage added by using this drawable.

    I’m going to do some performance testing on applying this at runtime to a large amount of images in a Grid View.

    Thanks again.

  32. Jason says:

    Cool, but this drawable will lose ability to scale if set to imageview as imageDrawable. Is there any way to keep the “ScaleType” in imageview? Thanks.

  33. Romain Guy says:

    Jason, the ScaleType of an ImageView will work with any Drawable.

  34. Jason says:

    But after modification form your sample application (add ImageView in stream_item layout, set StreamDrawable to ImageView instead of RelativeLayout’s background). I can’t get any of the scale type work. Just wonder that is there more works to do to support ScaleType? Thanks.

  35. Jason says:

    Is that because we are not drawing the whole bitmap on canvas? We only draw “region” of bitmap to canvas through the paint object. Which means we have to scale the bitmap by ourself in the customized drawable. It goes much more complex than I thought

  36. Vince says:

    @Chris:

    For centering:

    mBitmapRect.set(0, 0, bitmap.getWidth(), bitmap.getHeight());

    mShaderMatrix.setRectToRect(mBitmapRect, mRect, Matrix.ScaleToFit.FILL);
    mBitmapShader.setLocalMatrix(mShaderMatrix);

  37. Vince says:

    What is the license on this code? I’d like to make some modifications and open source a library if you’re open to it.

  38. Vince says:

    Hi all,

    I just extended this to a RoundedImageView (as opposed to drawable) that has full support for different ScaleTypes, in addition to rudimentary support for TransitionDrawables:
    https://github.com/makeramen/RoundedImageView

  39. m3n0R says:

    @Vince nice nice nice… I just tryied to do the same. The point is i’m using AQuery + my RoundedImageView, so inside I can scale the bitmap, and after that create the corners… this is using so much memory so… I’m gonna take a look to your solution! Thx!

  40. Dani says:

    Thanks a lot.
    This worked perfectly for me. Just that the image was not scaled within the imageview – may be because an image-matrix was set to ImageView with a dynamic scale. Scale was calculated using intrinsic-width and intrinsic-height. So, just used this – http://stackoverflow.com/questions/14109187/using-a-rounded-corners-drawable#comment19523400_14109187 – and it worked.
    :)

  41. Jayshil Dave says:

    Romain, thanks for the tutorial. I was trying what you mentioned regarding rounded corners for a list view. In a relative layout, added a imageView which was a square 9 patch and rounded corners with center part transparent. It worked well.

    However, since the list is long shouldnt it go right uptil the bottom of the list, instead of just the visible section. Can we achieve that without path clipping, is there a way for me to draw the image which would include the translation and scroll amount of a list view?

  42. Tielei says:

    If wrap_content is used in layout, the getIntrinsicWidth and getIntrinsicHeight must be override for StreamDrawable.
    @Marco Schmitz