Android 教你从0开始撸进度条

阅读这篇文章需要知道一些基础知识:

path.moveTo(x,y)

path.lineTo(x,y)

canvas.drawPath(path,paint)

invalidate()

上面函数的含义要明白是什么意思

1.实现目标


要实现的进度条如上,并非网上的圆柱形或者矩形进度条。

2.分析目标

所要描绘的有三部分:

(1).外形边框
(2).里面进度背景
(3).进度的描绘


(1).外形边框的实现

刚开始看到这个进度条的图后就不能用规则图形去描绘了,只能用Path线条去描绘了,根据上图就要确定四个点才能描绘出整个图的轮廓。

1.第一个点:x1(x1,y1),简单来说就是要求横坐标x1,因为y1默认就是0,求x1就是求X,现在因为已经知道一个角度的tan值和高度Y,因此可以通过三角函数来求X。

扫描二维码关注公众号,回复: 2557474 查看本文章

tanB = X / Y,那么X = tanB * Y。

2.第一个点:x2(x2,y2),x2就是进度条的宽度,进度条的宽和高都是可以从getMeasuredWidth()和getMeasuredHeight()来求出。

3.第三个点:x3(x3,y3),横坐标就是进度条的宽度 - X,因为前后是对称的,仔细想一下就知道了。纵坐标就是进度条的高度。

4.第四个点:x4(x4,y4),横坐标就是0,纵坐标就是进度条的高度,因此这四个点都可以求出来了。

(2).进度条背景的实现

其实也是四个点来描绘,这个四个点就是外形边框的四个点都要加上边框或者减去边框所求得。

如第一个点x1(x1,y1+边框),第二个点x2(x2 - 边框,y2 + 边框),第三个点x3(x3,y3 - 边框),第四点x4(边框,y4 - 边框)

(3).进度的描绘

仔细分析:进度的状态总共有四种状态

1.进度是三角形:


2.直角梯形:


3.不规则:


4.平行四边形:


现在一一去实现:

首先是要确定进度:

进度的长度如下:


而X的长度如下:


那第一种情况就是进度比X的距离要小:


这里的right就是进度,r就是对应上图的X

第二种情况就是直角梯形:

符合的情况是进度的距离大于X的距离并且小于进度条的宽度减去X的距离:


第三种情况就是不规则五边形:

符合的情况是进度大于宽度减去X的距离,并且进度小于进度条的宽度:


第四种情况就是进度的距离等于进度条的宽度:


怎么去描绘文字就不解释了。

3.源码

首先在attr文件下自定义控件属性:

    <declare-styleable name="ChooseGilrsProgressView">
        <attr name="pborderWidth" format="dimension" />
        <attr name="pdegrees" format="float|reference"/>
    </declare-styleable>

继承View,详细代码如下:

public class ChooseGilrsProgressView extends View {

    private Paint mBroaderPaint;//边框画笔
    private Paint mBackgroundPaint;//边框内背景画笔
    private Paint mFillPaint;//填充画笔

    //外边的宽度
    private static final int DEFAULT_BORDER_WIDTH = 2;
    private int BorderWidth = DEFAULT_BORDER_WIDTH;
    //假设是45°
    double degrees;
    //画笔Paint
    private Paint mTextPaint;
    //宽和高
    private int width;
    private int height;
    //路径
    float r;
    //边框线条 内部背景 内部进度
    private Path pathFrame, pathInside, pathFill;
    //当前进度
    private float progress;
    //最大进度
    private float maxProgress;

    public ChooseGilrsProgressView(Context context) {
        this(context, null);
        initView();
    }

