How to create a partially-rounded-corners-rectangular drawable with center-crop and without creating new bitmap?

二次信任 提交于 2019-11-30 05:31:16

问题


Background

I've already seen how to create a drawable that's circular out of a bitmap, and also how to add an outline (AKA stroke) around it, here.

The problem

I can't find out how to do a similar task for rounding only some of the corners of the bitmap, inside the drawable, without creating a new bitmap, and still do it for a center-crop ImageView.

What I've found

This is what I've found, but it does create a new bitmap, and when using it in an imageView with center-crop (source here):

/**
 * Create rounded corner bitmap from original bitmap.
 *
 * @param input                               Original bitmap.
 * @param cornerRadius                        Corner radius in pixel.
 * @param squareTL,squareTR,squareBL,squareBR where to use square corners instead of rounded ones.
 */
public static Bitmap getRoundedCornerBitmap(final Bitmap input, final float cornerRadius, final int w, final int h,
                                            final boolean squareTL, final boolean squareTR, final boolean squareBL, final boolean squareBR) {
    final Bitmap output = Bitmap.createBitmap(w, h, Config.ARGB_8888);
    final Canvas canvas = new Canvas(output);
    final int color = 0xff424242;
    final Rect rect = new Rect(0, 0, w, h);
    final RectF rectF = new RectF(rect);
    // make sure that our rounded corner is scaled appropriately
    Paint paint = new Paint();
    paint.setXfermode(null);
    paint.setAntiAlias(true);
    canvas.drawARGB(0, 0, 0, 0);
    paint.setColor(color);
    canvas.drawRoundRect(rectF, cornerRadius, cornerRadius, paint);
    // draw rectangles over the corners we want to be square
    if (squareTL) 
        canvas.drawRect(0, 0, w / 2, h / 2, paint);
    if (squareTR) 
        canvas.drawRect(w / 2, 0, w, h / 2, paint);
    if (squareBL) 
        canvas.drawRect(0, h / 2, w / 2, h, paint);
    if (squareBR) 
        canvas.drawRect(w / 2, h / 2, w, h, paint);
    paint.setXfermode(PORTER_DUFF_XFERMODE_SRC_IN);
    canvas.drawBitmap(input, 0, 0, paint);
    return output;
}

And, this is what I've found for creating a rounded corners drawable that acts on all corners:

public static class RoundedCornersDrawable extends Drawable {
    private final float mCornerRadius;
    private final RectF mRect = new RectF();
    private final BitmapShader mBitmapShader;
    private final Paint mPaint;

    public RoundedCornersDrawable(final Bitmap bitmap, final float cornerRadius) {
        mCornerRadius = cornerRadius;
        mBitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP,
                Shader.TileMode.CLAMP);
        mPaint = new Paint();
        mPaint.setAntiAlias(false);
        mPaint.setShader(mBitmapShader);
        mRect.set(0, 0, bitmap.getWidth(), bitmap.getHeight());
    }

    @Override
    protected void onBoundsChange(final Rect bounds) {
        super.onBoundsChange(bounds);
        mRect.set(0, 0, bounds.width(), bounds.height());
    }

    @Override
    public void draw(final Canvas canvas) {
        canvas.drawRoundRect(mRect, mCornerRadius, mCornerRadius, mPaint);
    }

    @Override
    public int getOpacity() {
        return PixelFormat.TRANSLUCENT;
    }

    @Override
    public void setAlpha(final int alpha) {
        mPaint.setAlpha(alpha);
    }

    @Override
    public void setColorFilter(final ColorFilter cf) {
        mPaint.setColorFilter(cf);
    }
}

But this solution only works well if the imageView shows the content while maintaining the same aspect ratio as the bitmap, and also has its size pre-determined.

The question

How to create a center-cropped drawable, that shows a bitmap, has rounded corners for specific corners, and also be able to show an outline/stroke around it?

I want to do it without creating a new bitmap or extending ImageView. Only use a drawable that has the bitmap as the input.


回答1:


The SMART way is to use the PorterDuff blending mode. It's really simple and slick to create any fancy shading, blending, "crop" effect. you can find a lot of good tutorial about PorterDuff. here a good one.




回答2:


I always use this library to achieve what you are looking for. you can round any corner you want and also add stroke.

https://github.com/vinc3m1/RoundedImageView

You can use it or see it's source codes just for inspiration.

EDIT

there is no need to use Image View and make bitmap or drawable yourself and show it in Image View. Just replace Image View with Rounded Image View and it will handle everything for you without any extra work in code ! here is sample :

<com.makeramen.roundedimageview.RoundedImageView
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/imageView1"
    android:scaleType="centerCrop"
    app:riv_corner_radius="8dp"
    app:riv_border_width="2dp"
    app:riv_border_color="#333333"
    app:riv_oval="false" />

In code, just pass any image resource to it or use any Image Loader with it.

RoundedImageView myRoundedImage=(RoundedImageView)findViewById(R.id.imageView1);
myRoundedImage.setImageResource(R.drawable.MY_DRAWABLE);
// OR
ImageLoader.getInstance().displayImage(YOUR_IMAGE_URL, myRoundedImage);

if you want to just make specific corners rounded try this:

 <com.makeramen.roundedimageview.RoundedImageView
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/imageView1"
    android:scaleType="centerCrop"
    app:riv_corner_radius_top_right="8dp"
    app:riv_corner_radius_bottom_right="8dp"
    app:riv_border_width="2dp"
    app:riv_border_color="#333333"
    app:riv_oval="false" />



回答3:


Well, you can create a new .xml drawable named my_background and paste this code below:

