Android 自定義圓形圖片 CircleImageView


1.效果預覽

1.1.布局中寫自定義圓形圖片的路徑即可

  

 

1.2.然后看一看圖片效果

  

 

1.3.原圖是這樣的 @mipmap/ic_launcher

  


2.使用過程

2.1.CircleImageView源代碼 

public class CircleImageView extends AppCompatImageView {

    private static final ScaleType SCALE_TYPE = ScaleType.CENTER_CROP;

    private static final Bitmap.Config BITMAP_CONFIG = Bitmap.Config.ARGB_8888;
    private static final int COLORDRAWABLE_DIMENSION = 1;

    private static final int DEFAULT_BORDER_WIDTH = 0;
    private static final int DEFAULT_BORDER_COLOR = Color.BLACK;

    private final RectF mDrawableRect = new RectF();
    private final RectF mBorderRect = new RectF();

    private final Matrix mShaderMatrix = new Matrix();
    private final Paint mBitmapPaint = new Paint();
    private final Paint mBorderPaint = new Paint();

    private int mBorderColor = DEFAULT_BORDER_COLOR;
    private int mBorderWidth = DEFAULT_BORDER_WIDTH;

    private Bitmap mBitmap;
    private BitmapShader mBitmapShader;
    private int mBitmapWidth;
    private int mBitmapHeight;

    private float mDrawableRadius;
    private float mBorderRadius;

    private boolean mReady;
    private boolean mSetupPending;

    public CircleImageView(Context context) {
        super(context);
    }

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

    public CircleImageView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        super.setScaleType(SCALE_TYPE);

        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CircleImageView, defStyle, 0);

        mBorderWidth = a.getDimensionPixelSize(R.styleable.CircleImageView_border_width, DEFAULT_BORDER_WIDTH);
        mBorderColor = a.getColor(R.styleable.CircleImageView_border_color, DEFAULT_BORDER_COLOR);

        a.recycle();

        mReady = true;

        if (mSetupPending) {
            setup();
            mSetupPending = false;
        }
    }

    @Override
    public ScaleType getScaleType() {
        return SCALE_TYPE;
    }

    @Override
    public void setScaleType(ScaleType scaleType) {
        if (scaleType != SCALE_TYPE) {
            throw new IllegalArgumentException(String.format("ScaleType %s not supported.", scaleType));
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        if (getDrawable() == null) {
            return;
        }

        canvas.drawCircle(getWidth() / 2, getHeight() / 2, mDrawableRadius, mBitmapPaint);
        canvas.drawCircle(getWidth() / 2, getHeight() / 2, mBorderRadius, mBorderPaint);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        setup();
    }

    public int getBorderColor() {
        return mBorderColor;
    }

    public void setBorderColor(int borderColor) {
        if (borderColor == mBorderColor) {
            return;
        }

        mBorderColor = borderColor;
        mBorderPaint.setColor(mBorderColor);
        invalidate();
    }

    public int getBorderWidth() {
        return mBorderWidth;
    }

    public void setBorderWidth(int borderWidth) {
        if (borderWidth == mBorderWidth) {
            return;
        }

        mBorderWidth = borderWidth;
        setup();
    }

    @Override
    public void setImageBitmap(Bitmap bm) {
        super.setImageBitmap(bm);
        mBitmap = bm;
        setup();
    }

    @Override
    public void setImageDrawable(Drawable drawable) {
        super.setImageDrawable(drawable);
        mBitmap = getBitmapFromDrawable(drawable);
        setup();
    }

    @Override
    public void setImageResource(int resId) {
        super.setImageResource(resId);
        mBitmap = getBitmapFromDrawable(getDrawable());
        setup();
    }

    private Bitmap getBitmapFromDrawable(Drawable drawable) {
        if (drawable == null) {
            return null;
        }

        if (drawable instanceof BitmapDrawable) {
            return ((BitmapDrawable) drawable).getBitmap();
        }

        try {
            Bitmap bitmap;

            if (drawable instanceof ColorDrawable) {
                bitmap = Bitmap.createBitmap(COLORDRAWABLE_DIMENSION, COLORDRAWABLE_DIMENSION, BITMAP_CONFIG);
            } else {
                bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), BITMAP_CONFIG);
            }

            Canvas canvas = new Canvas(bitmap);
            drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
            drawable.draw(canvas);
            return bitmap;
        } catch (OutOfMemoryError e) {
            return null;
        }
    }

    private void setup() {
        if (!mReady) {
            mSetupPending = true;
            return;
        }

        if (mBitmap == null) {
            return;
        }

        mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);

        mBitmapPaint.setAntiAlias(true);
        mBitmapPaint.setShader(mBitmapShader);

        mBorderPaint.setStyle(Paint.Style.STROKE);
        mBorderPaint.setAntiAlias(true);
        mBorderPaint.setColor(mBorderColor);
        mBorderPaint.setStrokeWidth(mBorderWidth);

        mBitmapHeight = mBitmap.getHeight();
        mBitmapWidth = mBitmap.getWidth();

        mBorderRect.set(0, 0, getWidth(), getHeight());
        mBorderRadius = Math.min((mBorderRect.height() - mBorderWidth) / 2, (mBorderRect.width() - mBorderWidth) / 2);

        mDrawableRect.set(mBorderWidth, mBorderWidth, mBorderRect.width() - mBorderWidth, mBorderRect.height() - mBorderWidth);
        mDrawableRadius = Math.min(mDrawableRect.height() / 2, mDrawableRect.width() / 2);

        updateShaderMatrix();
        invalidate();
    }

    private void updateShaderMatrix() {
        float scale;
        float dx = 0;
        float dy = 0;

        mShaderMatrix.set(null);

        if (mBitmapWidth * mDrawableRect.height() > mDrawableRect.width() * mBitmapHeight) {
            scale = mDrawableRect.height() / mBitmapHeight;
            dx = (mDrawableRect.width() - mBitmapWidth * scale) * 0.5f;
        } else {
            scale = mDrawableRect.width() / mBitmapWidth;
            dy = (mDrawableRect.height() - mBitmapHeight * scale) * 0.5f;
        }

        mShaderMatrix.setScale(scale, scale);
        mShaderMatrix.postTranslate((int) (dx + 0.5f) + mBorderWidth, (int) (dy + 0.5f) + mBorderWidth);

        mBitmapShader.setLocalMatrix(mShaderMatrix);
    }

}
View Code

 

