Search code examples
androidbitmaphole-punchingporter-duff

Android Create a circle hole in a centerCropped image


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 ?


Solution

  • 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);
        }
    }