自定义ProgressBar

说到自定义View,相信很多同学都是从ProgressBar开始的,主要是因为它写起来会比较简单,然后可以操作一遍自定义View的流程。刚好这两天有个朋友叫帮忙写个ProgressBar,虽然之前写了至少3个了,但也还是答应了下来,他给出的效果图如下:


效果.png

先上最后的效果如下:


HxjProgressBar.gif

这里挑几个主要方法说明一下,最后会贴出完整代码。
onMeasure()计算出view的大小,同时计算出几个关键位置

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        //这里就取父容器给定的默认大小
        int widthSize = getDefaultSize(0, widthMeasureSpec);
        int heightSize = getDefaultSize(0, heightMeasureSpec);

        /**
         * 获取真实用于画view的正方形大小,这里需要为边界处齿轮的圆边预留出gearStrokeWidth的宽度
         * 同时预留出齿轮动画需要占用的宽度gearAnimLength
         */
        int realWidth = (int) (widthSize - getPaddingLeft() - getPaddingRight() - gearStrokeWidth * 2 - 2 * gearAnimLength);
        int realHeight = (int) (heightSize - getPaddingTop() - getPaddingBottom() - gearStrokeWidth * 2 - 2 * gearAnimLength);

        //我们的真实用来画view的范围取一个正方形,这里正方形的边长取可用长和宽的最小值
        realSize = realWidth >= realHeight ? realHeight : realWidth;
        //这里算出整个用于绘画的正方形的中心点
        centerPoint = new Point((int) (getPaddingLeft() + realWidth / 2 + gearStrokeWidth + gearAnimLength), (int) (getPaddingTop() + realHeight / 2 + gearStrokeWidth + gearAnimLength));

        //计算出外部齿轮圆的半径,以齿轮中间到圆心计算
        outerCircleRadius = (realSize - gearWidth) / 2;
        if (outerCircleRadius < 0) {
            outerCircleRadius = 0;
        }
        //计算出内圆的半径
        innerCircleRadius = realSize / 2 - gearWidth - circlePadding;
        if (innerCircleRadius < 0) {
            innerCircleRadius = 0;
        }
        //设置View大小
        setMeasuredDimension(widthSize, heightSize);
    }

onDraw()画View,这里分四块来画,drawInnerCircle(canvas) 画内圆、drawGear(canvas)画齿轮、drawProgressDot(canvas)画内圆上的进度球、drawProgressText(canvas)画进度文字。

protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        drawInnerCircle(canvas);
        drawGear(canvas);
        drawProgressDot(canvas);
        drawProgressText(canvas);
    }

