一个简单又不简单的进度条

前言

要求的效果是这样的


一个简单又不简单的进度条_第1张图片
progress_view.gif

粗看之下,好像挺简单的,先画背景、再画进度条、最后画文字,就可以了。
但细看之下,有坑,当文字的颜色和进度条相融时,还要从绿色变成白色,这可不好做。
还好Androd是有解决方案的,就是利用图形学里一个概念——PorterDuff.Mode。

PorterDuff是啥

本着学英语的态度,笔者去翻译了一下这个单词,然而并没有结果,查了资料才发现,这个单词是两个人名的组合:Thomas Porter 和 Tom Duff,这两位大神是研究图形混合的。说到这里大家应该能明白了,PorterDuff.Mode其实就是两种或多种图形混合在一起时的各种模式,这里有16种Mode,看下图(蓝色方块是Src即源图,黄色圆形是Dst即目标图):


一个简单又不简单的进度条_第2张图片
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);
    }
}

你可能感兴趣的:(一个简单又不简单的进度条)