android realizes CircleImageView by modifying image pixels

Keywords: Android

There are many ways to implement CircleImageView, each with its own advantages and disadvantages, so it needs to be used according to different scenarios. Today, we use the method of modifying image pixels to realize CircleImageView. The main knowledge points are nothing more than Pythagorean theorem and the distance from point to circle.

Material picture:

 

The effect is as follows:

 

1. clipPath cutting canvas

The minimum version supported by this method is Android 4.3(API Level 18), which is convenient and fast, but it does not support hardware addition. In addition, it also has the disadvantages of Path, which does not support anti aliasing.

 @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        Paint paint = new Paint();

        mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.pic);
        mPath = new Path();

        mPath.addCircle(mBitmap.getWidth() / 2, mBitmap.getHeight() / 2, mBitmap.getWidth() / 2, Path.Direction.CCW);
        canvas.clipPath(mPath);
        canvas.drawBitmap(mBitmap, 0, 0, paint);
    }

 

2. Use PorterDuffXfermode

PorterDuffXfermode is the mainstream image synthesis tool for Android. It has many support modes, strong stability, good effect, high quality, and anti aliasing support. It is loved by many developers. It can be said that it is the first choice for many application development. The disadvantage is that the difficulty of learning is a little high, in addition, it takes up more memory.

 /**
     * Draw a circular picture
     *
     */
    @Override  
    protected void onDraw(Canvas canvas) {  
  
        Drawable drawable = getDrawable();  
        if (null != drawable) {  
            Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();  
            Bitmap b = getCircleBitmap(bitmap);  
            final Rect rectSrc = new Rect(0, 0, b.getWidth(), b.getHeight());  
            final Rect rectDest = new Rect(0,0,getWidth(),getHeight());
            paint.reset();  
            canvas.drawBitmap(b, rectSrc, rectDest, paint);  
  
        } else {  
            super.onDraw(canvas);  
        }  
    }  
  
    /**
     * How to get a circular picture
     * @param bitmap
     * @param pixels
     * @return Bitmap
     */
    private Bitmap getCircleBitmap(Bitmap bitmap) {  
        Bitmap output = Bitmap.createBitmap(bitmap.getWidth(),  
                bitmap.getHeight(), Config.ARGB_8888);  
        Canvas canvas = new Canvas(output);  
          
        final int color = 0xff424242;
       
        final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());  
        paint.setAntiAlias(true);  
        canvas.drawColor(Color.TRANSPARENT);  
        paint.setColor(color);  
        int x = bitmap.getWidth(); 
        
        canvas.drawCircle(x / 2, x / 2, x / 2, paint);  
        paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));  
        canvas.drawBitmap(bitmap, rect, rect, paint);  
        return output;  
        
        
    }  

4. Set the Shader of the brush Paint, and then draw a circular picture with the brush

This method is used by Glide and picasso. It is simple and convenient to use and occupies a medium proportion.

 
   
    @Override  
    protected void onDraw(Canvas canvas) {  
  
        Drawable drawable = getDrawable();  
        if (null != drawable) {  
            Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();  
            Bitmap b = transform(bitmap);  
            final Rect rectSrc = new Rect(0, 0, b.getWidth(), b.getHeight());  
            final Rect rectDest = new Rect(0,0,getWidth(),getHeight());
            paint.reset();  
            canvas.drawBitmap(b, rectSrc, rectDest, paint);  
  
        } else {  
            super.onDraw(canvas);  
        }  
    } 

public Bitmap transform(Bitmap source) {
            int size = Math.min(source.getWidth(), source.getHeight());

            int x = (source.getWidth() - size) / 2;
            int y = (source.getHeight() - size) / 2;

            Bitmap squaredBitmap = Bitmap.createBitmap(source, x, y, size, size);
            if (squaredBitmap != source) {
                source.recycle();
            }

            Bitmap bitmap = Bitmap.createBitmap(size, size, source.getConfig());

            Canvas canvas = new Canvas(bitmap);
            Paint paint = new Paint();
            BitmapShader shader = new BitmapShader(squaredBitmap,
                    BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP);

          float mScale = (mRadius * 2.0f) / Math.min(bitmap.getHeight(), bitmap.getWidth());

          Matrix matrix = new Matrix();
          matrix.setScale(mScale, mScale);
          bitmapShader.setLocalMatrix(matrix);


            paint.setShader(shader);
            paint.setAntiAlias(true);

            float r = size / 2f;
            canvas.drawCircle(r, r, r, paint);

            squaredBitmap.recycle();
            return bitmap;
        }

5. Modify pixel

This method supports anti aliasing, is simple to use, and its memory share is also in the middle level.

public class CircleImageView extends AppCompatImageView {

    public CircleImageView(Context context) {
        this(context,null);
    }


    public CircleImageView(Context context, AttributeSet attrs) {
        this(context, attrs,0);
    }

    public CircleImageView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }


    @Override
    public void onDraw(Canvas canvas) {

        int width  = getWidth();
        int height = getHeight();


        int minSize = Math.min(width,height)/2;
        Drawable drawable = getDrawable();
        if(drawable!=null && minSize!=0) {

            if(Math.min(drawable.getIntrinsicWidth(),drawable.getIntrinsicHeight())==0) {
                super.onDraw(canvas);
                return;
            }

            int intrinsicWidth = drawable.getIntrinsicWidth();
            int intrinsicHeight = drawable.getIntrinsicHeight();

            float R = Math.min(intrinsicWidth,intrinsicHeight)/2;

            Bitmap bmp = Bitmap.createBitmap(intrinsicWidth, intrinsicHeight, Bitmap.Config.ARGB_8888);
            Canvas targetCanvas = new Canvas(bmp);

            drawable.draw(targetCanvas);

            for (int y=0;y<intrinsicHeight;y++){
                for (int x=0;x<intrinsicWidth;x++){
                    if((Math.pow(x-intrinsicWidth/2,2) + Math.pow(y-intrinsicHeight/2,2))<=Math.pow(R,2)){
                        continue;
                    }
                    bmp.setPixel(x,y, Color.TRANSPARENT);
                }
            }

            Matrix imageMatrix = getImageMatrix();
            if(imageMatrix!=null){
                canvas.concat(imageMatrix);
            }
            final int saveCount = canvas.getSaveCount();
            canvas.save();

            if (getCropToPadding()) {
                final int scrollX = getScrollX();
                final int scrollY = getScrollY();
                canvas.clipRect(scrollX + getPaddingLeft(), scrollY + getPaddingTop(),
                        scrollX + getRight() - getLeft() - getPaddingRight(),
                        scrollY + getBottom() - getTop() - getPaddingBottom());
            }

            canvas.translate(getPaddingLeft(),getPaddingTop());

            DrawFilter drawFilter = canvas.getDrawFilter();
            canvas.setDrawFilter(new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG|Paint.FILTER_BITMAP_FLAG));
            canvas.drawBitmap(bmp,0,0,null);
            canvas.setDrawFilter(drawFilter);

            canvas.restoreToCount(saveCount);

            bmp.recycle();

        }else{
            super.onDraw(canvas);
        }
    }
}

Posted by FrOzeN on Fri, 29 Nov 2019 06:28:15 -0800