以上都是往画板上画对应图形的方法,只有drawGear(canvas)中会涉及到动画时会稍微复杂一些,这些齿轮本质都是线条,只是根据动画进度在长度上会有一些计算。
drawGear(canvas)

    private void drawGear(Canvas canvas) {
        float atom = (float) (Math.PI * 5 / 180);
        gearStartAngle = (1 - gearRange) * Math.PI;
        gearEndAngle = (1 + gearRange) * Math.PI;
        if (gearEndAngle == 2 * Math.PI) {
            gearEndAngle -= atom;
        }

        double diffAngle = gearEndAngle - gearStartAngle;
        float startX, startY, stopX, stopY;

        /**
         * 这里找到drawProgress附近的9根线条
         * 以下为每5度一条线,总线条根数为72根
         */
        int totalLineCount = 72;
        //最靠近当前drawProgress的线条的位置
        pLinePoint = (int) (totalLineCount - Math.ceil(totalLineCount * drawProgress / maxProgress));

        int point = (int) (gearStartAngle / atom);
        for (double angle = gearStartAngle; angle < gearEndAngle; angle = angle + atom) {
            /**
             * 这里计算颜色有些诡异,由于我们计算角度是从底下逆时针开始计算,
             * 而我们的仪表盘进度顺时针开始计算,所以这里是gearEndAngle-angle,而不是gearStartAngle+angle
             */
            mGearPaint.setColor(transitColor((float) ((gearEndAngle - angle) / diffAngle)));
            float startR = outerCircleRadius;
            float endR = outerCircleRadius;
            if (animProgress != 1) {
                if (point == gearPoints[pLinePoint + 36]) {
                    startR = startR + 5 * gearAnimGradient / 2;
                    endR = endR + gearAnimLength;
                } else if (point == gearPoints[pLinePoint + 35] || point == gearPoints[pLinePoint + 37]) {
                    startR = startR + 4 * gearAnimGradient / 2;
                    endR = endR + gearAnimLength - gearAnimGradient;
                } else if (point == gearPoints[pLinePoint + 34] || point == gearPoints[pLinePoint + 38]) {
                    startR = startR + 3 * gearAnimGradient / 2;
                    endR = endR + gearAnimLength - 2 * gearAnimGradient;
                } else if (point == gearPoints[pLinePoint + 33] || point == gearPoints[pLinePoint + 39]) {
                    startR = startR + 2 * gearAnimGradient / 2;
                    endR = endR + gearAnimLength - 3 * gearAnimGradient;
                } else if (point == gearPoints[pLinePoint + 32] || point == gearPoints[pLinePoint + 40]) {
                    startR = startR + gearAnimGradient / 2;
                    endR = endR + gearAnimLength - 4 * gearAnimGradient;
                }
            }

            startX = (float) (centerPoint.x + Math.sin(angle) * (startR - gearWidth / 2));
            startY = (float) (centerPoint.y + Math.cos(angle) * (startR - gearWidth / 2));
            stopX = (float) (centerPoint.x + Math.sin(angle) * (endR + gearWidth / 2));
            stopY = (float) (centerPoint.y + Math.cos(angle) * (endR + gearWidth / 2));
            canvas.drawLine(startX, startY, stopX, stopY, mGearPaint);
            point++;
        }
    }

其他的细节也不好用代码描述了,下面贴出完整代码:
HxjProgressBar.java

public class HxjProgressBar extends View {
    private static final int DEFAULT_PROGRESS_TEXT_SIZE = 30;//sp
    private static final int DEFAULT_TIPS_SIZE = 14;//sp
    private static final int DEFAULT_CIRCLE_PADDING = 14;//sp
    private static final int DEFAULT_GEAR_WIDTH = 8;//dp
    private static final int DEFAULT_INNER_STROKE_WIDTH = 1;//dp
    private static final int DEFAULT_GEAR_STROKE_WIDTH = 2;//dp
    private static final int DEFAULT_PROGRESS_DOT_RADIUS = 2;//dp

    private static final long PROGRESS_ANIM_DURATION = 1200L;
    private static final long MIN_PROGRESS_ANIM_DURATION = 400L;


    /**
     * 中间进度文字大小
     */
    private float progressTextSize;

    /**
     * 提示文字大小
     */
    private float tipsSize;

    /**
     * 提示文字颜色
     */
    private int tipsColor;

    /**
     * 内圆半径
     */
    private float innerCircleRadius;

    /**
     * 外圆半径
     */
    private float outerCircleRadius;

    /**
     * 内外圆空白处的宽度
     */
    private float circlePadding;

    /**
     * 内圆颜色
     */
    private int innerCircleColor;

    /**
     * 起始颜色
     */
    private int selectStartColor;

    /**
     * 结束颜色
     */
    private int selectEndColor;

    /**
     * 外圈齿轮宽度
     */
    private float gearWidth;

    /**
     * 内圆画笔宽度
     */
    private float innerStrokeWidth;

    /**
     * 齿轮画笔宽度
     */
    private float gearStrokeWidth;

    /**
     * 外部齿轮的范围,取值:0-1,默认为1,即一个满圆,0.5为上半圆
     */
    private float gearRange = 1;

    /**
     * 最大进度
     */
    private float maxProgress;


    /**
     * 真实画View的区域理想为正方形
     * 该正方形的大小
     */
    private float realSize;

    /**
     * view用于绘画的中心点
     */
    private Point centerPoint;

    /**
     * 齿轮起始角度
     */
    private double gearStartAngle;

    /**
     * 齿轮结束角度
     */
    private double gearEndAngle;

    /**
     * 进度条上的小圆球半径
     */
    private float progressDotRadius;

    /**
     * 进度单位
     */
    private String progressUnit;

