可以显示进度的带边框的圆形ImageView(Xfermodes实现)
简介
这是一个可以显示进度的带边框的圆形ImageView,在现在的Android APP为了取得更好的显示效果,大都将一些图片显示为圆形,网上也有很多开源的组件,Android最新的SDK中也包含了这个组件,但是其功能都比较单一,我写的这个ImageView可以自定义宽度和颜色的边框,并可以动态的显示进度,适用的场合包括上传头像,图片下载等等需要加载的地方,而且源码已经开放,大家可以根据自己的需要把图片修改成自己喜欢形状,非常方便。
源码:https://github.com/dayiming/CircleProgressImageView
思路讲解
乍一看可能觉的写这个控件很麻烦,但是仔细考虑一下你会发现这个并不难,制作的方法类似于PS制图时经常使用的布尔运算,通过图片透明处的相加减即可完成。在android上如何实现布尔运算呢?你只需要了解Xfermodes这个类就可以了。
简单一点来说,当你在图片1上绘制图片2时,你可以给画笔设置上如下图所示的各种种属性,此时图片1就是Dst,图片2局势Src,就可以实现如图展示的图片之间进行PS中布尔运算的效果。
代码讲解
首先我们需要创建一个CircleProgressImage类继承自ImageView类。这里为什么我们不自己写一个类去直接绘图呢?因为像网上支持的图片加载框架ImageLoader,Picasa等等大都只支持将ImageView或其子类传入其中作为参数,我们的控件继承自ImageView也是为了以后在项目中使用的方便。
public class CircleProgressImage extends ImageView {
}
接着我们考虑到我们需要绘制两个位图,第一个位图是圆形,用来和原图进行布尔运算对原图进行切割,第二个位图用来覆盖在原图上显示进度,所以我们定义了下面这些变量用于绘制位图和显示进度的文字
private Paint slipPaint = null; //用于切割图片的形状画笔
private Paint maskPaint = null; //绘制半透明遮盖物的画笔
private Paint textPaint = null; //绘制文字的画笔
private Bitmap shape = null; //用于绘制圆形的位图
private Bitmap mask = null; //用于绘制上层覆盖层的位图
private int progress = 0; //进度
private boolean isNewMask = true; //是否需要重新绘制遮盖物的标志,降低内存消耗
初始化画笔等变量
slipPaint = new Paint();
//设置这个属性,原图会被切割成用这个画笔画上去的形状
slipPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
slipPaint.setFilterBitmap(false);
maskPaint = new Paint();
//设置这个属性,这个画笔画上去的形状会被切割成原图的形状,且原图和这个画笔画的图都会保存
maskPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP));
maskPaint.setFilterBitmap(false);
textPaint = new Paint();
textPaint.setColor(textColor);
textPaint.setTextSize(textSize); //设置20sp大小的文字
下面就要开始绘图了
@Override
protected void onDraw(Canvas canvas) {
//首先获取用户传入ImageView的图片资源
Drawable drawable = getDrawable();
if (drawable == null) { //如果这个图片资源是null的话则直接返回
return;
}
try {
//绘图前先保存一遍画布,类似于数据库的“事务”
int layer = canvas.saveLayer(0, 0, getWidth(), getHeight(), null, Canvas.ALL_SAVE_FLAG);
//给图片资源设置宽高
drawable.setBounds(0, 0, getWidth(), getHeight());
//将原先的Canvas作为图片资源的画布,在其上对退片资源进行绘制
drawable.draw(canvas);
//如果形状位图为空或者已经被回收则进行重绘,防止多次绘制重复位图浪费资源
if (shape == null || shape.isRecycled()) {
shape = getShape(getWidth(), getHeight());
}
//用设置了DST_IN的画笔将形状画上去,原图此时被切割成圆形
canvas.drawBitmap(shape, 0, 0, slipPaint);
//画圆环
Paint ringPaint = new Paint();
ringPaint.setAntiAlias(true);
ringPaint.setStyle(Paint.Style.STROKE);
ringPaint.setColor(borderColor);
ringPaint.setStrokeWidth(borderWidth);
canvas.drawCircle(getWidth() / 2, getHeight() / 2, (getWidth() - borderWidth) / 2, ringPaint);
//画透明遮盖物,如果是新设置了进度或者遮盖物位图为空或者已经被回收则进行重新绘制
if (isNewMask || mask == null || mask.isRecycled()) {
mask = getMask(getWidth(), getHeight());
isNewMask = false;
}
//通过设置SRC_ATOP属性的画笔将遮盖物画上去,原图和遮盖物重合切保持原图形状
canvas.drawBitmap(mask, 0, 0, maskPaint);
//画文字
String text = progress + "%";
//获取文字大小,已将文字居中绘制
Rect textRect = new Rect();
textPaint.getTextBounds(text, 0, text.length(), textRect);
canvas.drawText(text, (getWidth() - textRect.width()) / 2, (getHeight() + textRect.height()) / 2, textPaint);
//绘制完成将绘制的内容放置上去
canvas.restoreToCount(layer);
} catch (Exception e) {
e.printStackTrace();
}
}
private Bitmap getShape(int width, int height) {
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
RectF localRectF = new RectF(0, 0, width, height);
Paint paint = new Paint();
paint.setAntiAlias(true); //去锯齿
canvas.drawOval(localRectF, paint);
return bitmap;
}
private Bitmap getMask(int width, int height) {
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
Paint paint = new Paint();
paint.setAntiAlias(true); //去锯齿
paint.setColor(borderColor);
//设置遮盖物的半透明度
paint.setAlpha(160);
//根据进度设置遮盖物的高度
if (this.progress <= 100) {
canvas.drawRect(0, ((float) height / 100) * (100 - progress), width, height, paint);
} else {
canvas.drawRect(0, 0, width, height, paint);
}
return bitmap;
}
如果大家想要修改形状的话只需要将shape这个位图设置成你需要的形状即可,如果想让进度改变形状,类似于波浪的效果的话,只需要将mask这个位图的上沿用sin曲线绘制出来就可以了,熟练使用布尔运算可以做出更多精彩的效果。
看到这里相信大家也已经懂了这个组件具体是如何实现的了,大家可以自己重写一个使用或者下载我上传的源代码直接使用,我也将里面可以抽出来的属性抽取了出来,使用起来非常灵活。也希望大家对这个有什么看法或者是建议给我评论,我一定吸取您的建议。