<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle"
    >
    <solid android:color="#00000000"/>
    <corners
        android:bottomLeftRadius="12dp"
        android:bottomRightRadius="12dp"
        android:topLeftRadius="12dp"
        android:topRightRadius="12dp" />
    <stroke
        android:width="1dp"
        android:color="#000000"
        />
</shape>

Then, you set your ImageButton's, ImageView's background to your new drawable, like this:

android:background="@drawable/my_background"
android:scaleType="centerCrop"

or programatically:

myView.setBackground(R.drawable.my_background);

Hope it helps!

EDIT:

To programmatically create a similar drawable, you can use it:

GradientDrawable shape = new GradientDrawable();
    shape.setShape(GradientDrawable.RECTANGLE);
    shape.setCornerRadii(new float[] { 8, 8, 8, 8, 0, 0, 0, 0 });
    shape.setColor(Color.TRANSPARENT);
    shape.setStroke(3, Color.BLACK);
    v.setBackgroundDrawable(shape);



回答4:


Ok, here's my try. The only gotcha is that "int corners" is meant to be a set of flags. Such as 0b1111 where each 1 represents a corner to be rounded, and 0 is the opposite. The order is TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT.

example usage first, formatted for readability:

((ImageView) findViewById(R.id.imageView)).setImageDrawable(
    new RoundedRectDrawable(
        getResources(),
        bitmap,
        (float) .15,
        0b1101,
        8,
        Color.YELLOW
    )
);

code:

import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.drawable.BitmapDrawable;
import android.os.SystemClock;

/**
 * Created by mliu on 4/15/16.
 */
public class RoundedRectDrawable extends BitmapDrawable {
private final BitmapShader bitmapShader;
private final Paint p;
private final RectF rect;
private final float borderRadius;
private final float outlineBorderRadius;
private final int w;
private final int h;
private final int corners;
private final int border;
private final int bordercolor;

public RoundedRectDrawable(final Resources resources, final Bitmap bitmap, float borderRadiusSeed, int corners, int borderPX, int borderColor) {
    super(resources, bitmap);
    bitmapShader = new BitmapShader(getBitmap(),
            BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP);
    final Bitmap b = getBitmap();
    p = getPaint();
    p.setAntiAlias(true);
    p.setShader(bitmapShader);
    w = b.getWidth();
    h = b.getHeight();
    rect = new RectF(0,0,w,h);
    borderRadius = borderRadiusSeed * Math.min(w, h);
    border = borderPX;
    this.corners = corners;
    this.bordercolor = borderColor;
    outlineBorderRadius = borderRadiusSeed * Math.min(w+border,h+border);
}

@Override
public void draw(final Canvas canvas) {
    if ((corners&0b1111)==0){
        if (border>0) {
            Paint border = new Paint();
            border.setColor(bordercolor);
            canvas.drawRect(rect, border);
        }
        canvas.drawRect(rect.left + border, rect.top + border, rect.width() - border, rect.height() - border, p);
    } else {
        if (border >0) {
            Paint border = new Paint();
            border.setColor(bordercolor);
            canvas.drawRoundRect(rect, outlineBorderRadius, outlineBorderRadius, border);

            if ((corners & 0b1000) == 0) {
                //top left
                canvas.drawRect(rect.left, rect.top, rect.width() / 2, rect.height() / 2, border);
            }
            if ((corners & 0b0100) == 0) {
                //top right
                canvas.drawRect(rect.width() / 2, rect.top, rect.width(), rect.height() / 2, border);
            }
            if ((corners & 0b0010) == 0) {
                //bottom left
                canvas.drawRect(rect.left, rect.height() / 2, rect.width() / 2, rect.height(), border);
            }
            if ((corners & 0b0001) == 0) {
                //bottom right
                canvas.drawRect(rect.width() / 2, rect.height() / 2, rect.width(), rect.height(), border);
            }
        }
        canvas.drawRoundRect(new RectF(rect.left + border, rect.top + border, rect.width() - border, rect.height() - border), borderRadius, borderRadius, p);
        if ((corners & 0b1000) == 0) {
            //top left
            canvas.drawRect(rect.left + border, rect.top + border, rect.width() / 2, rect.height() / 2, p);
        }
        if ((corners & 0b0100) == 0) {
            //top right
            canvas.drawRect(rect.width() / 2, rect.top + border, rect.width() - border, rect.height() / 2, p);
        }
        if ((corners & 0b0010) == 0) {
            //bottom left
            canvas.drawRect(rect.left + border, rect.height() / 2, rect.width() / 2, rect.height() - border, p);
        }
        if ((corners & 0b0001) == 0) {
            //bottom right
            canvas.drawRect(rect.width() / 2, rect.height() / 2, rect.width() - border, rect.height() - border, p);
        }
    }
}

}

So, this handles the outline first, if needed, then the bitmap. It marks the canvas up with a rounded rect first, then "squares out" each corner you don't want to round. Seems highly inefficient, and probably is, but average case run time before minimal optimizations (corners = 0b0000, 10 canvas.draw calls) takes ~ 200us on a S7. And, that time is SUPER inconsistent based on phone usage. I've gotten as low as 80us and as high as 1.5ms.

NOTES/WARNING: I am BAD at this. This is not optimal. There's probably better answers already here on SO. The subject matter is just a bit uncommon and difficult to search up. I was originally not going to post this, but at time of writing this is still not marked answered, and the library OP did not wish to use due to problems with their Drawable actually use a very similar approach as my terrible solution. So, now I'm less embarrassed to share this. Additionally, though what I posted today was 95% written yesterday, I know I got some of this code from a tutorial or a SO post, but I can't remember who to attribute cause I didn't end up using it in my project. Apologies whoever you are.



来源:https://stackoverflow.com/questions/36370964/how-to-create-a-partially-rounded-corners-rectangular-drawable-with-center-crop

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!