Android 自定义View实现带进度的下载按钮

转载请注明出处:https://blog.csdn.net/a512337862/article/details/80612225

因为项目需要,所以用自定义View实现了带进度条的下载按钮。效果图如下:
这里写图片描述

思路

主要是分为三个阶段:未下载,正在下载,下载结束(成功/失败)。根据不同的进度来判断不同的状态,从而绘制不同的UI。

代码

attr.xml

    <declare-styleable name="DownloadProgressButton">
        <attr name="textColor" format="color" />
        <attr name="textSize" format="dimension" />
        <attr name="progressColor" format="color" />
        <attr name="bottomColor" format="color" />
        <attr name="topColor" format="color" />
        <attr name="progressWidthPercent" format="integer" />
    </declare-styleable>

DownloadProgressButton.java

/**
 * Author : BlackHao
 * Time : 2018/5/18 10:25
 * Description : 自定义带圆形进度条下载控件
 * Remarks :
 */

public class DownloadProgressButton extends View implements View.OnClickListener {

    /**
     * 未下载
     */
    public static final int READY_TO_DOWNLOAD = 0;
    /**
     * 正在下载
     */
    public static final int IS_DOWNLOADING = 1;
    /**
     * 下载完成
     */
    public static final int FINISH_DOWNLOAD = 2;
    /**
     * 下载失败
     */
    public static final int FAIL_DOWNLOAD = 3;
    //进度最大值
    private int max;
    //当前进度
    private int progress;
    //进度条的颜色
    private int progressColor;
    //进度条宽度百分比
    private int progressWidthPercent;
    //底层背景色
    private int bottomColor;
    //最上层圆的颜色
    private int topColor;
    //进度文本的颜色
    private int textColor;
    //进度文本的字体大小
    private int textSize;
    //进度条画笔
    private Paint progressPaint;
    //文本画笔
    private Paint textPaint;
    //用来显示文本的矩形
    private Rect textRect;
    //当前模式
    private int downloadModel = 0;
    //
    private ProgressChangeListener clickListener;
    private Path path;

    public DownloadProgressButton(Context context) {
        this(context, null, 0);
    }

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

