Using a rounded corners drawable

2019-01-30 20:49发布

问题:

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:

  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?

回答1:

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 ) .



回答2:

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



回答3:

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.