圆形进度条

public class SLCircleProgress extends View {
    /**
     * 进度条所占用的角度
     */
    private static final int ARC_FULL_DEGREE = 360;
    //进度条个数
    private static final int COUNT = 200;
    //每个进度条所占用角度
    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 SLCircleProgress(Context context) {
        super(context);
        init();
    }


    public SLCircleProgress(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }


    public SLCircleProgress(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.TRANSPARENT);
        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 = MiscUtil.measure(widthMeasureSpec, MiscUtil.dipToPx(getContext(), Constant.DEFAULT_SIZE));
            height = MiscUtil.measure(heightMeasureSpec, MiscUtil.dipToPx(getContext(), Constant.DEFAULT_SIZE));


            //计算圆弧半径和圆心点
            circleRadius = Math.min(width, height) / 2;
            ARC_LINE_LENGTH = 10;
            ARC_LINE_WIDTH = 2;


            centerX = width / 2;
            centerY = height / 2;
        }

        Log.e("shiroKenn", "width + height ---  " + width + " ---  " + height);
        Log.e("shiroKenn", "widthMeasureSpec + heightMeasureSpec ---  " + widthMeasureSpec + " ---  " + heightMeasureSpec);
    }


    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.WHITE);
        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.GRAY);
                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.TRANSPARENT);
        canvas.drawCircle(centerX, centerY, (circleRadius - ARC_LINE_LENGTH) * 0.8f, textBgPaint);


        textBgPaint.setStyle(Paint.Style.STROKE);//设置空心
        textBgPaint.setStrokeWidth(2);
        textBgPaint.setColor(Color.TRANSPARENT);
        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 = SLCircleProgress.this.progress;
                for (int i = 1; i <= 100; i++) {
                    SLCircleProgress.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;
    }

    private boolean isDragging = false;

    @Override
    public boolean onTouchEvent(@NonNull MotionEvent 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);
                    if (listener != null) {
                        //監聽進度點擊的變化,這裏監聽的是百分比
                        listener.onProgressChanged(calDegreeByPosition(currentX, currentY) / ARC_FULL_DEGREE);
                    }
                    isDragging = true;
                }
                break;


            case MotionEvent.ACTION_MOVE:
                if (isDragging) {
                    //判断拖动时是否移出去了
                    if (checkOnArc(currentX, currentY)) {
                        setProgressSync(calDegreeByPosition(currentX, currentY) / ARC_FULL_DEGREE * max);
                        if (listener != null) {
                            //監聽進度點擊的變化,這裏監聽的是百分比
                            listener.onProgressChanged(calDegreeByPosition(currentX, currentY) / ARC_FULL_DEGREE);
                        }
                    } else {
                        isDragging = false;
                    }
                }
                break;

            case MotionEvent.ACTION_UP:
                isDragging = false;
                break;
        }
        return true;
    }


    /**
     * 判断该点是否在弧线上(附近)这个20 是为了扩大一部分区域 让手指点击方便
     */
    private boolean checkOnArc(float currentX, float currentY) {
        float distance = calDistance(currentX, currentY, centerX, centerY);
        float degree = calDegreeByPosition(currentX, currentY);
        return distance + 20 > circleRadius - ARC_LINE_WIDTH * 5 && distance - 20 < circleRadius + ARC_LINE_WIDTH * 5
                && (degree >= -20 && degree <= ARC_FULL_DEGREE + 20);
    }

    /**
     * 根据当前位置,计算出进度条已经转过的角度。
     */
    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;
    }

    private float calDistance(float x1, float y1, float x2, float y2) {
        return (float) Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
    }

    /**
     * 對外接口,監聽進度點擊的變化,這裏監聽的是百分比
     */
    public interface OnProgressChangeListener {
        void onProgressChanged(float progress);
    }

    private OnProgressChangeListener listener;

    public void setOnProgressChangeListener(OnProgressChangeListener onProgressChangeListener) {
        this.listener = onProgressChangeListener;
    }

}
public class MiscUtil {

    /**
     * 测量 View
     *
     * @param measureSpec
     * @param defaultSize View 的默认大小
     * @return
     */
    public static int measure(int measureSpec, int defaultSize) {
        int result = defaultSize;
        int specMode = View.MeasureSpec.getMode(measureSpec);
        int specSize = View.MeasureSpec.getSize(measureSpec);

        if (specMode == View.MeasureSpec.EXACTLY) {
            result = specSize;
        } else if (specMode == View.MeasureSpec.AT_MOST) {
            result = Math.min(result, specSize);
        }
        return result;
    }

    /**
     * dip 转换成px
     *
     * @param dip
     * @return
     */
    public static int dipToPx(Context context, float dip) {
        float density = context.getResources().getDisplayMetrics().density;
        return (int) (dip * density + 0.5f * (dip >= 0 ? 1 : -1));
    }

    /**
     * 获取数值精度格式化字符串
     *
     * @param precision
     * @return
     */
    public static String getPrecisionFormat(int precision) {
        return "%." + precision + "f";
    }

    /**
     * 反转数组
     *
     * @param arrays
     * @param <T>
     * @return
     */
    public static <T> T[] reverse(T[] arrays) {
        if (arrays == null) {
            return null;
        }
        int length = arrays.length;
        for (int i = 0; i < length / 2; i++) {
            T t = arrays[i];
            arrays[i] = arrays[length - i - 1];
            arrays[length - i - 1] = t;
        }
        return arrays;
    }

    /**
     * 测量文字高度
     * @param paint
     * @return
     */
    public static float measureTextHeight(Paint paint) {
        Paint.FontMetrics fontMetrics = paint.getFontMetrics();
        return (Math.abs(fontMetrics.ascent) - fontMetrics.descent);
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值