    public DownloadProgressButton(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initDefault();
        TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.DownloadProgressButton, defStyleAttr, 0);
        int n = a.getIndexCount();
        for (int i = 0; i < n; i++) {
            int attr = a.getIndex(i);
            if (attr == R.styleable.DownloadProgressButton_topColor) {//最上层圆的颜色,默认绿色
                topColor = a.getColor(attr, Color.TRANSPARENT);

            } else if (attr == R.styleable.DownloadProgressButton_textColor) {//进度文本的颜色
                textColor = a.getColor(attr, Color.BLACK);

            } else if (attr == R.styleable.DownloadProgressButton_progressColor) {//进度条颜色
                progressColor = a.getColor(attr, Color.GREEN);

            } else if (attr == R.styleable.DownloadProgressButton_bottomColor) {//背景色
                bottomColor = a.getColor(attr, Color.GREEN);

            } else if (attr == R.styleable.DownloadProgressButton_textSize) {//进度文本的大小
                // 默认设置为16sp,TypeValue也可以把sp转化为px
                textSize = a.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(
                        TypedValue.COMPLEX_UNIT_SP, 16, getResources().getDisplayMetrics()));

            } else if (attr == R.styleable.DownloadProgressButton_progressWidthPercent) {//进度条宽度占总宽度的百分比
                progressWidthPercent = a.getInteger(attr, 50);
            }
        }
        //Recycles the TypedArray
        a.recycle();
        /*
         * 初始化画笔
         */
        //文本画笔
        textPaint = new Paint();
        textPaint.setTextSize(textSize);
        textPaint.setColor(textColor);
        //设置抗锯齿
        textPaint.setAntiAlias(true);
        //进度条画笔
        progressPaint = new Paint();
        //设置抗锯齿
        progressPaint.setAntiAlias(true);
        //初始化textRect
        textRect = new Rect();
        path = new Path();
        //
        setOnClickListener(this);
    }

    /**
     * 设置默认值
     */
    private void initDefault() {
        //最上层默认为透明
        topColor = Color.TRANSPARENT;
        //字体颜色默认为黑色
        textColor = Color.BLACK;
        //进度条默认为绿色
        progressColor = Color.GREEN;
        //背景为灰色
        bottomColor = Color.GRAY;
        //字体大小16
        textSize = 16;
        //进度0
        progress = 0;
        //最大进度100
        max = 100;
        //进度条宽度比例50
        progressWidthPercent = 50;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //canvas的宽高
        int width = getWidth();
        int height = getHeight();
        //中点
        int centerX = getWidth() / 2;
        int centerY = getHeight() / 2;
        //获取宽高中较短的作为底层圆的半径
        int bottomRadius = width > height ? getHeight() / 2 : getWidth() / 2;
        //下载边框宽度
        int circleWidth = bottomRadius / 5;
        switch (downloadModel) {
            case READY_TO_DOWNLOAD:
                //绘制下载按钮
                progressPaint.setColor(Color.GRAY);
                progressPaint.setStrokeWidth(circleWidth);
                //绘制圆
                progressPaint.setStyle(Paint.Style.STROKE);
                canvas.drawCircle(centerX, centerY, bottomRadius - circleWidth / 2, progressPaint);
                //绘制三角箭头
                progressPaint.setStyle(Paint.Style.FILL);
                canvas.drawLine(centerX, centerY, centerX, centerY - bottomRadius / 2, progressPaint);
                //绘制三角形
                path.reset();
                path.moveTo(centerX - bottomRadius / 2, centerY);// 此点为多边形的起点
                path.lineTo(centerX + bottomRadius / 2, centerY);
                path.lineTo(centerX, centerY + bottomRadius / 2);
                path.close(); // 使这些点构成封闭的多边形
                canvas.drawPath(path, progressPaint);
                break;
            case IS_DOWNLOADING:
                //进度条宽度占的比例
                double scale = progressWidthPercent / 100.00;
                //这四个参数用来drawText以及drawArc
                int top = centerY - bottomRadius;
                int bottom = centerY + bottomRadius;
                int left = centerX - bottomRadius;
                int right = centerX + bottomRadius;
                //显示的进度
                int showProgress = progress * 100 / max;
                //绘制最底层的大圆(空心圆)
                progressPaint.setStyle(Paint.Style.STROKE);
                progressPaint.setColor(bottomColor);
                //大圆的宽度刚好等于进度条的宽度
                int strokeWidth = (int) (bottomRadius * scale);
                progressPaint.setStrokeWidth(strokeWidth);
                //这里要考虑到strokeWidth的长度
                canvas.drawCircle(centerX, centerY, bottomRadius - strokeWidth / 2, progressPaint);
                //根据进度绘制进度条
                progressPaint.setStyle(Paint.Style.STROKE);
                progressPaint.setStrokeWidth(strokeWidth);
                progressPaint.setColor(progressColor);
                //起始角度为-90度才是从正上方开始画
                canvas.drawArc(left + strokeWidth / 2, top + strokeWidth / 2, right - strokeWidth / 2, bottom - strokeWidth / 2,
                        -90, 360 * showProgress / 100, false, progressPaint);
                //绘制最上层的圆(实心圆)
                progressPaint.setStyle(Paint.Style.FILL);
                int topRadius = (int) (bottomRadius * (1 - scale));
                progressPaint.setColor(topColor);
                //设置边框宽度
                progressPaint.setStrokeWidth(0);
                canvas.drawCircle(centerX, centerY, topRadius, progressPaint);
                //在圆的中央显示进度文本
                textRect.set(left, top, right, bottom);
                drawTextOnRect(canvas, textRect, showProgress + " %");
                break;
            case FINISH_DOWNLOAD:
                //绘制下载完成
                progressPaint.setColor(Color.GREEN);
                progressPaint.setStrokeWidth(circleWidth);
                //绘制圆
                progressPaint.setStyle(Paint.Style.STROKE);
                canvas.drawCircle(centerX, centerY, bottomRadius - circleWidth / 2, progressPaint);
                //绘制完成符号(√)
                progressPaint.setStyle(Paint.Style.FILL);
                canvas.drawLine(centerX, centerY + bottomRadius / 2, centerX - bottomRadius / 2, centerY, progressPaint);
                canvas.drawLine(centerX - circleWidth / 2, centerY + bottomRadius / 2, centerX + bottomRadius / 2, centerY - bottomRadius / 2, progressPaint);
                break;
            case FAIL_DOWNLOAD:
                //下载失败
                progressPaint.setColor(Color.RED);
                progressPaint.setStrokeWidth(circleWidth);
                //绘制圆
                progressPaint.setStyle(Paint.Style.STROKE);
                canvas.drawCircle(centerX, centerY, bottomRadius - circleWidth / 2, progressPaint);
                //绘制完成符号(X)
                progressPaint.setStyle(Paint.Style.FILL);
                canvas.drawLine(centerX - bottomRadius / 2, centerY - bottomRadius / 2, centerX + bottomRadius / 2, centerY + bottomRadius / 2, progressPaint);
                canvas.drawLine(centerX - bottomRadius / 2, centerY + bottomRadius / 2, centerX + bottomRadius / 2, centerY - bottomRadius / 2, progressPaint);
                break;
        }

    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        //计算View的宽高
        int width = widthSize, height = heightSize;
        if (widthMode == MeasureSpec.EXACTLY) {
            //指定大小或者match_parent
            width = widthSize;
        } else if (widthMode == MeasureSpec.AT_MOST) {
            //wrap_content
            width = 100;
        }

        if (heightMode == MeasureSpec.EXACTLY) {
            //指定大小或者match_parent
            height = heightSize;
        } else if (heightMode == MeasureSpec.AT_MOST) {
            //wrap_content
            height = 100;
        }
        setMeasuredDimension(width, height);
    }

    /**
     * 在指定矩形中间drawText
     *
     * @param canvas     画布
     * @param targetRect 指定矩形
     * @param text       需要绘制的 Text
     */
    private void drawTextOnRect(Canvas canvas, Rect targetRect, String text) {
        Paint.FontMetricsInt fontMetrics = textPaint.getFontMetricsInt();
        // 获取baseLine
        int baseline = targetRect.top + (targetRect.bottom - targetRect.top - fontMetrics.bottom + fontMetrics.top) / 2 - fontMetrics.top;
        // 下面这行是实现水平居中,drawText对应改为传入targetRect.centerX()
        textPaint.setTextAlign(Paint.Align.CENTER);
        canvas.drawText(text, targetRect.centerX(), baseline, textPaint);
    }

    public int getProgress() {
        return progress;
    }

    public void setProgress(int progress) {
        this.progress = progress;
        if (progress == max) {
            downloadModel = FINISH_DOWNLOAD;
            //完成回调结果
            if (clickListener != null) {
                clickListener.onSuccess(this);
            }
        } else if (progress == 0) {
            downloadModel = READY_TO_DOWNLOAD;
        } else if (progress > 0 && progress < max) {
            downloadModel = IS_DOWNLOADING;
        }else {
            downloadModel = FAIL_DOWNLOAD;
            //完成回调结果
            if (clickListener != null) {
                clickListener.onFailed(this);
            }
        }
        postInvalidate();
    }

    public int getProgressColor() {
        return progressColor;
    }

    public void setProgressColor(int progressColor) {
        this.progressColor = progressColor;
        postInvalidate();
    }

    public int getBottomColor() {
        return bottomColor;
    }

    public int getProgressWidthPercent() {
        return progressWidthPercent;
    }

    public void setProgressWidthPercent(int progressWidthPercent) {
        this.progressWidthPercent = progressWidthPercent;
        postInvalidate();
    }

    public void setBottomColor(int bottomColor) {
        this.bottomColor = bottomColor;
        postInvalidate();
    }

    public int getTopColor() {
        return topColor;
    }

    public void setTopColor(int topColor) {
        this.topColor = topColor;
        postInvalidate();
    }

    public int getTextColor() {
        return textColor;
    }

    public void setTextColor(int textColor) {
        this.textColor = textColor;
        postInvalidate();
    }

    public int getTextSize() {
        return textSize;
    }

    public void setTextSize(int textSize) {
        this.textSize = textSize;
        postInvalidate();
    }

    public void setClickListener(ProgressChangeListener clickListener) {
        this.clickListener = clickListener;
    }

    @Override
    public void onClick(View v) {
        if (clickListener != null) {
            clickListener.onPbClick(v, downloadModel);
        }
    }

    /**
     * 点击/下载完成(失败) 回调
     */
    public interface ProgressChangeListener {
        //点击监听
        void onPbClick(View view, int model);
        //下载成功
        void onSuccess(View view);
        //下载失败
        void onFailed(View view);
    }
}

结语

1.源码github地址 : https://github.com/LuoChen-Hao/BlackHaoCustomView

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值