android:自定义圆环形百分比控件,代码简单,加载动画,代码详细说明

需求要写一个环形百分比,是这样的:

   

第一张是UI图,第二张做出来后,在layout上渲染出来的图。

首先,需要知道android的座标系是怎么样的,左上为(0,0)往右x轴增大,往下y轴增大

然后就直接上代码啦,变量不多,都有注释,也没有弄AttributeSet,想弄layout设置参数的,可以自己加一下,style文件写好配置,layout文件里设置,然后自定义控件里获取。

public class CirclePercentView extends View {
    // 圆画笔
    private Paint circlePaint;
    // 圆环画笔
    private Paint ringPaint;
    // 百分数画笔
    private Paint textPaint;
    //动态设置的底环颜色
    private String circleColor;
    //动态设置圆环颜色
    private String ringColor;
    private float percent = 35.2f;//设一个初始的百分比,超过0的话,能在Android studio layout文件下直接看到效果
    //控件宽度
    private int mWidth;
    //画圆环需要的RectF,因为只需要生成一次,假如刷新界面的话,他就会一直生成,虽然不影响,但是把它拿出来全局
    private RectF rectF;
    //文字大小
    private float textSize;
    //圆环宽度
    private int stroke;

    public CirclePercentView(Context context) {
        super(context);
        init();
    }

    public CirclePercentView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public CirclePercentView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    public CirclePercentView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init();
    }

    private void init() {
        circlePaint = new Paint();
        circlePaint.setAntiAlias(true);//设置抗锯齿
        circlePaint.setStyle(Paint.Style.STROKE);//设置绘画风格为边框
        //这个是个dp转px的方法,不严格的话可以随便写个10,12,15随意
        stroke = AppUtils.dip2px(getContext(), 10);
        circlePaint.setStrokeWidth(stroke);//设置边框宽度
        circlePaint.setColor(getResources().getColor(R.color.blue_ff));//设置颜色,可以替换成Color.parseColor("#xxxxxx")

        ringPaint = new Paint();
        ringPaint.setAntiAlias(true);
        ringPaint.setStyle(Paint.Style.STROKE);
        ringPaint.setStrokeWidth(stroke);
        ringPaint.setColor(getResources().getColor(R.color.blue_24));//可以替换成Color.parseColor("#xxxxxx")

        textPaint = new Paint();
        textPaint.setStyle(Paint.Style.FILL);//设置风格为充满
        textPaint.setAntiAlias(true);
        textPaint.setColor(getResources().getColor(R.color.black));
        textSize = AppUtils.dip2px(getContext(), 14);
        textPaint.setTextSize(textSize);
    }

    /**
     * 设置颜色和中间的文字
     * @param color 这个color是字符串,不带#号6位
     * @param percent 传0-100的
     */
    public void setTextColor(String color, double percent) {
        circleColor = "#80" + color;//让背景环颜色为环颜色透明一些,
        ringColor = "#" +color;
        startAnimator((float) percent);//开启动画
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mWidth = getWidth();
        int mHeight = getHeight();
        if(mWidth > mHeight){
            mWidth = mHeight;//获取最短的一边
        }
        rectF = new RectF();//rectF为画扇形所需要的矩形,扇形会在矩形内
        //rectF长宽为right-left或bottom-top,因为是正方形,都一样
        //因为画笔使用了stroke,他半径不为控件的一半了,不设置stroke的话长宽都是是圆半径*2,
        // 长宽 = mWidth - stroke,
        //因为rectF范围不是满控件了,所以位置不能是0 0不然画出来,会偏左上角,
        // 为了使rectF居中,都往右下角偏移了stroke/2,所以也要少减stroke/2,
        rectF.left = stroke/2;
        rectF.top = stroke/2;
        //知道了长宽,通过左上可以计算右下
        rectF.right = mWidth - stroke/2;
        rectF.bottom = mWidth - stroke/2;
        //可以自己设置成rectF.left = 0; rectF.top = 0; rectF.right = mWidth - stroke; rectF.bottom = mWidth - stroke
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        if (circleColor != null) {
            circlePaint.setColor(Color.parseColor(circleColor));//如果有颜色的话,设置颜色
        }
        if (ringColor != null) {
            ringPaint.setColor(Color.parseColor(ringColor));//如果有颜色的话,设置颜色颜色
        }
        //画外层圆环   当画笔设置了 StrokeWidth 时,圆的半径=内圆的半径+StrokeWidth/2
        canvas.drawCircle(mWidth/2, mWidth/2, (mWidth/2 - stroke) + stroke/2, circlePaint);
        float point = percent*360/100;
        if (point > 360) {
            point = 360;
        }
        //rectF扇形范围,-90,从上开始绘画,比如改成0,就是从左开始绘画,顺时针,point绘画多少角度
        // false是去掉扇形的半径绘画,ringPaint画笔
        canvas.drawArc(rectF, -90, point, false, ringPaint);
        String str = percent + "%";//需要自定义文字的话,就定义一个全局str,从外面传进来
        float textWid = textPaint.measureText(str);
        //str文字,第二个和第三个参数是为了使他居中mWidth/2 - textWid/2:从x轴的哪里开始绘画,应该很好计算
        //mWidth/2 + textSize/2 之所以是+是因为文字是从左下角开始绘制的,开始以为是从上到下,写成了-,就偏上了
        canvas.drawText(str, mWidth/2 - textWid/2, mWidth/2 + textSize/2, textPaint);
    }

    private void startAnimator(float process) {
        ValueAnimator animator = ValueAnimator.ofFloat(0, process);//从百分0到process的过程
        animator.setDuration(800);//持续时间
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                percent = (Float) animation.getAnimatedValue();//当这个动画过程进行过程中,获取过程中的数值
                BigDecimal b = new BigDecimal((Float) animation.getAnimatedValue());
                percent = b.setScale(2, ROUND_DOWN).floatValue();//保留两位小数
                invalidate();//重绘,会重新走onDraw,onDraw里是根据percent绘画的,所以界面会改变
            }
        });
        animator.start();
    }
}

View是先走onMeasure ->onSizeChanged->onDraw 因此,在onSizeChange获取控件宽度。

动画的原理是,让角度慢慢增长,通过使百分比从0开始,一段时间增长至所设置的百分比,在百分比改变的时候,将界面进行重绘

当画圆和扇形,画笔设置成了stroke,它的半径就不是想当然的控件宽度除以2了,假如是像上面代码圆环充满控件,它的宽度就是控件宽度-环的宽度除以2,这个是要自己计算的,同理能得出画扇形所需要的矩形,注意让矩形位置居中。自己画了张图,额,字比较丑,将就一下

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章