    /**
     * 辅助说明
     */
    private String tips;

    /**
     * 进度小数位数
     */
    private int decimals;

    /**
     * 当前进度
     */
    private float mProgress;

    /**
     * 当前动画进度
     */
    private float drawProgress;

    private float gearAnimLength;
    private float gearAnimGradient;

    private Paint mInnerCirclePaint;
    private Paint mGearPaint;
    private Paint mProgressDotPaint;
    private Paint mProgressPaint;
    private Paint mUnitPaint;
    private ValueAnimator progressAnimator;

    private String progressText;
    private float animProgress = 1;

    private int[] gearPoints = new int[144];
    private int pLinePoint;

    public HxjProgressBar(Context context) {
        this(context, null);
    }

    public HxjProgressBar(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public HxjProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initAttrs(attrs);
        initPaint();
        initGearPoints();
    }

    /**
     * 这里初始化一个齿轮位置数组,附带左右边界36个位置的齿轮位置
     * 齿轮的总数为72根,在这里为了处理齿轮边界动画,在数组中左右各加36个齿轮位置
     */
    private void initGearPoints() {
        for (int i = 0; i < 36; i++) {
            gearPoints[i] = 36 + i;
            gearPoints[108 + i] = i;
        }
        for (int i = 0; i < 72; i++) {
            gearPoints[i + 36] = i;
        }
    }

    private void initPaint() {
        mInnerCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG);//抗锯齿
        mInnerCirclePaint.setAntiAlias(true);//防抖动
        mInnerCirclePaint.setColor(innerCircleColor);
        mInnerCirclePaint.setStrokeWidth(innerStrokeWidth);
        mInnerCirclePaint.setStyle(Paint.Style.STROKE);

        mGearPaint = new Paint(Paint.ANTI_ALIAS_FLAG);//抗锯齿
        mGearPaint.setAntiAlias(true);//防抖动
        mGearPaint.setStrokeCap(Paint.Cap.ROUND);//设置画笔末端样式
        mGearPaint.setStrokeWidth(gearStrokeWidth);

        mProgressDotPaint = new Paint(Paint.ANTI_ALIAS_FLAG);//抗锯齿
        mProgressDotPaint.setAntiAlias(true);//防抖动
        mProgressDotPaint.setColor(selectStartColor);
        mProgressDotPaint.setStyle(Paint.Style.FILL);

        mProgressPaint = new Paint(Paint.ANTI_ALIAS_FLAG);//抗锯齿
        mProgressPaint.setAntiAlias(true);//防抖动

