Android Create a circle hole in a centerCropped image

816 Views Asked by At

I come again for an android brain idea!

I would like to put a circle hole hole in a imageView which bitmap is scaled as centerCrop. I know where I need to put the circle hole( in dp from left and bottom) and the hole radius. But don't know how to build it!

I know that I can use Porterduff to do the hole but what you suggest to do?

  • Custom bitmap
  • Custom drawable/view
  • Custom code

Thanks

Following Answers there is my CustomImage with hole:

public class MyImageView extends ImageView {


private AttributeSet attrs;
private float y;
private float x;
private float r;
private Paint paint;
private Rect mSrcRect;
private Rect mDestRect;
private Bitmap mBitmap;
private int alreadycalled = 0;

public MyImageView(Context context, AttributeSet attrs) {
    super(context, attrs);
    this.attrs = attrs;
    initView();
}

public MyImageView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    this.attrs = attrs;
    initView();
}

public MyImageView(Context context, float x, float y, float radius) {
    super(context);
    this.x = x;
    this.y = y;
    this.r = radius;
    Log.d("parameters", String.format("left:%s , right:%s, radius:%s", String.valueOf(x), String.valueOf(y), String.valueOf(r)));
    initView();
}

private void initView() {
    paint = new Paint(Paint.ANTI_ALIAS_FLAG);
    paint.setStyle(Paint.Style.FILL_AND_STROKE);
    paint.setStrokeWidth(10);
    paint.setColor(0xff000000);
}

@Override
protected void onDraw(Canvas canvas) {
    alreadycalled++;
    Log.d("alreadycalled", "called " + alreadycalled);
    Drawable mDrawable = getDrawable();
    if (mDrawable == null) {
        return; // couldn't resolve the URI
    }
    int dWidth = mDrawable.getIntrinsicWidth();
    int dHeight = mDrawable.getIntrinsicHeight();

    float scale = 1.0f;
    scale = Math.max(getWidth() * 1.0f / dWidth, getHeight()
            * 1.0f / dHeight);
    int nWidth = (int) (dWidth * scale);
    int nHeight = (int) (dHeight * scale);
    int offsetLeft = (nWidth - getWidth()) / 2;
    int offsetTop = (nHeight - getHeight()) / 2;
    mBitmap = ((BitmapDrawable) mDrawable).getBitmap();
    //custom mSrcRect mDestRect to achieve centerCrop
    mSrcRect = new Rect(0, 0, dWidth, dWidth);
    mDestRect = new Rect(-offsetLeft, -offsetTop, getWidth() + offsetLeft, getHeight() + offsetTop);
    Log.d("src", mSrcRect.toString());
    Log.d("dest", mDestRect.toString());
    int sc = canvas.saveLayer(0, 0, getWidth(), getHeight(), null,
            Canvas.MATRIX_SAVE_FLAG | Canvas.CLIP_SAVE_FLAG
                    | Canvas.HAS_ALPHA_LAYER_SAVE_FLAG
                    | Canvas.FULL_COLOR_LAYER_SAVE_FLAG
                    | Canvas.CLIP_TO_LAYER_SAVE_FLAG);
    paint.setColor(0xffffffff);
    canvas.drawBitmap(mBitmap, mSrcRect, mDestRect, paint);
    paint.setColor(0xffff0000);
    paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
    Log.d("position", String.format("%s , %s", String.valueOf(x), String.valueOf(y)));
    canvas.drawCircle(x, y, r, paint);
    paint.setXfermode(null);
    canvas.restoreToCount(sc);


}

I called it programmatically with theses lines :

BitmapDrawable bd=(BitmapDrawable) getResources().getDrawable(R.drawable.triangle_bas_accueil2);
MyImageView customImView = new MyImageView(getApplicationContext(), mX, mY, mRadius);
customImView.setImageDrawable(bd);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
params.gravity = Gravity.BOTTOM;
customImView.setLayoutParams(params);
down_relative.addView(customImView);

But the onDraw() method is called twice (maybe it needs) but that makes me two holes, one that I can change parameters but the other still at the same place ! The container of the MyImageView is a RelativeLayout.

If someOne has an idea? @tiny-sunlight ?

2

There are 2 best solutions below

10
On BEST ANSWER

This one is just for CenterCrop and can't deal with scaleType.And this code may have some problems because I'm not good at canvas.

public class MyImageView extends ImageView {


    private final AttributeSet attrs;
    private Paint paint;
    private Rect mSrcRect;
    private Rect mDestRect;
    private Bitmap mBitmap;
    public MyImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.attrs = attrs;
        initView();
    }

    public MyImageView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.attrs = attrs;
        initView();
    }

    private void initView() {
        paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setStyle(Paint.Style.FILL_AND_STROKE);
        paint.setStrokeWidth(10);
        paint.setColor(0xff000000);
    }

    @Override
    protected void onDraw(Canvas canvas) {
//        super.onDraw(canvas);
        //create the drawable.Maybe you can cache it.
        Drawable mDrawable = (BitmapDrawable) getResources().getDrawable(R.drawable.triangle_bas_accueil2);
        if (mDrawable == null) {
            return; // couldn't resolve the URI
        }
        int dWidth = mDrawable.getIntrinsicWidth();
        int dHeight = mDrawable.getIntrinsicHeight();

        float scale = 1.0f;
        scale = Math.max(getWidth() * 1.0f / dWidth, getHeight()
                * 1.0f / dHeight);
        int nWidth = (int) (dWidth*scale);
        int nHeight = (int) (dHeight*scale);
        int offsetLeft = (nWidth - getWidth())/2;
        int offsetTop = (nHeight - getHeight())/2;
        //cache mBitmap 
        mBitmap = mBitmap == null ? ((BitmapDrawable) mDrawable).getBitmap(): mBitmap;
        //custom mSrcRect mDestRect to achieve centerCrop
        mSrcRect = new Rect(0, 0, dWidth, dWidth);
        mDestRect = new Rect(-offsetLeft, -offsetTop,getWidth()+offsetLeft, getHeight()+offsetTop);
        int x = 250;int r = 100;
        int sc = canvas.saveLayer(0, 0, getWidth(), getHeight(), null,
                Canvas.MATRIX_SAVE_FLAG | Canvas.CLIP_SAVE_FLAG
                        | Canvas.HAS_ALPHA_LAYER_SAVE_FLAG
                        | Canvas.FULL_COLOR_LAYER_SAVE_FLAG
                        | Canvas.CLIP_TO_LAYER_SAVE_FLAG);
        paint.setColor(0xffffffff);
        canvas.drawBitmap(mBitmap,mSrcRect,mDestRect,paint);
        paint.setColor(0xff000000);
        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
        canvas.drawCircle(x,x,r,paint);
        paint.setXfermode(null);
        canvas.restoreToCount(sc);
    }
}
1
On

Custom code. Loop through each pixel and change the alpha. Here is some pseudocode:

for (int y = 0; y < imageHeight; y++) {
    for (int x = 0; x < imageWidth; x++) {
        // Check if the current pixel is within the circle
        if (dist(x, y, centerX, centerY) <= radius) {
            // Change the alpha
            image.setPixelAlpha(x, y, 0);
        }
    }
}