2.2.在values中新建一個資源文件==>attrs_CircleImageView.xml  

<resources>

    <!-- 圓形頭像 -->
    <declare-styleable name="CircleImageView">
        <attr name="border_width" format="dimension" />
        <attr name="border_color" format="color" />
    </declare-styleable>

</resources>
View Code

 

2.3.布局中將ImageView視圖換成自定義類,路徑寫自己的

  

  

 

2.4.大功告成!然后圖片就乖乖地變成圓形了。如果想了解源碼,請看下方的分析。


3.CircleImageView源代碼分析

3.1.成員變量分析

  

  ScaleType是圖片顯示方式。可以參考一下這篇文章了解ScaleType。

  可以有八種取值方式:

  ①.matrix==>表示原圖從ImageView的左上角開始繪制。

  ②.fitXY==>填充整個ImageView,需要對圖片進行一些縮放,會變形。

  ③.fitStart==>按比例縮放至View的寬度或者高度(取最小值),然后居上或者居左顯示。

  ④.fitCenter==>將圖片按比例縮放之后居中顯示。

  ⑤.fitEnd==>按比例縮放之后居右或者居下顯示。

  ⑥.center==>將原圖按照原來的大小居中顯示,如果超出ImageView的大小,剪裁掉多余的部分。

  ⑦.centerCrop==>將ImageView填充滿,按照比例縮放原圖,多余的寬和高裁剪掉,最常用的。

  ⑧.centerInside==>將原圖完整的顯示出來,按照比例縮放原圖,一般都變得很小了。

 

  Bitmap.Config是什么東西呢?可以參考一下這篇文章了解Bitmap.Config。

  其實這都是色彩的存儲方法,我們知道ARGB指的是一種色彩模式。

  里面A代表Alpha,R表示Red,G表示Green,B表示Blue。每個原色都存儲着所表示的顏色的信息值。

  

  位圖位數越高代表其可以存儲的顏色信息越多,當然圖像也就越逼真。

 

  這里定義了一個COLORDEAWABLE_DIMESION用來干什么呢?

  

 

  然后又定義了一個DEFAULT_BORDER_WIDTH,用來干啥呢?

  首先了解一下TypedArray,參考一下這篇文章。

  首先需要有一個資源文件,就是自定義的視圖布局,這里是attrs_CircleImageView.xml

  

    然后這里用到了DEFAULT_BORDER_WIDTH了。

  

 

  了解一下RectF,參考一下這篇文章了解RectF。

  RectF類和Rect類似,但是RectF參數是傳的Float,所以尾巴有個F了。

  

 

  可以參考一下這篇文章了解Matrix。

  這其實就是一個三維矩陣。

  

 

  然后主要作用分成4塊。

  

   用到的方法有:

  

 

  

  postTranslate是指在setScale后平移。

  由於縮放是以(0,0)為中心,所以為了把界面的中心與(0,0)對齊,調用postTranslate(centerX,centerY)把

  圖片向這(x,y)方向移動。

 

  什么是Paint類呢?參考這篇文章詳細了解。

  這個類可以畫集合圖形,文本和Bitmap。

  

  什么是BitmapShader呢?參考這篇文章詳細了解。

  就是處理圖片渲染的。可以做到這樣的效果。

  

 

  然后定義了兩個整型數據,兩個浮點型,兩個boolean型,之后再分析作用。

  

 

3.2.構造函數分析

  一個參數的構造函數

  

 

  兩個參數的構造函數

  

 

  三個參數的構造函數

  

  這個是最關鍵的一個構造函數了。

  將資源文件中的寬度和顏色獲取到。

  然后調用setup()函數進行初始化。

 

3.3.重寫函數getScaleType

  

  

3.4.重寫函數setScaleType

  

 

