一个简单又不简单的进度条
2019-10-16 本文已影响0人
GoMoon
前言
要求的效果是这样的
progress_view.gif
粗看之下,好像挺简单的,先画背景、再画进度条、最后画文字,就可以了。
但细看之下,有坑,当文字的颜色和进度条相融时,还要从绿色变成白色,这可不好做。
还好Androd是有解决方案的,就是利用图形学里一个概念——PorterDuff.Mode。
PorterDuff是啥
本着学英语的态度,笔者去翻译了一下这个单词,然而并没有结果,查了资料才发现,这个单词是两个人名的组合:Thomas Porter 和 Tom Duff,这两位大神是研究图形混合的。说到这里大家应该能明白了,PorterDuff.Mode其实就是两种或多种图形混合在一起时的各种模式,这里有16种Mode,看下图(蓝色方块是Src即源图,黄色圆形是Dst即目标图):
proter-duff.png
分析
那么,如何借助PorterDuff来实现我们想要的进度条效果呢?
这里需要绘制四个图层:最底层为灰色背景,其上为绿色进度条,最上面两个图层就要用到PorterDuff。
我们可以选用SrcATop这种混合模式,按照官方解析,在SrcATop模式下,Src图像像素不覆盖Dst图像像素的部分直接丢弃 ,Src图像像素剩余部分绘制在Dst图像像素之上。所以,我们可以让绿色的文字为Dst,然后最上层画一层白色的进度条,不相交时不起作用,相交时只显示为Src的颜色,文字也就变成了白色。
代码
代码比较简单,直接贴在下面吧。
public class ProgressView extends View {
/**
* 圆角弧度
*/
private static final int RADIUS = 60;
private Paint mPaint = new Paint();
private int mProgress;
public ProgressView(Context context) {
this(context, null);
}
public ProgressView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void setProgress(int progress) {
if (progress >= 0 && progress <= 100) {
mProgress = progress;
invalidate();
}
}
@SuppressLint("DrawAllocation")
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int width = getMeasuredWidth();
int height = getMeasuredHeight();
//画底部背景
mPaint.setColor(Color.LTGRAY);
canvas.drawRoundRect( new RectF(0, 0, width, height), RADIUS, RADIUS, mPaint);
//画进度条
mPaint.setColor(getResources().getColor(R.color.colorPrimary));
canvas.drawRoundRect(new RectF(0, 0, width * ((float) mProgress / 100), height), RADIUS, RADIUS, mPaint);
//画文字图层
mPaint.setColor(getResources().getColor(R.color.colorPrimary));
mPaint.setTextSize(sp2px(getContext(), 20));
mPaint.setTypeface(Typeface.DEFAULT_BOLD);
mPaint.setTextAlign(Paint.Align.CENTER);
Bitmap textBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas textCavas = new Canvas(textBitmap);
String content = mProgress + "%";
float textY = height / 2.0f - (mPaint.getFontMetricsInt().descent / 2.0f + mPaint.getFontMetricsInt().ascent / 2.0f);
textCavas.drawText(content, width / 2.0f, textY, mPaint);
//画最上层的白色图层,未相交时不会显示出来
mPaint.setColor(Color.WHITE);
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP));
textCavas.drawRoundRect(new RectF(0, 0, width * ((float) mProgress / 100), height), RADIUS, RADIUS, mPaint);
//画结合后的图层
canvas.drawBitmap(textBitmap, 0, 0, mPaint);
mPaint.setXfermode(null);
textBitmap.recycle();
}
public static int sp2px(Context context, float spValue) {
final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
return (int) (spValue * fontScale + 0.5f);
}
}