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); } }
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>
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,用來干啥呢?
首先需要有一個資源文件,就是自定義的視圖布局,這里是attrs_CircleImageView.xml
然后這里用到了DEFAULT_BORDER_WIDTH了。
RectF類和Rect類似,但是RectF參數是傳的Float,所以尾巴有個F了。
這其實就是一個三維矩陣。
然后主要作用分成4塊。
用到的方法有:
postTranslate是指在setScale后平移。
由於縮放是以(0,0)為中心,所以為了把界面的中心與(0,0)對齊,調用postTranslate(centerX,centerY)把
圖片向這(x,y)方向移動。
這個類可以畫集合圖形,文本和Bitmap。
就是處理圖片渲染的。可以做到這樣的效果。
然后定義了兩個整型數據,兩個浮點型,兩個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; } }
4.2.第三方庫圓形頭像CircleImageView的使用
效果如下:
用法如下:
效果如下:
源代碼:

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