    public ChooseGilrsProgressView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
        initView();
    }

    public ChooseGilrsProgressView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ChooseGilrsProgressView, defStyleAttr, 0);
        try {
            BorderWidth = ta.getDimensionPixelSize(R.styleable.ChooseGilrsProgressView_pborderWidth, DEFAULT_BORDER_WIDTH);
            degrees = ta.getFloat(R.styleable.ChooseGilrsProgressView_pdegrees, 45);
        } finally {
            ta.recycle();
        }
        initView();
    }


    private void initView() {
        //边框 抗锯齿
        mBroaderPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mBroaderPaint.setColor(0xffffffff);
        mBroaderPaint.setStrokeWidth(BorderWidth);//线宽度

        mBroaderPaint.setStyle(Paint.Style.STROKE);
        mBroaderPaint.setStrokeJoin(Paint.Join.MITER);
        mBroaderPaint.setAntiAlias(true);

        //背景抗锯齿
        mBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mBackgroundPaint.setColor(0xff686B7E);
        mBackgroundPaint.setStyle(Paint.Style.FILL_AND_STROKE);
        mBackgroundPaint.setStrokeJoin(Paint.Join.MITER);

        //进度
        mFillPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mFillPaint.setStyle(Paint.Style.FILL);
        mFillPaint.setColor(0xffFE88A7);
        mFillPaint.setStrokeJoin(Paint.Join.MITER);

        //字体显示进度
        mTextPaint = new Paint();
        mTextPaint.setStrokeWidth(2);
        mTextPaint.setStyle(Paint.Style.STROKE);
        mTextPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
        mTextPaint.setColor(0xffffffff);
        mTextPaint.setTextSize(24f);
        mTextPaint.setTextAlign(Paint.Align.LEFT);


    }


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //获取view的宽高
        width = getMeasuredWidth();
        height = getMeasuredHeight();
        //固定死的
        r = height / (float) Math.tan(Math.toRadians(degrees));

    }


    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        //  initView();
    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        pathFrame = new Path();
        pathInside = new Path();
        pathFill = new Path();
        //画边框
        drawSide(canvas);
        //画背景
        drawBackground(canvas);
        //画进度
        drawProgress(canvas);
        //画文字
        drawProgressText(canvas);
    }

    //绘制背景边框
    private void drawSide(Canvas canvas) {

        pathFrame.moveTo(r, 0);
        pathFrame.lineTo(width, 0);
        pathFrame.lineTo(width - r, height);
        pathFrame.lineTo(0, height);
        pathFrame.close();
        canvas.drawPath(pathFrame, mBroaderPaint);
    }

    //绘制背景内部
    private void drawBackground(Canvas canvas) {
        pathInside.moveTo(r, BorderWidth);
        pathInside.lineTo(width - BorderWidth - BorderWidth, BorderWidth);
        pathInside.lineTo(width - r, height - BorderWidth);
        pathInside.lineTo(2 * BorderWidth, height - BorderWidth);
        pathInside.close();
        canvas.drawPath(pathInside, mBackgroundPaint);
    }


    //绘制进度
    private void drawProgress(Canvas canvas) {
        float right;
        //如果当前进度大于最大进度 就是最大的进度
        if (progress > maxProgress) {
            right = width;
        } else {//否则就是按比例计算
            right = (progress / maxProgress) * width;
        }
        // 第一种情况 三角形 因为默认是45度 所以直角三角形 宽 高相等
        if (right <= r) {
            pathFill.moveTo(right + 2 * BorderWidth, height - right - BorderWidth);
            pathFill.lineTo(right + 2 * BorderWidth, height - BorderWidth);
            pathFill.lineTo(2 * BorderWidth, height - BorderWidth);
            pathFill.close();
            canvas.drawPath(pathFill, mFillPaint);
        } else if (right > r && right <= width - r) {
            pathFill.moveTo(r, BorderWidth);
            pathFill.lineTo(right, BorderWidth);
            pathFill.lineTo(right, height - BorderWidth);
            pathFill.lineTo(2 * BorderWidth, height - BorderWidth);
            pathFill.close();
            canvas.drawPath(pathFill, mFillPaint);
        } else if (right > width - r && right < width) {
            pathFill.moveTo(r, BorderWidth);
            pathFill.lineTo(right, BorderWidth);
            pathFill.lineTo(right, width - right - BorderWidth);
            pathFill.lineTo(width - r, height - BorderWidth);
            pathFill.lineTo(2 * BorderWidth, height - BorderWidth);
            pathFill.close();
            canvas.drawPath(pathFill, mFillPaint);
        } else if (right == width) {
            pathFill.moveTo(r + BorderWidth / 2, BorderWidth);
            pathFill.lineTo(width - 2 * BorderWidth, BorderWidth);
            pathFill.lineTo(width - r - BorderWidth / 2, height - BorderWidth);
            pathFill.lineTo(2 * BorderWidth, height - BorderWidth);
            pathFill.close();
            canvas.drawPath(pathFill, mFillPaint);
        }
    }

    //绘制文本
    private void drawProgressText(Canvas canvas) {
        float textWidth = mTextPaint.measureText((int) progress + "/" + (int) maxProgress + "");
        //文字的y轴坐标
        Paint.FontMetrics fontMetrics = mTextPaint.getFontMetrics();
        float y = height / 2 + (Math.abs(fontMetrics.ascent) - fontMetrics.descent) / 2;
        //文字内容 描绘x轴和y轴
        canvas.drawText((int) progress + "/" + (int) maxProgress + "", width / 2 - textWidth / 2, y, mTextPaint);
    }

    /**
     * 设置进度
     *
     * @param progress
     */
    public void setProgress(float progress) {
        this.progress = progress;
        invalidate();
    }

    //设置最大进度
    public void setMaxProgress(float maxProgress) {
        this.maxProgress = maxProgress;
    }



}
以上就是实现所需进度条的逻辑。


猜你喜欢

转载自blog.csdn.net/qq_33453910/article/details/80676159