自定义对战进度条

简单表述.png
预览.gif

Demo完整代码地址

1. 分析:

该View主要有六个部分,底部背景左边进度条右边进度条横向半透明光柱比分文字光标

① 重写 onMeasure ,初始化一些值:
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int height = MeasureSpec.getSize(heightMeasureSpec);
        int width = MeasureSpec.getSize(widthMeasureSpec);

        if (getLayoutParams().height == ViewGroup.LayoutParams.WRAP_CONTENT) {
            width = halfDrawableWidth * 2;
        }

        if (getLayoutParams().width == ViewGroup.LayoutParams.WRAP_CONTENT) {
            height = halfDrawableHeight * 2;
        }

        // 记录进度条总长度
        progressWidth = width;
        // 记录进度条起始位置
        barStartWidth = barPadding;
        // 记录进度条结束位置
        barEndWidth = progressWidth - barPadding;
        setMeasuredDimension(width, height);
    }
② 初始化画笔
        // 底部边框背景
        paintBackGround = new Paint();
        paintBackGround.setColor(backGroundColor);
        paintBackGround.setAntiAlias(true);
        paintLight = new Paint();
        paintLight.setAntiAlias(true);

        // 左半部分进度条
        paintBar = new Paint();
        paintBar.setColor(barColor);
        paintBar.setAntiAlias(true);
        // 右半部分进度条
        paintOtherBar = new Paint();
        paintOtherBar.setColor(barColor);
        paintOtherBar.setAntiAlias(true);

        // 左半部分文字
        paintLeftText = new Paint();
        paintLeftText.setStyle(Paint.Style.FILL);
        paintLeftText.setColor(textColor);
        paintLeftText.setTextSize(textSize);
        paintLeftText.setFakeBoldText(textIsBold);   // 加粗
        paintLeftText.setAntiAlias(true);
        paintLeftText.setTextSkewX(-0.25f);   // 斜体
        // 右半部分文字
        paintRightText = new Paint();
        paintRightText.setStyle(Paint.Style.FILL);
        paintRightText.setColor(textColor);
        paintRightText.setTextSize(textSize);
        paintRightText.setFakeBoldText(textIsBold);
        paintRightText.setAntiAlias(true);
        paintRightText.setTextSkewX(-0.25f);
③ 提前创建渐变进度条效果
        getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
            @Override
            public boolean onPreDraw() {
                getViewTreeObserver().removeOnPreDrawListener(this);

                //是否需要渐变器
                if (isGradient) {
                    linearGradient = new LinearGradient(0, y / 2f,
                            progressWidth, y / 2f, gradientStartColor, gradientEndColor, Shader.TileMode.CLAMP);
                    linearGradientOther = new LinearGradient(0, y / 2f,
                            progressWidth, y / 2f, otherGradientStartColor, otherGradientEndColor, Shader.TileMode.CLAMP);
                    paintBar.setShader(linearGradient);
                    paintOtherBar.setShader(linearGradientOther);
                }
                return false;
            }
        });

开始绘制,重写onDraw()方法。按照层叠关系依次绘制底部背景左边进度条右边进度条横向半透明光柱比分文字光标

    protected synchronized void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        // 光标水平位置 
        // 注: x一定不要用int类型
        x = (float) ((progressWidth - 3 * barPadding) * progressPercentage + barPadding);
        y = getHeight();
        // 圆角大小,为实际进度条高度一半
        if (isRound) {
            barRadioSize = (y - barPadding * 2) / 2f;
        }

        // 绘制底部边框
        drawBackground(canvas);
        // 绘制左半部分进度条
        drawLeftBar(canvas);
        // 绘制右半部分进度条
        drawRightBar(canvas);
        // 绘制横向半透明光柱
        drawLight(canvas);
        // 绘制比例文字
        drawRateText(canvas);
        // 绘制光标
        drawPicture(canvas);
    }
④ 绘制底部背景
    private void drawBackground(Canvas canvas) {

        canvas.save();
        rectFBG.set(0, 0, progressWidth, y);
        if (isRound) {
            // 底部背景圆角大小为高度的一半
            canvas.drawRoundRect(rectFBG, y / 2f, y / 2f, paintBackGround);
        } else {
            canvas.drawRect(rectFBG, paintBackGround);
        }
        canvas.restore();
    }
