13

There is a nice post made by the popular Google developer Romain Guy that shows how to use a rounded corners drawable (called "StreamDrawable" in his code ) on a view efficiently.

The sample itself works very well on my Galaxy S3 when in portrait mode, but I have a few issues with it:

  1. if the screen is small (for example on qvga screens), the shown images get cropped.

  2. if I have an input bitmap that is too small than how I wish to show it, the output image has its edges smeared. Even on the Galaxy S3, when you run the sample code and it's on landscape, it looks awful:

    enter image description here

  3. I'm still not sure about it (since I use a workaround of scaling the image for using the sample code), but it think that even this solution is a bit slow when being used in a listView. Maybe there is a renderscript solution for this?

It doesn't matter if I use setImageDrawable or setBackgroundDrawable. It must be something in the drawable itself.

I've tried to play with the variables and the bitmapShader, but nothing worked. Sadly TileMode doesn't have a value for just stretching the image, only tiling it in some way.

As a workaround I can create a new scaled bitmap, but it's just a workaround. Surely there is a better way which will also not use more memory than it should.

How do I fix those issues and use this great code?

Community
  • 1
  • 1
android developer
  • 114,585
  • 152
  • 739
  • 1,270
  • you should post a comment on that article or ping him to answer here. – Paresh Mayani Jan 01 '13 at 09:09
  • have you solved the problem? – tasomaniac Jan 15 '13 at 12:08
  • for now , no . but you can have a workaround of scaling the bitmap . not the best thing , but if you are short on time , you can use it . – android developer Jan 15 '13 at 16:47
  • Since at some point the bitmap needs to be drawn at a different size than whatever you're starting with, which is resizing, I don't think I'd consider resizing it by hand to be a work-around / hack. It's just low-level. Either you do it, or a library does it. – Groxx Feb 07 '13 at 18:15
  • This is incorrect since it uses more memory than you need. When you create a new ,scaled bitmap , you get to have 2 bitmaps at the same time . Instead , if you already have the bitmap , you would simply draw what you need by sampling only what you need . Since the drawable already does some special drawing , why not having this one too ? It might even be possible to use the GPU for this task . Another thing is that if you have the image files , you won't be able to use WRAP_CONTENT since you want to set the size by yourself. – android developer Feb 07 '13 at 20:31
  • See my implementation here: [RoundedImageView](https://github.com/makeramen/RoundedImageView) where I address many of these issues – vinc3m1 Feb 22 '13 at 20:36
  • @makeramen this website contain a huge memory leak problem, plus it doesn't support setting an image resource id. – android developer Mar 02 '13 at 09:57
  • fwiw I solved a similar problem by masking over the original bitmap ([Android XML rounded clipped corners](http://stackoverflow.com/a/8313096/383414)). – Richard Le Mesurier Jul 19 '13 at 11:09
  • @RichardLeMesurier this isn't always the best solution, as the surrounding background might be different . it's a trick in case they are the same... good trick, but still... – android developer Jul 19 '13 at 13:22
  • Agreed. It was a quick and dirty hack for a client that said "make it look like the iPhone app..." My favorite request, right. Just included it as a comment cos this site is getting too fragmented these days IMHO. – Richard Le Mesurier Jul 20 '13 at 20:29
  • yes, i'm also quite tired of this "make it look like the iPhone app" request. i keep telling people that it's not an iphone, and there are already guidelines for android, but nobody listens... – android developer Jul 20 '13 at 20:53

3 Answers3

9

I think that the solution that is presented on this website works well.

unlike other solutions, it doesn't cause memory leaks, even though it is based on Romain Guy's solution.

EDIT: now on the support library, you can also use RoundedBitmapDrawable (using RoundedBitmapDrawableFactory ) .

android developer
  • 114,585
  • 152
  • 739
  • 1,270
  • Can you point out the idea behind it (or somewhere I can find it)? I really don't have time to read that whole project – suitianshi Aug 21 '14 at 08:34
  • Yes, the idea is very much based on what Romain Guy has presented on his website: http://www.curious-creature.org/2012/12/11/android-recipe-1-image-with-rounded-corners/ . – android developer Aug 21 '14 at 10:41
4

I had some size issues with this code, and I solved it.

Maybe this will help you, too:

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

2) override two more methods:

@Override
    public int getIntrinsicWidth() {
    return bmp.getWidth();
}

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

Best regards, DaRolla

DaRolla
  • 534
  • 1
  • 8
  • 8
1

There underlying problem is that the BitmapShader's TileMode doesn't have a scaling option. You'll note in the source that it's been set to Shader.TileMode.CLAMP, and the docs describe that as:

replicate the edge color if the shader draws outside of its original bounds

To work around this, there are three solutions:

  1. Constrain the size of the view in which the drawable is used to the size of the bitmap.
  2. Constrain the drawing region; for instance, change:

    int width = bounds.width() - mMargin;
    int height = bounds.height() - mMargin;
    mRect.set(mMargin, mMargin, width, height);
    

    To:

    int width = Math.min(mBitmap.getWidth(), bounds.width()) - mMargin;
    int height = Math.min(mBitmap.getHeight(), bounds.height()) - mMargin;
    mRect.set(mMargin, mMargin, width, height);
    
  3. Scale the bitmap to the size of the drawable. I've moved creating the shader into onBoundsChange() and have opted to create a new bitmap from here:

    bitmap = Bitmap.createScaledBitmap(mBitmap, width, height, true);
    mBitmapShader = new BitmapShader(bitmap,
            Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
    

    Note that this a potentially slow operation and will be running on the main thread. You might want to carefully consider how you want to implement it before you go for this last solution.

Paul Lammertsma
  • 37,593
  • 16
  • 136
  • 187
  • what are the advantages and disadvantes of each of those methods? the third one is obviously both slow and use more memory , but what about the others? which one do you recommend? – android developer Jul 19 '13 at 13:24
  • If you want the bitmap to stretch to the size of the view, some implementation similar to the last is pretty much the only way to go while using a `BitmapShader`. The first will restrict the size of the view itself, while the second will only paint the shader in the top left corner of the view. – Paul Lammertsma Jul 19 '13 at 14:25
  • well, what if i want it to behave like a normal imageView, only just that it will make the image itself to have rounded corners? – android developer Jul 19 '13 at 20:00