项目中要用到一个聊天头像的控件,跟钉钉的类似的这种,于是乎自己就想撸一个出来,虽然网上也有现成的,但别人的总是需要修修改改,还不如自己从头开始,也方便后续维护,直接看效果图:
怎么样,还不错吧,哈哈~自我感觉良好,唯一不同的是钉钉两个人和三个人是左右横排的,我们的是竖排的。下面我们来简单分析一下实现原理:
首先抛开图片不说,先看底色+昵称,底色就很简单了,根据用户的唯一id,来指定一个规则,进行显示不同的颜色,这个看产品具体来定了,整体就采用canvas的drawCircle、drawArc、drawText等方法来绘制了,看看应该是挺容易的,难点就是具体的坐标计算,只要算准位置,这个自定义view就是分分钟了,哈哈~再来分析一下有图片的情况,图片的绘制当然就是drawBitmap了,另外再加上Xfermode这个强大的效果处理(请参考我上一篇文章—— Canvas的drawBitmap以及Paint的PorterDuffXfermode使用心得),也是分分钟实现我们想要的不规则图片绘制。好了,原理分析完了,下面直接上代码,核心部分就是我们的onDraw方法了:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (count == 0) {
return;
}
//必须先设置字体大小
if (count == 1) {
if (textSize1 == 0) {
return;
}
} else {
if (textSizeOther == 0) {
return;
}
}
if (count == 1) {
//一个人(两个汉字)
deal1(canvas);
} else if (count == 2) {
//两个人(上下各两个字)
deal2(canvas);
} else if (count == 3) {
//三个人则上面一个(两个汉字),下面两个(一个汉字)
deal3(canvas);
} else {
//4人以上群聊取前四个人的头像拼接(一个汉字)从左到右,从上到下
deal4(canvas);
}
}
可以看到,这里我们分为四种情况来考虑,每种情况分别处理,首先来看deal1:
private void deal1(final Canvas canvas) {
if (checkBitmap(list.get(0).bitmap)) {
//新建Canvas层级
int saveCount = canvas.saveLayer(0f, 0f, width, height, null, Canvas.ALL_SAVE_FLAG);
//绘制底图——原来canvas上的内容dst
canvas.drawBitmap(list.get(0).bitmap, 0, 0, mPaint);
//生成圆形图片蒙版src
Bitmap mask = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas canvasMask = new Canvas(mask);
RectF rf = new RectF(0f, 0f, width, height);
canvasMask.drawOval(rf, mPaint);
//设置交叉模式,绘制蒙版
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
canvas.drawBitmap(mask, 0, 0, mPaint);
//还原
mPaint.setXfermode(null);
//将如上画的层覆盖到原有层级上
canvas.restoreToCount(saveCount);
} else {
mPaint.setColor(getBgColor(list.get(0).id));
canvas.drawCircle(width / 2f, height / 2f, width / 2f, mPaint);
//画名字
textPaint.setTextSize(textSize1);
String name = list.get(0).presentName;
name = name.length() > 2 ? name.substring(name.length() - 2, name.length()) : name;
if (textColor != 0) {
textPaint.setColor(textColor);
}
//baseLine计算参考:http://blog.csdn.net/harvic880925/article/details/50423762
Paint.FontMetrics fontMetrics = textPaint.getFontMetrics();
float baseline = height / 2f + (fontMetrics.bottom - fontMetrics.top) / 2f - fontMetrics.bottom;
float x = (width - textPaint.measureText(name)) / 2f;
canvas.drawText(name, x, baseline, textPaint);
}
}
这里我们分为有没有图片来分别处理,有头像就绘制用户头像,没有则昵称+底色,drawText方法呢,主要就是涉及到一个baseline的计算,这里大家可以参考这位大神的分析:http://blog.csdn.net/harvic880925/article/details/50423762,讲解的是非常清楚。这段代码其他就没有什么难点了,都是canvas的应用绘制和坐标计算。
同理,当两个用户的时候,头像的处理也是类似,只不过中间加了一个空隙,这个在绘制的时候需要计算进去。另外就是我们在绘制图片的时候,为了让图片居中显示,我们就采用上篇讲过的只绘制bitmap的中间区域,而不是从图片左上角开始绘制,这样就会好很多了。另外就是三个,大于等于四个用户的时候,昵称的绘制如果按照标准的居中,看起来并没有在中间,这是因为扇形的视觉缘故,所以这里我增加了一个偏移量:
/**
* x方向的偏移量,用于修正位置
*/
private float xDelta;
/**
* y方向的偏移量,用于修正位置
*/
private float yDelta;
这两个用来修正文本绘制的视觉位置。
另外关于昵称文本大小颜色等也加入了一些自定义变量去控制,最终调用如下:
mHiHeaderImageView.setTextSize1(20f).setTextSizeOther(15f).setList(list);
至于后面几种情况,这里就不贴出具体代码了,基本上跟第一种是一样的,无非就是多了坐标的处理而已,至此我们的仿钉钉头像这个控件就完成了,怎么样,简单吧!
同样最后给出源码,感兴趣的同学可以自己下载查看,有疑问的可以在下方留言,谢谢大家~