        mUnitPaint = new Paint(Paint.ANTI_ALIAS_FLAG);//抗锯齿
        mUnitPaint.setAntiAlias(true);//防抖动
    }


    private void initAttrs(AttributeSet attrs) {
        Context context = getContext();

        float scaledDensity = getResources().getDisplayMetrics().scaledDensity;
        float density = getResources().getDisplayMetrics().density;
        TypedValue typedValue = new TypedValue();
        context.getTheme().resolveAttribute(R.attr.colorAccent, typedValue, true);
        int accentColor = typedValue.data;
        context.getTheme().resolveAttribute(R.attr.colorPrimary, typedValue, true);
        int primaryColor = typedValue.data;
        context.getTheme().resolveAttribute(R.attr.colorPrimaryDark, typedValue, true);
        int primaryDarkColor = typedValue.data;


        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.HxjProgressBar);
        progressTextSize = typedArray.getDimension(R.styleable.HxjProgressBar_progressTextSize, (int) (DEFAULT_PROGRESS_TEXT_SIZE * scaledDensity + 0.5));
        tipsSize = typedArray.getDimension(R.styleable.HxjProgressBar_tipsSize, (int) (DEFAULT_TIPS_SIZE * scaledDensity + 0.5));
        circlePadding = typedArray.getDimension(R.styleable.HxjProgressBar_circlePadding, (int) (DEFAULT_CIRCLE_PADDING * density + 0.5));

        gearWidth = typedArray.getDimension(R.styleable.HxjProgressBar_gearWidth, (int) (DEFAULT_GEAR_WIDTH * density + 0.5));
        gearAnimLength = gearWidth / 2;
        gearAnimGradient = gearAnimLength / 7F;
        innerCircleColor = typedArray.getColor(R.styleable.HxjProgressBar_innerCircleColor, accentColor);
        selectStartColor = typedArray.getColor(R.styleable.HxjProgressBar_selectStartColor, primaryColor);
        tipsColor = typedArray.getColor(R.styleable.HxjProgressBar_tipsColor, accentColor);
        selectEndColor = typedArray.getColor(R.styleable.HxjProgressBar_selectEndColor, primaryDarkColor);
        innerStrokeWidth = typedArray.getDimension(R.styleable.HxjProgressBar_innerStrokeWidth, (int) (DEFAULT_INNER_STROKE_WIDTH * density + 0.5));
        gearStrokeWidth = typedArray.getDimension(R.styleable.HxjProgressBar_gearStrokeWidth, (int) (DEFAULT_GEAR_STROKE_WIDTH * density + 0.5));
        progressDotRadius = typedArray.getDimension(R.styleable.HxjProgressBar_progressDotRadius, (int) (DEFAULT_PROGRESS_DOT_RADIUS * density + 0.5));
        maxProgress = typedArray.getFloat(R.styleable.HxjProgressBar_maxProgress, 100F);
        mProgress = typedArray.getFloat(R.styleable.HxjProgressBar_progress, 0F);
        progressUnit = typedArray.getString(R.styleable.HxjProgressBar_progressUnit);
        decimals = typedArray.getInteger(R.styleable.HxjProgressBar_decimals, 0);
        tips = typedArray.getString(R.styleable.HxjProgressBar_tips);
        drawProgress = mProgress;
        formatProgress(mProgress);
        gearRange = typedArray.getFloat(R.styleable.HxjProgressBar_gearRange, 1);
        gearRange = correctGearRange(gearRange);
        typedArray.recycle();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        //这里就取父容器给定的默认大小
        int widthSize = getDefaultSize(0, widthMeasureSpec);
        int heightSize = getDefaultSize(0, heightMeasureSpec);

        /**
         * 获取真实用于画view的正方形大小,这里需要为边界处齿轮的圆边预留出gearStrokeWidth的宽度
         * 同时预留出齿轮动画需要占用的宽度gearAnimLength
         */
        int realWidth = (int) (widthSize - getPaddingLeft() - getPaddingRight() - gearStrokeWidth * 2 - 2 * gearAnimLength);
        int realHeight = (int) (heightSize - getPaddingTop() - getPaddingBottom() - gearStrokeWidth * 2 - 2 * gearAnimLength);

        //我们的真实用来画view的范围取一个正方形,这里正方形的边长取可用长和宽的最小值
        realSize = realWidth >= realHeight ? realHeight : realWidth;
        //这里算出整个用于绘画的正方形的中心点
        centerPoint = new Point((int) (getPaddingLeft() + realWidth / 2 + gearStrokeWidth + gearAnimLength), (int) (getPaddingTop() + realHeight / 2 + gearStrokeWidth + gearAnimLength));

        //计算出外部齿轮圆的半径,以齿轮中间到圆心计算
        outerCircleRadius = (realSize - gearWidth) / 2;
        if (outerCircleRadius < 0) {
            outerCircleRadius = 0;
        }
        //计算出内圆的半径
        innerCircleRadius = realSize / 2 - gearWidth - circlePadding;
        if (innerCircleRadius < 0) {
            innerCircleRadius = 0;
        }
        //设置View大小
        setMeasuredDimension(widthSize, heightSize);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        drawInnerCircle(canvas);
        drawGear(canvas);
        drawProgressDot(canvas);
        drawProgressText(canvas);
    }

    /**
     * 画进度文字
     * @param canvas
     */
    private void drawProgressText(Canvas canvas) {
        if (animProgress <= 0.5) {
            mProgressPaint.setTextSize((1 - 0.5F * animProgress) * progressTextSize);
        } else {
            mProgressPaint.setTextSize((0.75F + 0.25F * animProgress) * progressTextSize);
        }

        mProgressPaint.setTextAlign(Paint.Align.CENTER);
        int progressWidth = (int) mProgressPaint.measureText(progressText);

        Paint.FontMetrics fontMetrics = mProgressPaint.getFontMetrics();
        float top = fontMetrics.top;//为基线到字体上边框的距离,即上图中的top
        float bottom = fontMetrics.bottom;//为基线到字体下边框的距离,即上图中的bottom

        mUnitPaint.setTextSize(progressTextSize / 2);
        mUnitPaint.setTextAlign(Paint.Align.LEFT);
        int unitWidth = (int) mUnitPaint.measureText(progressUnit);

        //添加渐变shader
        LinearGradient gradient = new LinearGradient(centerPoint.x - progressWidth / 2.0F,
                centerPoint.y,
                centerPoint.x + progressWidth / 2.0F,
                centerPoint.y,
                selectStartColor,
                selectEndColor,
                Shader.TileMode.MIRROR);
        mProgressPaint.setShader(gradient);

        float baseY;
        //这里在计算进度text的绘制高度时考虑是否需要绘制tips,不需要时完全居中,需要时则以centerY为baseY略微偏上
        if (!TextUtils.isEmpty(tips)) {
            baseY = centerPoint.y;
        } else {
            baseY = centerPoint.y - top / 2 - bottom / 2;
        }
        canvas.drawText(progressText, centerPoint.x - unitWidth / 2.0F, baseY, mProgressPaint);
        if (!TextUtils.isEmpty(progressUnit)) {
            mUnitPaint.setShader(gradient);
            canvas.drawText(progressUnit, centerPoint.x - unitWidth / 2.0F + progressWidth / 2.0F, baseY, mUnitPaint);
        }

        mUnitPaint.setTextSize(tipsSize);
        mUnitPaint.setShader(null);
        mUnitPaint.setColor(tipsColor);
        mUnitPaint.setTextAlign(Paint.Align.CENTER);
        if (!TextUtils.isEmpty(tips)) {
            canvas.drawText(tips, centerPoint.x, centerPoint.y + innerCircleRadius / 2, mUnitPaint);
        }
    }

    /**
     * 画内圆
     *
     * @param canvas
     */
    private void drawInnerCircle(Canvas canvas) {
        canvas.drawCircle(centerPoint.x, centerPoint.y, innerCircleRadius, mInnerCirclePaint);
    }

    /**
     * 画齿轮
     *
     * @param canvas
     */
    private void drawGear(Canvas canvas) {
        float atom = (float) (Math.PI * 5 / 180);
        gearStartAngle = (1 - gearRange) * Math.PI;
        gearEndAngle = (1 + gearRange) * Math.PI;
        if (gearEndAngle == 2 * Math.PI) {
            gearEndAngle -= atom;
        }

        double diffAngle = gearEndAngle - gearStartAngle;
        float startX, startY, stopX, stopY;

        /**
         * 这里找到drawProgress附近的9根线条
         * 以下为每5度一条线,总线条根数为72根
         */
        int totalLineCount = 72;
        //最靠近当前drawProgress的线条的位置
        pLinePoint = (int) (totalLineCount - Math.ceil(totalLineCount * drawProgress / maxProgress));

        int point = (int) (gearStartAngle / atom);
        for (double angle = gearStartAngle; angle < gearEndAngle; angle = angle + atom) {
            /**
             * 这里计算颜色有些诡异,由于我们计算角度是从底下逆时针开始计算,
             * 而我们的仪表盘进度顺时针开始计算,所以这里是gearEndAngle-angle,而不是gearStartAngle+angle
             */
            mGearPaint.setColor(transitColor((float) ((gearEndAngle - angle) / diffAngle)));
            float startR = outerCircleRadius;
            float endR = outerCircleRadius;
            if (animProgress != 1) {
                if (point == gearPoints[pLinePoint + 36]) {
                    startR = startR + 5 * gearAnimGradient / 2;
                    endR = endR + gearAnimLength;
                } else if (point == gearPoints[pLinePoint + 35] || point == gearPoints[pLinePoint + 37]) {
                    startR = startR + 4 * gearAnimGradient / 2;
                    endR = endR + gearAnimLength - gearAnimGradient;
                } else if (point == gearPoints[pLinePoint + 34] || point == gearPoints[pLinePoint + 38]) {
                    startR = startR + 3 * gearAnimGradient / 2;
                    endR = endR + gearAnimLength - 2 * gearAnimGradient;
                } else if (point == gearPoints[pLinePoint + 33] || point == gearPoints[pLinePoint + 39]) {
                    startR = startR + 2 * gearAnimGradient / 2;
                    endR = endR + gearAnimLength - 3 * gearAnimGradient;
                } else if (point == gearPoints[pLinePoint + 32] || point == gearPoints[pLinePoint + 40]) {
                    startR = startR + gearAnimGradient / 2;
                    endR = endR + gearAnimLength - 4 * gearAnimGradient;
                }
            }

            startX = (float) (centerPoint.x + Math.sin(angle) * (startR - gearWidth / 2));
            startY = (float) (centerPoint.y + Math.cos(angle) * (startR - gearWidth / 2));
            stopX = (float) (centerPoint.x + Math.sin(angle) * (endR + gearWidth / 2));
            stopY = (float) (centerPoint.y + Math.cos(angle) * (endR + gearWidth / 2));
            canvas.drawLine(startX, startY, stopX, stopY, mGearPaint);
            point++;
        }
    }

    /**
     * 画进度球
     *
     * @param canvas
     */
    private void drawProgressDot(Canvas canvas) {
        double angle = 2 * Math.PI - drawProgress / maxProgress * 2 * Math.PI;
        float dotX = (float) (centerPoint.x + innerCircleRadius * Math.sin(angle));
        float dotY = (float) (centerPoint.y + innerCircleRadius * Math.cos(angle));

        LinearGradient gradient = new LinearGradient(dotX + progressDotRadius,
                dotY - progressDotRadius,
                dotX - progressDotRadius,
                dotY + progressDotRadius,
                selectEndColor,
                selectStartColor,
                Shader.TileMode.MIRROR);
        mProgressDotPaint.setShader(gradient);

        canvas.drawCircle(dotX, dotY, progressDotRadius, mProgressDotPaint);
    }

    /**
     * 计算颜色过渡
     * @param progress
     * @return
     */
    private int transitColor(float progress) {
        int startA = Color.alpha(selectStartColor);
        int startR = Color.red(selectStartColor);
        int startG = Color.green(selectStartColor);
        int startB = Color.blue(selectStartColor);

        int endA = Color.alpha(selectEndColor);
        int endR = Color.red(selectEndColor);
        int endG = Color.green(selectEndColor);
        int endB = Color.blue(selectEndColor);

        return Color.argb((int) (startA + (endA - startA) * progress),
                (int) (startR + (endR - startR) * progress),
                (int) (startG + (endG - startG) * progress),
                (int) (startB + (endB - startB) * progress));
    }

    public float getProgress() {
        return mProgress;
    }

    public void setProgress(float progress) {
        progress = correctProgress(progress);
        if (progress == mProgress) {
            return;
        }
        cancelCurrenAnim();
        mProgress = progress;
        drawProgress = mProgress;
        formatProgress(mProgress);
        animProgress = 1;
        invalidate();
    }

    private void formatProgress(float progress) {
        if (decimals <= 0) {
            progressText = String.valueOf((int) progress);
        } else {
            progressText = String.format("%." + decimals + "f", progress);
        }
    }

    public void setProgressWithAnim(float progress) {
        progress = correctProgress(progress);
        if (progress == mProgress) {
            return;
        }

        final float lastProgress = mProgress;
        final float diff = progress - lastProgress;
        mProgress = progress;

        cancelCurrenAnim();
        progressAnimator = ValueAnimator.ofFloat(0, 1);
        progressAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                animProgress = (float) animation.getAnimatedValue();
                if (animProgress <= 0.5) {
                    formatProgress(lastProgress);
                } else {
                    formatProgress(mProgress);
                }
                drawProgress = lastProgress + diff * animProgress;
                invalidate();
            }
        });
        progressAnimator.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animation) {

            }

            @Override
            public void onAnimationEnd(Animator animation) {
            }

            @Override
            public void onAnimationCancel(Animator animation) {
                drawProgress = mProgress;
                formatProgress(mProgress);
                invalidate();
            }

            @Override
            public void onAnimationRepeat(Animator animation) {

            }
        });
        progressAnimator.setInterpolator(new OvershootInterpolator());
        long duration = MIN_PROGRESS_ANIM_DURATION + (long) ((PROGRESS_ANIM_DURATION - MIN_PROGRESS_ANIM_DURATION) * ((mProgress - lastProgress) / maxProgress));
        progressAnimator.setDuration(duration);
        progressAnimator.start();
    }

    private void cancelCurrenAnim() {
        if (progressAnimator != null && progressAnimator.isRunning()) {
            progressAnimator.cancel();
        }
    }

    private float correctProgress(float progress) {
        if (progress < 0) {
            progress = 0;
        }
        if (progress > maxProgress) {
            progress = maxProgress;
        }
        return progress;
    }


    public float getProgressTextSize() {
        return progressTextSize;
    }

    public void setProgressTextSize(float progressTextSize) {
        this.progressTextSize = progressTextSize;
        invalidate();
    }

    public float getTipsSize() {
        return tipsSize;
    }

    public void setTipsSize(float tipsSize) {
        this.tipsSize = tipsSize;
        invalidate();
    }

    public float getCirclePadding() {
        return circlePadding;
    }

    public void setCirclePadding(float circlePadding) {
        this.circlePadding = circlePadding;
        invalidate();
    }

    public int getInnerCircleColor() {
        return innerCircleColor;
    }

    public void setInnerCircleColor(int innerCircleColor) {
        this.innerCircleColor = innerCircleColor;
        invalidate();
    }

    public int getSelectStartColor() {
        return selectStartColor;
    }

    public void setSelectStartColor(int selectStartColor) {
        this.selectStartColor = selectStartColor;
        invalidate();
    }

    public int getSelectEndColor() {
        return selectEndColor;
    }

    public void setSelectEndColor(int selectEndColor) {
        this.selectEndColor = selectEndColor;
        invalidate();
    }

    public float getGearWidth() {
        return gearWidth;
    }

    public void setGearWidth(float gearWidth) {
        this.gearWidth = gearWidth;
        invalidate();
    }

    public float getInnerStrokeWidth() {
        return innerStrokeWidth;
    }

    public void setInnerStrokeWidth(float innerStrokeWidth) {
        this.innerStrokeWidth = innerStrokeWidth;
        invalidate();
    }

    public float getGearStrokeWidth() {
        return gearStrokeWidth;
    }

    public void setGearStrokeWidth(float gearStrokeWidth) {
        this.gearStrokeWidth = gearStrokeWidth;
        invalidate();
    }

    public float getGearRange() {
        return gearRange;
    }

    public void setGearRange(float gearRange) {
        gearRange = correctGearRange(gearRange);
        if (this.gearRange == gearRange) {
            return;
        }
        this.gearRange = gearRange;
        invalidate();
    }

    private float correctGearRange(float range) {
        if (range < 0) {
            range = 0;
        }
        if (range > 1) {
            range = 1;
        }
        return range;
    }

    public float getMaxProgress() {
        return maxProgress;
    }

    public void setMaxProgress(float maxProgress) {
        this.maxProgress = maxProgress;
        invalidate();
    }

    public float getProgressDotRadius() {
        return progressDotRadius;
    }

    public void setProgressDotRadius(float progressDotRadius) {
        this.progressDotRadius = progressDotRadius;
        invalidate();
    }

    public String getProgressUnit() {
        return progressUnit;
    }

    public void setProgressUnit(String progressUnit) {
        this.progressUnit = progressUnit;
    }

    public String getTips() {
        return tips;
    }

    public void setTips(String tips) {
        this.tips = tips;
    }

    public int getDecimals() {
        return decimals;
    }

    public void setDecimals(int decimals) {
        this.decimals = decimals;
    }
}

支持自定义的属性如下:
attr.xml



    
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
    

这里只是将大致流程记录下来,整个View的逻辑代码较多,参考价值不会太大。每个UI小姐姐想要的效果都不太一样,需要根据不同的要求来具体实现。

你可能感兴趣的:(自定义ProgressBar)