Android:自定义View实现绚丽的圆形进度条

来源:互联网 发布:淘宝联盟手机版官方 编辑:程序博客网 时间:2024/06/11 16:36

本博文包含两个组件,首先上效果图:

1.ProgressBarView1(支持拖动):


2.ProgressBarView2(不同进度值显示不同颜色,不支持拖拽):

    

代码不多,注释也比较详细,全部贴上了:

(一)ProgressBarView1:

/** * 自定义绚丽的ProgressBar. */public class ProgressBarView1 extends View {    /**     * 进度条所占用的角度     */    private static final int ARC_FULL_DEGREE = 300;    /**     * 弧线的宽度     */    private int STROKE_WIDTH;    /**     * 组件的宽,高     */    private int width, height;    /**     * 进度条最大值和当前进度值     */    private float max, progress;    /**     * 是否允许拖动进度条     */    private boolean draggingEnabled = false;    /**     * 绘制弧线的矩形区域     */    private RectF circleRectF;    /**     * 绘制弧线的画笔     */    private Paint progressPaint;    /**     * 绘制文字的画笔     */    private Paint textPaint;    /**     * 绘制当前进度值的画笔     */    private Paint thumbPaint;    /**     * 圆弧的半径     */    private int circleRadius;    /**     * 圆弧圆心位置     */    private int centerX, centerY;    public ProgressBarView1(Context context) {        super(context);        init();    }    public ProgressBarView1(Context context, AttributeSet attrs) {        super(context, attrs);        init();    }    public ProgressBarView1(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        init();    }    private void init() {        progressPaint = new Paint();        progressPaint.setAntiAlias(true);        textPaint = new Paint();        textPaint.setColor(Color.WHITE);        textPaint.setAntiAlias(true);        thumbPaint = new Paint();        thumbPaint.setAntiAlias(true);        //使用自定义字体        textPaint.setTypeface(Typeface.createFromAsset(getContext().getAssets(), "fangz.ttf"));    }    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        super.onMeasure(widthMeasureSpec, heightMeasureSpec);        if (width == 0 || height == 0) {            width = getWidth();            height = getHeight();            //计算圆弧半径和圆心点            circleRadius = Math.min(width, height) / 2;            STROKE_WIDTH = circleRadius / 12;            circleRadius -= STROKE_WIDTH;            centerX = width / 2;            centerY = height / 2;            //圆弧所在矩形区域            circleRectF = new RectF();            circleRectF.left = centerX - circleRadius;            circleRectF.top = centerY - circleRadius;            circleRectF.right = centerX + circleRadius;            circleRectF.bottom = centerY + circleRadius;        }    }    private Rect textBounds = new Rect();    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        float start = 90 + ((360 - ARC_FULL_DEGREE) >> 1); //进度条起始点        float sweep1 = ARC_FULL_DEGREE * (progress / max); //进度划过的角度        float sweep2 = ARC_FULL_DEGREE - sweep1; //剩余的角度        //绘制起始位置小圆形        progressPaint.setColor(Color.WHITE);        progressPaint.setStrokeWidth(0);        progressPaint.setStyle(Paint.Style.FILL);        float radians = (float) (((360.0f - ARC_FULL_DEGREE) / 2) / 180 * Math.PI);        float startX = centerX - circleRadius * (float) Math.sin(radians);        float startY = centerY + circleRadius * (float) Math.cos(radians);        canvas.drawCircle(startX, startY, STROKE_WIDTH / 2, progressPaint);        //绘制进度条        progressPaint.setStrokeWidth(STROKE_WIDTH);        progressPaint.setStyle(Paint.Style.STROKE);//设置空心        canvas.drawArc(circleRectF, start, sweep1, false, progressPaint);        //绘制进度条背景        progressPaint.setColor(Color.parseColor("#d64444"));        canvas.drawArc(circleRectF, start + sweep1, sweep2, false, progressPaint);        //绘制结束位置小圆形        progressPaint.setStrokeWidth(0);        progressPaint.setStyle(Paint.Style.FILL);        float endX = centerX + circleRadius * (float) Math.sin(radians);        float endY = centerY + circleRadius * (float) Math.cos(radians);        canvas.drawCircle(endX, endY, STROKE_WIDTH / 2, progressPaint);        //上一行文字        textPaint.setTextSize(circleRadius >> 1);        String text = (int) (100 * progress / max) + "";        float textLen = textPaint.measureText(text);        //计算文字高度        textPaint.getTextBounds("8", 0, 1, textBounds);        float h1 = textBounds.height();        //% 前面的数字水平居中,适当调整        float extra = text.startsWith("1") ? -textPaint.measureText("1") / 2 : 0;        canvas.drawText(text, centerX - textLen / 2 + extra, centerY - 30 + h1 / 2, textPaint);        //百分号        textPaint.setTextSize(circleRadius >> 2);        canvas.drawText("%", centerX + textLen / 2 + extra + 5, centerY - 30 + h1 / 2, textPaint);        //下一行文字        textPaint.setTextSize(circleRadius / 5);        text = "可用内存充足";        textLen = textPaint.measureText(text);        textPaint.getTextBounds(text, 0, text.length(), textBounds);        float h2 = textBounds.height();        canvas.drawText(text, centerX - textLen / 2, centerY + h1 / 2 + h2, textPaint);        //绘制进度位置,也可以直接替换成一张图片        float progressRadians = (float) (((360.0f - ARC_FULL_DEGREE) / 2 + sweep1) / 180 * Math.PI);        float thumbX = centerX - circleRadius * (float) Math.sin(progressRadians);        float thumbY = centerY + circleRadius * (float) Math.cos(progressRadians);        thumbPaint.setColor(Color.parseColor("#33d64444"));        canvas.drawCircle(thumbX, thumbY, STROKE_WIDTH * 2.0f, thumbPaint);        thumbPaint.setColor(Color.parseColor("#99d64444"));        canvas.drawCircle(thumbX, thumbY, STROKE_WIDTH * 1.4f, thumbPaint);        thumbPaint.setColor(Color.WHITE);        canvas.drawCircle(thumbX, thumbY, STROKE_WIDTH * 0.8f, thumbPaint);    }    private boolean isDragging = false;    @Override    public boolean onTouchEvent(@NonNull MotionEvent event) {        if (!draggingEnabled) {            return super.onTouchEvent(event);        }        //处理拖动事件        float currentX = event.getX();        float currentY = event.getY();        int action = event.getAction();        switch (action) {            case MotionEvent.ACTION_DOWN:                //判断是否在进度条thumb位置                if (checkOnArc(currentX, currentY)) {                    float newProgress = calDegreeByPosition(currentX, currentY) / ARC_FULL_DEGREE * max;                    setProgressSync(newProgress);                    isDragging = true;                }                break;            case MotionEvent.ACTION_MOVE:                if (isDragging) {                    //判断拖动时是否移出去了                    if (checkOnArc(currentX, currentY)) {                        setProgressSync(calDegreeByPosition(currentX, currentY) / ARC_FULL_DEGREE * max);                    } else {                        isDragging = false;                    }                }                break;            case MotionEvent.ACTION_UP:                isDragging = false;                break;        }        return true;    }    private float calDistance(float x1, float y1, float x2, float y2) {        return (float) Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));    }    /**     * 判断该点是否在弧线上(附近)     */    private boolean checkOnArc(float currentX, float currentY) {        float distance = calDistance(currentX, currentY, centerX, centerY);        float degree = calDegreeByPosition(currentX, currentY);        return distance > circleRadius - STROKE_WIDTH * 5 && distance < circleRadius + STROKE_WIDTH * 5                && (degree >= -8 && degree <= ARC_FULL_DEGREE + 8);    }    /**     * 根据当前位置,计算出进度条已经转过的角度。     */    private float calDegreeByPosition(float currentX, float currentY) {        float a1 = (float) (Math.atan(1.0f * (centerX - currentX) / (currentY - centerY)) / Math.PI * 180);        if (currentY < centerY) {            a1 += 180;        } else if (currentY > centerY && currentX > centerX) {            a1 += 360;        }        return a1 - (360 - ARC_FULL_DEGREE) / 2;    }    public void setMax(int max) {        this.max = max;        invalidate();    }    public void setProgress(float progress) {        final float validProgress = checkProgress(progress);        //动画切换进度值        new Thread(new Runnable() {            @Override            public void run() {                float oldProgress = ProgressBarView1.this.progress;                for (int i = 1; i <= 100; i++) {                    ProgressBarView1.this.progress = oldProgress + (validProgress - oldProgress) * (1.0f * i / 100);                    postInvalidate();                    SystemClock.sleep(20);                }            }        }).start();    }    public void setProgressSync(float progress) {        this.progress = checkProgress(progress);        invalidate();    }    //保证progress的值位于[0,max]    private float checkProgress(float progress) {        if (progress < 0) {            return 0;        }        return progress > max ? max : progress;    }    public void setDraggingEnabled(boolean draggingEnabled) {        this.draggingEnabled = draggingEnabled;    }}

(二)ProgressBarView2:

/** * 自定义绚丽的ProgressBar. */public class ProgressBarView2 extends View {    /**     * 进度条所占用的角度     */    private static final int ARC_FULL_DEGREE = 300;    //进度条个数    private static final int COUNT = 100;    //每个进度条所占用角度    private static final float ARC_EACH_PROGRESS = ARC_FULL_DEGREE * 1.0f / (COUNT - 1);    /**     * 弧线细线条的长度     */    private int ARC_LINE_LENGTH;    /**     * 弧线细线条的宽度     */    private int ARC_LINE_WIDTH;    /**     * 组件的宽,高     */    private int width, height;    /**     * 进度条最大值和当前进度值     */    private float max, progress;    /**     * 绘制弧线的画笔     */    private Paint progressPaint;    /**     * 绘制文字的画笔     */    private Paint textPaint;    /**     * 绘制文字背景圆形的画笔     */    private Paint textBgPaint;    /**     * 圆弧的半径     */    private int circleRadius;    /**     * 圆弧圆心位置     */    private int centerX, centerY;    public ProgressBarView2(Context context) {        super(context);        init();    }    public ProgressBarView2(Context context, AttributeSet attrs) {        super(context, attrs);        init();    }    public ProgressBarView2(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        init();    }    private void init() {        progressPaint = new Paint();        progressPaint.setAntiAlias(true);        textPaint = new Paint();        textPaint.setColor(Color.WHITE);        textPaint.setAntiAlias(true);        textBgPaint = new Paint();        textBgPaint.setAntiAlias(true);    }    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        super.onMeasure(widthMeasureSpec, heightMeasureSpec);        if (width == 0 || height == 0) {            width = getWidth();            height = getHeight();            //计算圆弧半径和圆心点            circleRadius = Math.min(width, height) / 2;            ARC_LINE_LENGTH = circleRadius / 6;            ARC_LINE_WIDTH = ARC_LINE_LENGTH / 8;            centerX = width / 2;            centerY = height / 2;        }    }    private Rect textBounds = new Rect();    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        float start = (360 - ARC_FULL_DEGREE) >> 1; //进度条起始角度        float sweep1 = ARC_FULL_DEGREE * (progress / max); //进度划过的角度        //绘制进度条        progressPaint.setColor(Color.parseColor(calColor(progress / max, "#ffff0000", "#ff00ff00")));        progressPaint.setStrokeWidth(ARC_LINE_WIDTH);        float drawDegree = 1.6f;        while (drawDegree <= ARC_FULL_DEGREE) {            double a = (start + drawDegree) / 180 * Math.PI;            float lineStartX = centerX - circleRadius * (float) Math.sin(a);            float lineStartY = centerY + circleRadius * (float) Math.cos(a);            float lineStopX = lineStartX + ARC_LINE_LENGTH * (float) Math.sin(a);            float lineStopY = lineStartY - ARC_LINE_LENGTH * (float) Math.cos(a);            if (drawDegree > sweep1) {                //绘制进度条背景                progressPaint.setColor(Color.parseColor("#88aaaaaa"));                progressPaint.setStrokeWidth(ARC_LINE_WIDTH >> 1);            }            canvas.drawLine(lineStartX, lineStartY, lineStopX, lineStopY, progressPaint);            drawDegree += ARC_EACH_PROGRESS;        }        //绘制文字背景圆形        textBgPaint.setStyle(Paint.Style.FILL);//设置填充        textBgPaint.setColor(Color.parseColor("#41668b"));        canvas.drawCircle(centerX, centerY, (circleRadius - ARC_LINE_LENGTH) * 0.8f, textBgPaint);        textBgPaint.setStyle(Paint.Style.STROKE);//设置空心        textBgPaint.setStrokeWidth(2);        textBgPaint.setColor(Color.parseColor("#aaaaaaaa"));        canvas.drawCircle(centerX, centerY, (circleRadius - ARC_LINE_LENGTH) * 0.8f, textBgPaint);        //上一行文字        textPaint.setTextSize(circleRadius >> 1);        String text = (int) (100 * progress / max) + "";        float textLen = textPaint.measureText(text);        //计算文字高度        textPaint.getTextBounds("8", 0, 1, textBounds);        float h1 = textBounds.height();        canvas.drawText(text, centerX - textLen / 2, centerY - circleRadius / 10 + h1 / 2, textPaint);        //分        textPaint.setTextSize(circleRadius >> 3);        textPaint.getTextBounds("分", 0, 1, textBounds);        float h11 = textBounds.height();        canvas.drawText("分", centerX + textLen / 2 + 5, centerY - circleRadius / 10 + h1 / 2 - (h1 - h11), textPaint);        //下一行文字        textPaint.setTextSize(circleRadius / 6);        text = "点击优化";        textLen = textPaint.measureText(text);        canvas.drawText(text, centerX - textLen / 2, centerY + circleRadius / 2.5f, textPaint);    }    public void setMax(int max) {        this.max = max;        invalidate();    }    //动画切换进度值(异步)    public void setProgress(final float progress) {        new Thread(new Runnable() {            @Override            public void run() {                float oldProgress = ProgressBarView2.this.progress;                for (int i = 1; i <= 100; i++) {                    ProgressBarView2.this.progress = oldProgress + (progress - oldProgress) * (1.0f * i / 100);                    postInvalidate();                    SystemClock.sleep(20);                }            }        }).start();    }    //直接设置进度值(同步)    public void setProgressSync(float progress) {        this.progress = progress;        invalidate();    }    /**     * 计算渐变效果中间的某个颜色值。     * 仅支持 #aarrggbb 模式,例如 #ccc9c9b2     */    public String calColor(float fraction, String startValue, String endValue) {        int start_a, start_r, start_g, start_b;        int end_a, end_r, end_g, end_b;        //start        start_a = getIntValue(startValue, 1, 3);        start_r = getIntValue(startValue, 3, 5);        start_g = getIntValue(startValue, 5, 7);        start_b = getIntValue(startValue, 7, 9);        //end        end_a = getIntValue(endValue, 1, 3);        end_r = getIntValue(endValue, 3, 5);        end_g = getIntValue(endValue, 5, 7);        end_b = getIntValue(endValue, 7, 9);        return "#" + getHexString((int) (start_a + fraction * (end_a - start_a)))                + getHexString((int) (start_r + fraction * (end_r - start_r)))                + getHexString((int) (start_g + fraction * (end_g - start_g)))                + getHexString((int) (start_b + fraction * (end_b - start_b)));    }    //从原始#AARRGGBB颜色值中指定位置截取,并转为int.    private int getIntValue(String hexValue, int start, int end) {        return Integer.parseInt(hexValue.substring(start, end), 16);    }    private String getHexString(int value) {        String a = Integer.toHexString(value);        if (a.length() == 1) {            a = "0" + a;        }        return a;    }}




1 0
原创粉丝点击