3.5.重寫onDraw

  

  這里用canvas畫了兩個圓。

  android中對於Canvas.drawCircle()方法不理解的可以參考這篇文章。

 

3.6.重寫onSizeChanged

  

 

3.7.邊界顏色  

  

 

3.8.邊界寬度

  

  調用了自己寫的一個setup()函數。

 

3.9.重寫setImageBitmap

  

  調用了自己寫的一個setup()函數。 

 

3.10.重寫setImageDrawable

  

  調用了自己寫的一個setup()函數。

 

3.11.重寫setImageResource

  

  調用了自己寫的一個setup()函數。

 

3.12.將Drawable轉換成Bitmap

  

 

3.13.自己寫的setup函數

  

 

3.14.更新渲染

  


4.其他自定義圓形圖片

4.1.Android開發之自定義圓形的ImageView的實現

  效果如下:

  

  自定類代碼如下:

/**
 * 自定義的圓形ImageView,可以直接當組件在布局中使用。
 * @author caizhiming
 *
 */
public class XCRoundImageView extends ImageView{

    private Paint paint ;
    
    public XCRoundImageView(Context context) {  
        this(context,null);  
    }  
  
    public XCRoundImageView(Context context, AttributeSet attrs) {  
        this(context, attrs,0);  
    }  
  
    public XCRoundImageView(Context context, AttributeSet attrs, int defStyle) {  
        super(context, attrs, defStyle); 
        paint = new Paint();
        
    }  
  
    /**
     * 繪制圓形圖片
     * @author caizhiming
     */
    @Override  
    protected void onDraw(Canvas canvas) {  
  
        Drawable drawable = getDrawable();  
        if (null != drawable) {  
            Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();  
            Bitmap b = getCircleBitmap(bitmap, 14);  
            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);  
        }  
    }  
  
    /**
     * 獲取圓形圖片方法
     * @param bitmap
     * @param pixels
     * @return Bitmap
     * @author caizhiming
     */
    private Bitmap getCircleBitmap(Bitmap bitmap, int pixels) {  
        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.drawARGB(0, 0, 0, 0);  
        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;  
        
        
    }  
}  
View Code

 

4.2.第三方庫圓形頭像CircleImageView的使用

  效果如下:

  

  用法如下:

  

 

4.3.自定義ImageView系列——簡單圓形圖片

  效果如下:

  

  源代碼:

public class CircleImageView extends ImageView {

    //基本的三個構造函數
    public CircleImageView(Context context) {
        super(context);
    }

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

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

    //自定義View實現過程中很重要的onDraw繪制圖形的方法
    @Override
    protected void onDraw(Canvas canvas) {

        Drawable drawable = getDrawable();

        //空值判斷,必要步驟,避免由於沒有設置src導致的異常錯誤
        if (drawable == null) {
            return;
        }

        //必要步驟,避免由於初始化之前導致的異常錯誤
        if (getWidth() == 0 || getHeight() == 0) {
            return;
        }

        if (!(drawable instanceof BitmapDrawable)) {
            return;
        }
        Bitmap b = ((BitmapDrawable) drawable).getBitmap();

        if (null == b) {
            return;
        }

        Bitmap bitmap = b.copy(Bitmap.Config.ARGB_8888, true);

        int w = getWidth();

        Bitmap roundBitmap = getCroppedBitmap(bitmap, w);
        canvas.drawBitmap(roundBitmap, 0, 0, null);

    }

    /**
     * 初始Bitmap對象的縮放裁剪過程
     * @param bmp       初始Bitmap對象
     * @param radius    圓形圖片直徑大小
     * @return 返回一個圓形的縮放裁剪過后的Bitmap對象
     */
    public static Bitmap getCroppedBitmap(Bitmap bmp, int radius) {
        Bitmap sbmp;
        //比較初始Bitmap寬高和給定的圓形直徑,判斷是否需要縮放裁剪Bitmap對象
        if (bmp.getWidth() != radius || bmp.getHeight() != radius)
            sbmp = Bitmap.createScaledBitmap(bmp, radius, radius, false);
        else
            sbmp = bmp;
        Bitmap output = Bitmap.createBitmap(sbmp.getWidth(), sbmp.getHeight(),
                Config.ARGB_8888);
        Canvas canvas = new Canvas(output);

        final Paint paint = new Paint();
        final Rect rect = new Rect(0, 0, sbmp.getWidth(), sbmp.getHeight());

        paint.setAntiAlias(true);
        paint.setFilterBitmap(true);
        paint.setDither(true);
        canvas.drawARGB(0, 0, 0, 0);
        paint.setColor(Color.parseColor("#BAB399"));
        canvas.drawCircle(sbmp.getWidth() / 2 + 0.7f,
                sbmp.getHeight() / 2 + 0.7f, sbmp.getWidth() / 2 + 0.1f, paint);
        //核心部分,設置兩張圖片的相交模式,在這里就是上面繪制的Circle和下面繪制的Bitmap
        paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
        canvas.drawBitmap(sbmp, rect, rect, paint);

        return output;
    }

}
View Code



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM