生活中我們見到的大多數頭像都變成圓形頭像,因爲圓形可以給客戶一種親和感,更能被客戶接受。而我們Android5.0之前,想要使用圓形頭像必須要自己來手寫,5.0之後自帶了圓形頭像爲我們開發人員剩了好多事情。
閒話不多說了,直接來看一下效果。
實現分爲下面幾步:
1.自定義屬性(attr.xml);
2.構造方法中獲取屬性值;
3.調用ondraw()方法;
具體的實現步驟:
1.獲取圖片
@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(@DrawableRes int resId) {
super.setImageResource(resId);
mBitmap = getBitmapFromDrawable(getDrawable());
setup();
}
@Override
public void setImageURI(Uri uri) {
super.setImageURI(uri);
mBitmap = getBitmapFromDrawable(getDrawable());
setup();
}
2.方法中調用了setup()方法
if (!mReady) {
mSetupPending = true;
return;
}
因爲mReady默認是false的,所以只會執行前面的這一句就結束了。
3.調用構造方法獲取屬性值
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);
//屬性數組
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);
mBorderOverlay = a.getBoolean(R.styleable.CircleImageView_border_overlay, DEFAULT_BORDER_OVERLAY);
//回收數組
a.recycle();
//調用init()方法
init();
}
4.調用init()方法
private void init() {
super.setScaleType(SCALE_TYPE);
mReady = true;
//執行完setup()方法的前面部分,mSetupPending已經變成了true
if (mSetupPending) {
setup();
mSetupPending = false;
}
}
init()方法的作用只是一用來調節執行順序的,因爲一定要控制先執行構造方法拿到數據之後,然後再去執行setup()方法中的有效部分設置數據。
5.調用setup()方法中的有效部分
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();
//創建一個大小和這個view一樣大的矩形框
mBorderRect.set(0, 0, getWidth(), getHeight());
//計算外邊半徑(畫圓的時候會用到,外邊框的中間處)
mBorderRadius = Math.min((mBorderRect.height() - mBorderWidth) / 2, (mBorderRect.width() - mBorderWidth) / 2);
mDrawableRect.set(mBorderRect);
if (!mBorderOverlay) {
//控制mDrawableRect比mBorderRect邊距空有mBorderWidth
mDrawableRect.inset(mBorderWidth, mBorderWidth);
}
mDrawableRadius = Math.min(mDrawableRect.height() / 2, mDrawableRect.width() / 2);
updateShaderMatrix();
invalidate();
}
6.調用updateShaderMatrix()方法,用來優化繪製的效果
private void updateShaderMatrix() {
float scale;
float dx = 0;
float dy = 0;
mShaderMatrix.set(null);
//比較是按照寬來縮放還是按照長來縮放
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;
}
//mShaderMatrix對象用來對圖片進行變換
mShaderMatrix.setScale(scale, scale);
mShaderMatrix.postTranslate((int) (dx + 0.5f) + mDrawableRect.left, (int) (dy + 0.5f) + mDrawableRect.top);
mBitmapShader.setLocalMatrix(mShaderMatrix);
}
7.執行invalidate()方法,進行繪製頭像
@Override
protected void onDraw(Canvas canvas) {
if (getDrawable() == null) {
return;
}
canvas.drawCircle(getWidth() / 2, getHeight() / 2, mDrawableRadius, mBitmapPaint);
if (mBorderWidth != 0) {
canvas.drawCircle(getWidth() / 2, getHeight() / 2, mBorderRadius, mBorderPaint);
}
}