CircleImageView的工作原理
在分析CircleImageView源碼之前,先學(xué)習(xí)一些知識點(diǎn)
知識點(diǎn)1:BitmapShader
BitmapShader 繼承自Shader,而Shader有5個子類:
- BitmapShader
- ComposeShader
- LinearGradient
- RadialGradient
- SweepGradient
首先來認(rèn)識一下Shader是什么创橄?
直接翻譯過來叫著色器利术,沒了解過的人看了會感覺很蒙朴恳,就跟把Socket翻譯成套接字似的哪审,現(xiàn)實(shí)生活在跟本沒有這玩意啊,怎么理解呢奔浅?
經(jīng)過我細(xì)致的揣摩,我認(rèn)為Shader就像是一把刷子或者說是印章诗良,它決定寫出來的東西是什么顏色的汹桦,或者畫出到圖案是什么樣子到。
比如說我想要制作一臺車鉴裹,在所有都組裝完畢之后舞骆,加了油就可以開了,但是看起來怎么這么不狂拽炫吊炸天呢? 哦對了壹罚,忘了給車噴漆了葛作,這時候我們就拿幾噴漆罐對著車噴一些文字或圖案,那噴圖案的過程就是Shader的過程.
我們上面提到有5種Shader:
- BitmapShader ------ 圖案噴漆
- ComposeShader ------ 將其他噴漆合并
- LinearGradient ------ 線性漸變色的噴漆
- RadialGradient ------ 環(huán)形漸變色的噴漆
- SweepGradient ------ 掃描噴漆(類似于從某一個點(diǎn)射出很多不同顏色的射線)
那么我們重點(diǎn)關(guān)注一下BitmapShader猖凛,它是一個可以將圖案繪制出來的噴漆赂蠢,比如說我要把小狗的圖像作為一個噴漆,那么我拿著這個噴漆對著車噴一下辨泳,車馬上就會有一只可愛的小狗的圖畫.
知識點(diǎn)2:ColorFilter
這個通過直譯就比較好理解虱岂,顏色過濾器,就像是一個篩子菠红,比如說我有3種類型的物體:三角形第岖,矩形,圓形试溯,讓他們通過管道從一個地方傳遞到另外一個地方蔑滓,如果管道是完全通透的,那么遇绞,這些物體從起始點(diǎn)到終點(diǎn)都是一樣的键袱,那么如果我中間加一個三角形過濾器,只有三角形能通過摹闽,那么剩下矩形和圓形就過不去了蹄咖,顏色過濾器類似,但是它能設(shè)置量付鹿,既通過的多少.
ColorFilter在Android中是一個5* 4的矩陣:
{
1, 0, 0, 0, 0 // 顏色種的紅色R
0, 1, 0, 0, 0 // 顏色種的綠色G
0, 0, 1, 0, 0 // 顏色種的藍(lán)色B
0, 0, 0, 1, 0 // 顏色種的透明度A
}
如上所示澜汤,其中1的位置就是每個顏色的過濾器蚜迅,范圍0 ~ 2,其中1表示正常顏色俊抵,0表示完全隔斷谁不,2表示FF,所以我們?nèi)绻{(diào)整一張圖片的顏色务蝠,只要調(diào)整ColorFilter種的值就可以做到了.
知識點(diǎn)3:Matrix
這里討論的是Android中
android.graphics.Matrix
這個類
Android種的Martix是用來做圖像調(diào)整的拍谐,如平移,縮放馏段,旋轉(zhuǎn)轩拨,還有錯切,它是一個3 * 3的矩陣.
{
縮放X, 錯切X, 平移X,
錯切Y, 縮放Y, 平移Y,
透視1, 透視2, 透視3,
}
其中透視基本不用院喜,咱主要用到的是平移和縮放亡蓉。
正式開始
public class CircleImageView extends ImageView {
//首先強(qiáng)制設(shè)置縮放類型為CENTER_CROP
private static final ScaleType SCALE_TYPE = ScaleType.CENTER_CROP;
//其次,Bitmap類型為ARGB_8888
private static final Bitmap.Config BITMAP_CONFIG = Bitmap.Config.ARGB_8888;
private static final int COLORDRAWABLE_DIMENSION = 2;
private static final int DEFAULT_BORDER_WIDTH = 0;
private static final int DEFAULT_BORDER_COLOR = Color.BLACK;
private static final int DEFAULT_FILL_COLOR = Color.TRANSPARENT;
private static final boolean DEFAULT_BORDER_OVERLAY = false;
然后在UI在創(chuàng)建時會執(zhí)行init();
private void init() {
//設(shè)置ScaleType
super.setScaleType(SCALE_TYPE);
mReady = true;
if (mSetupPending) {
//重點(diǎn)看一下setup
setup();
mSetupPending = false;
}
}
我們重點(diǎn)關(guān)注一下setup()
private void setup() {
...
//初始化一個BitmapShader喷舀,就是上面講的會噴圖的噴漆
mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
//將噴圖賦予mBitmapPaint這個畫筆砍濒,感覺上面的比喻有點(diǎn)不對,BitmapShader應(yīng)該只是一個圖案硫麻,噴漆應(yīng)該是Paint
mBitmapPaint.setAntiAlias(true);
mBitmapPaint.setShader(mBitmapShader);
//圓環(huán)形的邊框用的畫筆
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);
//取到bitmap的寬高信息
mBitmapHeight = mBitmap.getHeight();
mBitmapWidth = mBitmap.getWidth();
//計算圓形頭像的范圍爸邢,如果寬 > 高,則用高作為正方形邊長
mBorderRect.set(calculateBounds());
//半徑為寬或者高的一半
mBorderRadius = Math.min((mBorderRect.height() - mBorderWidth) / 2.0f, (mBorderRect.width() - mBorderWidth) / 2.0f);
//DrawableRect為里面圖案的范圍拿愧,而BorderRect則是邊框的范圍
mDrawableRect.set(mBorderRect);
if (!mBorderOverlay && mBorderWidth > 0) {
//縮小mBorderWidth - 1 個像素
mDrawableRect.inset(mBorderWidth - 1.0f, mBorderWidth - 1.0f);
}
mDrawableRadius = Math.min(mDrawableRect.height() / 2.0f, mDrawableRect.width() / 2.0f);
//設(shè)置顏色過濾器
applyColorFilter();
//更新Matrix
updateShaderMatrix();
//刷新UI
invalidate();
}
接下來再看看updateShaderMatrix
private void updateShaderMatrix() {
float scale;
float dx = 0;
float dy = 0;
//reset the matrix
mShaderMatrix.set(null);
//計算從原始的bitmap到UI需要顯示的bitmap杠河,需要的縮放和位移,其中浇辜,如果原圖寬度大于高度券敌,則只位移X,否則位移Y
if (mBitmapWidth * mDrawableRect.height() > mDrawableRect.width() * mBitmapHeight) {
scale = mDrawableRect.height() / (float) mBitmapHeight;
dx = (mDrawableRect.width() - mBitmapWidth * scale) * 0.5f;
} else {
scale = mDrawableRect.width() / (float) mBitmapWidth;
dy = (mDrawableRect.height() - mBitmapHeight * scale) * 0.5f;
}
//傳入縮放設(shè)置
mShaderMatrix.setScale(scale, scale);
//傳入位移設(shè)置
mShaderMatrix.postTranslate((int) (dx + 0.5f) + mDrawableRect.left, (int) (dy + 0.5f) + mDrawableRect.top);
//將Martix設(shè)置給BitmapShader
mBitmapShader.setLocalMatrix(mShaderMatrix);
}
所以柳洋,通過查看源碼我們就能了解到待诅,CircleImageView的工作原理是將ImageView的Bitmap捕獲到,然后將這個Bitmap設(shè)置給BitmapShader熊镣,其中利用Matrix調(diào)整Bitmap在BitmapShader中的大小為ImageView大小并且位置居中卑雁,然后把BitmapShader利用Paint.setShader賦值給畫筆Paint,最后再利用這個畫筆繪制一個圓形即可畫出我們設(shè)置的圖片绪囱,而且是圓形测蹲,所以利用這個原理,我們可以畫任何我們想要的形狀哦毕箍。