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:
if the screen is small (for example on qvga screens), the shown images get cropped.
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:
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?
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 ) .
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
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:
- Constrain the size of the view in which the drawable is used to the size of the bitmap.
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);
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.