android仿ios圆形头像
一直都是光说不练,现在好了,说是会说,但是正真要写的时候怂了。今天把如何实现圆形头像的过程记录下来,提醒自己:要多写多练多分析。
分析
如何实现一个带边框的圆形头像控件
思路如下:
- 在Canvas上画一个背景圆
- 实现一个圆形的图片
- 在背景圆上覆盖一个半径较小的圆形图片
大体效果如下:
![](https://oalmlvop6.qnssl.com/blog/android/CircleView/android_circleImage_think.jpg)
在图片中,写的两个字不是很清楚,其中背代表的是背景圆,图代表的是圆形的图片
需要哪些条件
- 背景圆的颜色
- 背景圆超出部分的宽度
边撸边分析
自定义属性
自定义属性需要在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 (一))。
绘制
分析
当完成测量之后就需要进行绘制了。
在绘制前我先进行一个简单的分析:
![](https://oalmlvop6.qnssl.com/blog/android/CircleView/android_circle_analyse.jpg)
画的比较丑。就这样看看吧
在图上有个两个矩形,外面的矩形代表的是控件的大小,内部的矩形代表的是所要画的圆形头像的区域,两个矩形之间的间隔大小为我自定义的宽。
由于使用的图片不一定是正方形,因此,我们需要在宽和高之间选择一个较小的值。然后根据设置的背景图片来创建一个指定大小的新图片。
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