圆形头像CircleImageView源码浅析
生活随笔
收集整理的這篇文章主要介紹了
圆形头像CircleImageView源码浅析
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
CircleImageView項目源碼下載:?
https://github.com/hdodenhof/CircleImageView???
關于CircleImageView的使用很簡單的,我就不說了,直接從源碼開始分析,通過測試可以發現是從setImageXXX()開始呢,
接下來那我們查看setup()源碼:
//因為mReady默認值為false,所以第一次進這個函數的時候if語句為真進入括號體內 //設置mSetupPending為true然后直接返回,后面的代碼并沒有執行。 if (!mReady) {mSetupPending = true; return; }
然后從CircleImageView()構造函數看起
public CircleImageView(Context context) {super(context); init(); }public CircleImageView(Context context, AttributeSet attrs) {this(context, attrs, 0); }public CircleImageView(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle); //通過obtainStyledAttributes 獲得一組值賦給 TypedArray(數組) , 這一組值來自于res/values/attrs.xml中的name="CircleImageView"的declare-styleable中。 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CircleImageView, defStyle, 0); mBorderWidth = a.getDimensionPixelSize(R.styleable.CircleImageView_civ_border_width, DEFAULT_BORDER_WIDTH); mBorderColor = a.getColor(R.styleable.CircleImageView_civ_border_color, DEFAULT_BORDER_COLOR); mBorderOverlay = a.getBoolean(R.styleable.CircleImageView_civ_border_overlay, DEFAULT_BORDER_OVERLAY); mFillColor = a.getColor(R.styleable.CircleImageView_civ_fill_color, DEFAULT_FILL_COLOR);//調用 recycle() 回收TypedArray,以便后面重用 a.recycle(); init();}
獲取自定義屬性att.xml中得屬性
<resources> <declare-styleable name="CircleImageView"> <attr name="civ_border_width" format="dimension" /> <attr name="civ_border_color" format="color" /> <attr name="civ_border_overlay" format="boolean" /> <attr name="civ_fill_color" format="color" /> </declare-styleable> </resources> 接著我們看init()方法
private void init() {super.setScaleType(SCALE_TYPE); mReady = true; //上邊mSetupPending設置為true,所以這里執行setup() if (mSetupPending) {setup(); mSetupPending = false; } } 接著我們看看setup()
/** * 這個函數很關鍵,進行圖片畫筆邊界畫筆(Paint)一些重繪參數初始化: * 構建渲染器BitmapShader用Bitmap來填充繪制區域,設置樣式以及內外圓半徑計算等, * 以及調用updateShaderMatrix()函數和 invalidate()函數; */ private void setup() {//此時mReady為false,if不執行 if (!mReady) {mSetupPending = true; return; }if (getWidth() == 0 && getHeight() == 0) {return; }//防止空指針異常 if (mBitmap == null) {invalidate(); return; }// 構建渲染器,用mBitmap來填充繪制區域 ,參數值代表如果圖片太小的話 就直接拉伸 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); //設置填充畫筆樣式 mFillPaint.setStyle(Paint.Style.FILL); mFillPaint.setAntiAlias(true); mFillPaint.setColor(mFillColor); //這個地方是取的原圖片的寬高 mBitmapHeight = mBitmap.getHeight(); mBitmapWidth = mBitmap.getWidth(); // 設置含邊界顯示區域,取的是CircleImageView的布局實際大小,為方形 mBorderRect.set(calculateBounds()); //計算 圓形帶邊界部分(外圓)的最小半徑 mBorderRadius = Math.min((mBorderRect.height() - mBorderWidth) / 2.0f, (mBorderRect.width() - mBorderWidth) / 2.0f); // 初始圖片顯示區域為mBorderRect(CircleImageView的布局實際大小) mDrawableRect.set(mBorderRect); if (!mBorderOverlay && mBorderWidth > 0) {mDrawableRect.inset(mBorderWidth - 1.0f, mBorderWidth - 1.0f); }//這里計算的是內圓的最小半徑,也即去除邊界寬度的半徑 mDrawableRadius = Math.min(mDrawableRect.height() / 2.0f, mDrawableRect.width() / 2.0f); applyColorFilter(); //設置渲染器的變換矩陣也即是mBitmap用何種縮放形式填充 updateShaderMatrix(); //手動觸發ondraw()函數 完成最終的繪制 invalidate(); } 上面代碼注釋我寫的很詳細不再一步步解釋了,進行圖片畫筆邊界畫筆(Paint)一些重繪參數初始化:構建渲染器BitmapShader用Bitmap來填充繪制區域,設置樣式以及內外圓半徑計算等,以及調用updateShaderMatrix()函數和 invalidate()函數。?
這里關于半徑的計算,我畫圖舉個例子:CircleImageView的布局寬高度均為160,邊界的寬度為10如圖所示:
那么去除邊界寬度的內圓半徑為70,帶邊界部分的外圓半徑為75
接下來我們看看updateShaderMatrix()函數,
/** * 這個函數為設置BitmapShader的Matrix參數,設置最小縮放比例,平移參數。 * 作用:保證圖片損失度最小和始終繪制圖片正中央的那部分 */ private void updateShaderMatrix() {float scale; float dx = 0; float dy = 0; mShaderMatrix.set(null); //取最小的縮放比例 if (mBitmapWidth * mDrawableRect.height() > mDrawableRect.width() * mBitmapHeight) {//y軸縮放 x軸平移 使得圖片的y軸方向的邊的尺寸縮放到圖片顯示區域(mDrawableRect)一樣) scale = mDrawableRect.height() / (float) mBitmapHeight; dx = (mDrawableRect.width() - mBitmapWidth * scale) * 0.5f; } else {//x軸縮放 y軸平移 使得圖片的x軸方向的邊的尺寸縮放到圖片顯示區域(mDrawableRect)一樣) scale = mDrawableRect.width() / (float) mBitmapWidth; dy = (mDrawableRect.height() - mBitmapHeight * scale) * 0.5f; }// shaeder的變換矩陣,我們這里主要用于放大或者縮小。 mShaderMatrix.setScale(scale, scale); // 平移 mShaderMatrix.postTranslate((int) (dx + 0.5f) + mDrawableRect.left, (int) (dy + 0.5f) + mDrawableRect.top); // 設置變換矩陣 mBitmapShader.setLocalMatrix(mShaderMatrix); } 通過updateShaderMatrix函數設置BitmapShader的Matrix參數,對圖片mBitmap位置用縮放平移形式填充,目的是用最小的縮放比例,使得圖片的某個方向的邊的尺寸縮放到圖片顯示區域(mDrawableRect)一樣。做到了圖片損失度最小。同時scale保證Bitmap的寬或高和目標區域一致,那么高或寬就需要進行位移,使得Bitmap居中。
現在萬事俱備,只欠ondraw()了。接著上面再setup()最后會調用invalidate()函數觸發ondraw()函數完成最終的繪制。查看源碼
protected void onDraw(Canvas canvas) {if (mDisableCircularTransformation) {super.onDraw(canvas); return; }//如果圖片不存在就不畫 if (mBitmap == null) {return; }if (mFillColor != Color.TRANSPARENT) {canvas.drawCircle(mDrawableRect.centerX(), mDrawableRect.centerY(), mDrawableRadius, mFillPaint); }//繪制內圓形,參數內圓半徑,圖片畫筆為mBitmapPaint canvas.drawCircle(mDrawableRect.centerX(), mDrawableRect.centerY(), mDrawableRadius, mBitmapPaint); if (mBorderWidth > 0) { //如果圓形邊緣的寬度不為0 我們還要繪制帶邊界的外圓形 參數外圓半徑,邊界畫筆為mBorderPaint canvas.drawCircle(mBorderRect.centerX(), mBorderRect.centerY(), mBorderRadius, mBorderPaint); } }
使用配置好的mBitmapPaint和mBorderPaint先畫出繪制內圓形來以后再畫邊界圓形。源碼還有一些自定義設置樣式函數,很簡單。?
大功告成,是不是覺得思路比較簡單,精致干練。
我總結下源碼的精致之處:
-
流程控制的比較嚴謹,比如setup函數的使用
-
updateShaderMatrix保證圖片損失度最小和始終繪制圖片正中央的那部分
-
作者思路是畫圓用渲染器位圖填充,而不是把Bitmap重繪切割成一個圓形圖片。
總結
以上是生活随笔為你收集整理的圆形头像CircleImageView源码浅析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: PhotoView图片缩放控件源码浅析(
- 下一篇: IOS下将文字转成图片方法