android仿ios圓形頭像
一直都是光說不練,現在好了,說是會說,但是正真要寫的時候慫了。今天把如何實現圓形頭像的過程記錄下來,提醒自己:要多寫多練多分析。
分析
如何實現一個帶邊框的圓形頭像控件
思路如下:
- 在Canvas上畫一個背景圓
- 實現一個圓形的圖片
- 在背景圓上覆蓋一個半徑較小的圓形圖片
大體效果如下:
在圖片中,寫的兩個字不是很清楚,其中背代表的是背景圓,圖代表的是圓形的圖片
需要哪些條件
- 背景圓的顏色
- 背景圓超出部分的寬度
邊擼邊分析
自定義屬性
自定義屬性需要在res/value文件夾中創建一個xml文件,在該文件中定義一個的節點,在該節點中定義兩個自定義屬性:
<resources>
<!--爲CircleImageView-->
<declare-styleable name="CircleImageView">
<!--外圓顏色-->
<attr name="frameColor" format="color"></attr>
<!--外圓超出部分寬度-->
<attr name="frameWidth" format="dimension"></attr>
</declare-styleable>
</resources>
至於在<attr>
節點中的format
可供選擇的內容,我就不介紹了,可以參考這篇blog(android自定義view的自定義屬性)。
其中的CircleImageView
是我想要實現圓形頭像控件的類。該類我是繼承了ImageView。在繼承了ImageView後需要實現三個構造方法,在構造方法中得到自定義屬性的值。
public CircleImageView(Context context) {
super(context);
initAttr(context, null);
}
public CircleImageView(Context context, AttributeSet attrs) {
super(context, attrs);
initAttr(context, attrs);
}
public CircleImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initAttr(context, attrs);
}
在initAttr
方法取得自定義屬性的值,並且進行一些初始化操作:
public void initAttr(Context context, AttributeSet attrs) {
if (attrs != null) {
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.CircleImageView);
int len = array.getIndexCount();//得到定義的屬性的長度
for (int i = 0; i < len; i++) {
int attr = array.getIndex(i);//得到所有的屬性
switch (attr) {
case R.styleable.CircleImageView_frameColor: //得到自定義的圓形邊框顏色
mFrameColor = array.getColor(attr, Color.GRAY);
break;
case R.styleable.CircleImageView_frameWidth://得到自定義的圓形邊框寬度
mFrameWidth = (int) array.getDimension(attr, 3);
break;
}
}
}
//設置Paint參數
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setColor(mFrameColor);
}
如果對自定義操作不是很瞭解的話,可以看鴻洋大神的關於自定義View的blog(Android自定義控件之起步)
進行測量
當自定義屬性獲取完成,並且畫筆對象已經初始化完成之後,進行的操作是進行測量操作,需要重寫onMeasure()
方法。
在onMeasure()方法中,我們需要完成獲取控件的大小:
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = measureWidth(widthMeasureSpec);
int height = measureHeight(heightMeasureSpec);
bitmapWidth = width - 2 * mFrameWidth;
bitmapHeight = height - 2 * mFrameWidth;
this.setMeasuredDimension(width, height);
}
其中measureWidth()
和measureHeight()
是這樣實現的:
private int measureHeight(int heightMeasureSpec) {
int height = 0;
int size = MeasureSpec.getSize(heightMeasureSpec);
int mode = MeasureSpec.getMode(heightMeasureSpec);
if (mode == MeasureSpec.EXACTLY) {
height = size;
} else {
height = 50;
}
return height;
}
private int measureWidth(int widthMeasureSpec) {
int width = 0;
//得到寬的值和模式
int size = MeasureSpec.getSize(widthMeasureSpec);
int mode = MeasureSpec.getMode(widthMeasureSpec);
if (mode == MeasureSpec.EXACTLY) {
width = size;
} else {
width = 50;
}
return width;
}
這樣實現的原因是在於 widthMeasureSpec
和heightMeasureSpec
是一個32位的int值,其中高兩位代表SpecMode,後30位代表SpecSize。至於有哪些模式,這些模式代表的含義在鴻洋大神的這篇中也有提到(Android 自定義View (一))。
繪製
分析
當完成測量之後就需要進行繪製了。
在繪製前我先進行一個簡單的分析:
畫的比較醜。就這樣看看吧
在圖上有個兩個矩形,外面的矩形代表的是控件的大小,內部的矩形代表的是所要畫的圓形頭像的區域,兩個矩形之間的間隔大小爲我自定義的寬。
由於使用的圖片不一定是正方形,因此,我們需要在寬和高之間選擇一個較小的值。然後根據設置的背景圖片來創建一個指定大小的新圖片。
protected void onDraw(Canvas canvas) {
//畫邊框圓
int min = Math.min(bitmapHeight, bitmapWidth);//得到bitmapHeight和bitmapWidth中較小的值
BitmapDrawable background = (BitmapDrawable) getDrawable();//得到背景圖片
Bitmap image = Bitmap.createScaledBitmap(background.getBitmap(), min, min, false);//創建一個縮略圖
canvas.drawCircle((min + 2 * mFrameWidth) / 2, (min + 2 * mFrameWidth) / 2, (min + mFrameWidth) / 2, mPaint);//畫圓邊框
canvas.drawBitmap(drawCircleBitmap(image, min), mFrameWidth, mFrameWidth, null);//畫圖片
}
在得到該新圖片後在畫布上繪製一個半徑較大的圓。在繪製完成背景圓後就可以在該畫布上繪製一個我們自定義的圓形圖片。
繪製圓形圖像
先上代碼:
private Bitmap drawCircleBitmap(Bitmap background, int min) {
Bitmap circle = Bitmap.createBitmap(min, min, Bitmap.Config.ARGB_8888);//新建一個圖片
Canvas canvas = new Canvas(circle);
Paint paint = new Paint();
paint.setAntiAlias(true);
canvas.drawCircle(min / 2, min / 2, min / 2, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(background, 0, 0, paint);
return circle;
}
首先要創建一個指定大小和品質的Bitmap。然後利用該Bitmap創建一個畫布。在該畫布上先畫一個圓。然後利用畫筆的繪製效果。進行兩個圖像的合併。完成一個圓形的效果。
結語
到此,完成了圓形頭像控件的簡單繪製,在實現的過程中,有很多因素沒有進行考慮。完成的非常粗燥。
源碼我放在了github上,地址:自定義CircleView