⑤ 绘制左半部分进度条
        float right = getBoundaryPosition();

        float[] radios = new float[]{
                barRadioSize, barRadioSize, 0, 0,
                0, 0, barRadioSize, barRadioSize};

        if (progressPercentage == 1) {
            radios[2] = barRadioSize;
            radios[3] = barRadioSize;
            radios[4] = barRadioSize;
            radios[5] = barRadioSize;
        }

        canvas.save();
        rectFPB.set(barPadding, barPadding, right, y - barPadding);
        barRoundPath.reset();
        barRoundPath.addRoundRect(rectFPB, radios, Path.Direction.CCW);
        barRoundPath.close();
        if (isRound) {
            canvas.drawPath(barRoundPath, paintBar);
        } else {
            canvas.drawRect(rectFPB, paintBar);
        }
        canvas.restore();
⑥ 绘制右半部分进度条
float left = getBoundaryPosition();

        float[] radios = new float[]{0, 0,
                barRadioSize, barRadioSize,
                barRadioSize, barRadioSize, 0, 0};

        if (progressPercentage == 0) {
            radios[0] = barRadioSize;
            radios[1] = barRadioSize;
            radios[6] = barRadioSize;
            radios[7] = barRadioSize;
        }

        canvas.save();
        rectFPBO.set(left, barPadding, barEndWidth, y - barPadding);
        barRoundPathOther.reset();
        barRoundPathOther.addRoundRect(rectFPBO, radios, Path.Direction.CCW);
        barRoundPathOther.close();
        if (isRound) {
            canvas.drawPath(barRoundPathOther, paintOtherBar);
        } else {
            canvas.drawRect(rectFPBO, paintOtherBar);
        }
        canvas.restore();
⑦ 绘制横向半透明光柱
        if (lightBitmap == null) {
            return;
        }

        canvas.save();

        // 将画布坐标系移动到画布中央
        canvas.translate(0, barPadding);

        Rect src = new Rect(barStartWidth, 0, barEndWidth, (y - barPadding) / 2);
        canvas.drawBitmap(lightBitmap, src, src, paintLight);
        canvas.restore();
⑧ 绘制比例文字
paintLeftText.getTextBounds(leftTextStr, 0, leftTextStr.length(), rectLeftText);
        paintRightText.getTextBounds(rightTextStr, 0, rightTextStr.length(), rectRightText);

        int des1W = rectRightText.width();
        int desH = rectLeftText.height();

        canvas.drawText(leftTextStr,
                y / 2f,
                y / 2f + desH / 3.0f,
                paintLeftText);

        canvas.drawText(rightTextStr,
                progressWidth - y / 2f - des1W - barPadding,
                y / 2f + desH / 3.0f,
                paintRightText);
⑨ 绘制光标
        if (drawable == null) {
            return;
        }

        drawable.setBounds((int) getBoundaryPosition() - halfDrawableWidth, 0, (int) (getBoundaryPosition() + halfDrawableWidth), y);
        drawable.draw(canvas);
⑩ 计算光标临界值

为保证美观,光标必须停留在直方区域,如下图所示


进度条简易图.png
    private float getBoundaryPosition() {
        // 默认为计算的比例位置
        float boundaryPos = x;

        if (progressPercentage == 0 || x == barStartWidth) {
            // 光标位于最左边
            boundaryPos = barPadding;
        } else if (progressPercentage == 1 || x == barEndWidth) {
            // 光标位于最右边
            boundaryPos = barEndWidth;
        } else if (((x - barStartWidth) < barRadioSize || (x - barStartWidth) == barRadioSize)
                && x > barStartWidth) {
            // 光标位于进度条左侧弧形区域
            boundaryPos = Math.max(x, barRadioSize + barStartWidth);
        } else if ((x > barEndWidth - barRadioSize || x == barEndWidth - barRadioSize)
                && x < barEndWidth) {
            // 光标位于进度条右侧弧形区域
            boundaryPos = Math.min(x, barEndWidth - barRadioSize);
        }

        return boundaryPos;
    }

你可能感兴趣的:(自定义对战进度条)