带动效的环形进度图

效果图(完整代码在文章末尾贴出)

环形进度图

图表分析

图表分析

  1. 确定控件width、height,Math.min(width, height)获取当中的最小值,设定为圆形的直径(也可减去一个数值作为预留的边距,但要注意在以后的绘图中计算进去)

  2. 绘制背景色圆形:canvas.drawCircle(float centerX, float centerY, float radius, Paint paint); 参数说明:(圆心坐标点x, 圆心坐标点y, 圆形半径, 画笔)

  3. 绘制进度弧线:canvas.drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint); 具体使用说明移步http://www.cnblogs.com/tjudzj/p/4387145.html

  4. 绘制前景色圆形:前景色圆形半径 = 背景色半径 - 进度条的宽度

代码说明

创建java文件继承自View, 并实现构造方法

public class CircleRingGraph extends View {

    public CircleRingGraph(Context context, AttributeSet attrs) {
        super(context, attrs);
        initDefineAttr(context, attrs);
        initPaint();
    }

    public CircleRingGraph(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initDefineAttr(context, attrs);
        initPaint();
    }
}

initDefineAttr(context, attrs);

该方法内部获取xml文件里的自定义的属性信息

如何添加自定义的属性信息:

  1. 在values文件夹下创建attrs.xml文件
  2. 声明自定义属性信息的名称、数值类型(name、format),如下:
<!-- 圆形进度条类型图表的相关属性 -->
    <declare-styleable name="CircleRingGraph">
        <!-- 进度条的背景色 -->
        <attr name="progressBackgroundColor" format="color" />
        <!-- 进度条颜色 -->
        <attr name="progressColor" format="color" />
        <!-- 中心圆形的颜色 -->
        <attr name="circleHeartColor" format="color" />
        <!-- 进度条的宽度 -->
        <attr name="progressWidth" format="dimension" />
        <!-- 进度条是否显示圆形头部 -->
        <attr name="progressRoundCap" format="boolean" />
        <!-- 圆心显示的文字 -->
        <attr name="centerText" format="string" />
        <!-- 圆心显示的文字颜色 -->
        <attr name="centerTextColor" format="color" />
        <!-- 圆心显示的文字大小 -->
        <attr name="centerTextSize" format="dimension" />
    </declare-styleable>
  1. 在自定义View里边获取并设置xml文件里的自定义属性信息,即initDefineAttr(context, attrs) 方法里的内容
private void initDefineAttr(Context context, AttributeSet attrs) {

        if (attrs == null)
            return;

        DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();

        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CircleRingGraph);

        int count = typedArray.getIndexCount();
        for (int i = 0; i < count; i++) {

            int attr = typedArray.getIndex(i);
            switch (attr) {

                case R.styleable.CircleRingGraph_progressBackgroundColor:

                    colorOut = typedArray.getColor(attr, context.getResources().getColor(R.color.circle_out_blue));
                    break;

                case R.styleable.CircleRingGraph_progressColor:

                    colorPro = typedArray.getColor(attr, context.getResources().getColor(R.color.circle_progress_blue));
                    break;

                case R.styleable.CircleRingGraph_circleHeartColor:

                    colorIn = typedArray.getColor(attr, context.getResources().getColor(R.color.circle_in));
                    break;

                case R.styleable.CircleRingGraph_progressRoundCap:

                    roundCap = typedArray.getBoolean(attr, Boolean.FALSE);
                    break;

                case R.styleable.CircleRingGraph_progressWidth:

                    strokeWidth = typedArray.getDimension(attr, DEFAULT_STROKE_WIDTH);
                    break;

                case R.styleable.CircleRingGraph_centerText:

                    centerText = typedArray.getString(attr);
                    break;

                case R.styleable.CircleRingGraph_centerTextColor:

                    centerTextColor = typedArray.getColor(attr, context.getResources().getColor(R.color.colorBlack));
                    break;

                case R.styleable.CircleRingGraph_centerTextSize:

                    centerTextSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX,
                            typedArray.getDimensionPixelSize(attr, 16), displayMetrics);
                    break;

                default:
                    break;
            }
        }

        typedArray.recycle();

    }

initPaint() 用来设置背景圆、进度条、前景圆、文本数值的Paint的基本信息,放在initDefineAttr() 之后是因为要将自定义的一些属性值设置给Paint。

private void initPaint() {

        paintOut = new Paint();
        paintOut.setAntiAlias(true);
        paintOut.setStyle(Paint.Style.FILL);
        paintOut.setColor(colorOut);

        paintIn = new Paint();
        paintIn.setAntiAlias(true);
        paintIn.setStyle(Paint.Style.FILL);
        paintIn.setColor(colorIn);

        paintPro = new Paint();
        paintPro.setAntiAlias(true);
        if (roundCap)
            paintPro.setStrokeCap(Paint.Cap.ROUND);
        paintPro.setStyle(Paint.Style.STROKE);
        paintPro.setStrokeWidth(strokeWidth);
        paintPro.setColor(colorPro);

        paintTxt = new Paint();
        paintTxt.setAntiAlias(true);
        paintTxt.setTextSize(centerTextSize);
        paintTxt.setTextAlign(Paint.Align.CENTER);
        paintTxt.setColor(centerTextColor);
    }

onSizeChanged()内主要是获取并设置控件的基本数值信息

@Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {

        radiusOut = Math.min(w, h) / 2 - DEFAULT_PADDING;
        radiusIn = radiusOut - strokeWidth;
        centerX = w / 2;
        centerY = h / 2;

        float left = centerX - radiusOut + strokeWidth / 2;
        float top = centerY - radiusOut + strokeWidth / 2;
        float right = left + radiusOut * 2 - strokeWidth;
        float bottom = top + radiusOut * 2 - strokeWidth;
        rectF = new RectF(left, top, right, bottom);

        textBaseLineX = centerX;
        textBaseLineY = centerY + centerTextSize / 3;

        super.onSizeChanged(w, h, oldw, oldh);
    }

onSizeChanged() 当中用到的一些变量的解释

radiusOut :背景色圆形的半径

DEFAULT_PADDING:默认预留的控件的Padding

radiusIn :前景色圆形的半径

centerX :控件的中心坐标点 x

centerY:控件的中心坐标点 y

strokeWidth:进度条的宽度

rectF :弧形进度条所占的正方形区域

textBaseLineX :中心数值的基线坐标点 x

textBaseLineY :中心数值的基线坐标点 y

当所有的信息都齐活之后,接下来就是绘制了,绘制当然是用onDraw() 方法了, 废话少说,看代码

@Override
    protected void onDraw(Canvas canvas) {

        drawCircleRing(canvas);
        super.onDraw(canvas);
    }

    /**
     * draw circle ring
     */
    private void drawCircleRing(Canvas canvas) {

        // 画灰色的圆
        canvas.drawCircle(centerX, centerY, radiusOut, paintOut);

        // 画进度环
        if (progress != 0f) {

            canvas.drawArc(rectF, -90, progress, false, paintPro);
        }

        // 画内填充圆
        canvas.drawCircle(centerX, centerY, radiusIn, paintIn);

        //画中心位置文字
        canvas.drawText((int) progress + "", textBaseLineX, textBaseLineY, paintTxt);
    }

看起来soEasy对不对?对的,就是这么简单,不过要注意一点canvas.drawArc()的时候,因为我默认设置的是progress = 0;所以加上if(progress != 0f)的判断,不然界面一进来就默认绘制一段进度条

现在问题来了,既然是带动效的,那一定要能够动起来的。好,那就来加点特效,加动画!

public void doAnimation() {

        ValueAnimator animator = ValueAnimator.ofObject(new ProgressEvaluator(), 0f, progress);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {

                progress = (float) animation.getAnimatedValue();
                invalidate();
            }
        });
        animator.setDuration(1000);
        animator.setInterpolator(new OvershootInterpolator());
        animator.start();
    }

    private class ProgressEvaluator implements TypeEvaluator<Float> {
        @Override
        public Float evaluate(float fraction, Float startValue, Float endValue) {

            return startValue + fraction * (endValue - startValue);
        }
    }

看代码页依然没什么难的,重点有三:

1、 ProgressEvaluator() 的实现,主要在于startValue -> endValue 的关系公式的实现

2、 onAnimationUpdate(ValueAnimator animation) 方法内记得调用invalidate() 来刷新界面,不然怎么会动呢?

3、 动效设置 animator.setInterpolator(new OvershootInterpolator()) Android默认提供了集中动效插值器,各位可以自己尝试下效果。

有关动画的详细的知识,请移步: http://blog.csdn.net/harvic880925/article/details/50995268

本篇完整代码

package customview;

import android.animation.TypeEvaluator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.BlurMaskFilter;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.SweepGradient;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.View;
import android.view.animation.OvershootInterpolator;

import com.example.zpf.animmenu.R;

/**
 * Created by zpf on 2016/11/7.
 */
public class CircleRingGraph extends View {

    /**
     * 默认的Padding
     */
    private final float DEFAULT_PADDING = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
            16, getResources().getDisplayMetrics());

    /** 默认的进度条的宽度 */
    private final float DEFAULT_STROKE_WIDTH = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
            16, getResources().getDisplayMetrics());

    /**
     * centerX:中心坐标点x
     * centerY:中心坐标点y
     * radiusOut:背景色圆形的半径
     * radiusIn:前景色圆形的半径
     * */
    private float centerX, centerY, radiusOut, radiusIn;

    /**
     * paintOut:背景色圆形画笔
     * paintIn:前景色圆形画笔
     * paintPro:进度条的画笔
     * paintTxt:中心数据值文本的画笔
     * */
    private Paint paintOut, paintIn, paintPro, paintTxt;

    /**
     * colorOut: 进度条背景色
     * colorIn: 圆心背景色
     * colorPro: 进度条的颜色
     */
    private int colorOut = getResources().getColor(R.color.colorGray),
            colorIn = getResources().getColor(R.color.circle_in),
            colorPro = getResources().getColor(R.color.circle_progress_blue);
    /**
     * 用来画圆弧的RectF
     */
    private RectF rectF;

    /**
     * 进度条的宽度
     */
    private float strokeWidth = DEFAULT_STROKE_WIDTH;

    /**
     * 显示圆形进度条的头部
     */
    private boolean roundCap = Boolean.FALSE;
    /**
     * 进度条显示的进度
     */
    private float progress = 0;
    /**
     * 圆心位置显示的Text
     * centerText: 圆心位置的文本信息
     * colorCenterText: TextView文字颜色
     * centerTextSize: TextView文字大小
     */
    private String centerText;
    private int centerTextColor = getResources().getColor(R.color.colorBlack);
    private float centerTextSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 16,
            getResources().getDisplayMetrics());

    /**
     * textBaseLineX:绘制文本的基线x坐标点
     * textBaseLineY:绘制文本的基线y坐标点
     * */
    private float textBaseLineX, textBaseLineY;

    public CircleRingGraph(Context context) {
        super(context);
    }

    public CircleRingGraph(Context context, AttributeSet attrs) {
        super(context, attrs);
        initDefineAttr(context, attrs);
    }

    public CircleRingGraph(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initDefineAttr(context, attrs);
    }

    public float getProgress() {
        return progress;
    }

    public void setProgress(float progress) {
        this.progress = progress;
        doAnimation();
    }

    /**
     * 获取自定义的属性值
     */
    private void initDefineAttr(Context context, AttributeSet attrs) {

        if (attrs == null)
            return;

        DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CircleRingGraph);
        int count = typedArray.getIndexCount();
        for (int i = 0; i < count; i++) {

            int attr = typedArray.getIndex(i);
            switch (attr) {

                case R.styleable.CircleRingGraph_progressBackgroundColor:

                    colorOut = typedArray.getColor(attr, context.getResources().getColor(R.color.circle_out_blue));
                    break;

                case R.styleable.CircleRingGraph_progressColor:

                    colorPro = typedArray.getColor(attr, context.getResources().getColor(R.color.circle_progress_blue));
                    break;

                case R.styleable.CircleRingGraph_circleHeartColor:

                    colorIn = typedArray.getColor(attr, context.getResources().getColor(R.color.circle_in));
                    break;

                case R.styleable.CircleRingGraph_progressRoundCap:

                    roundCap = typedArray.getBoolean(attr, Boolean.FALSE);
                    break;

                case R.styleable.CircleRingGraph_progressWidth:

                    strokeWidth = typedArray.getDimension(attr, DEFAULT_STROKE_WIDTH);
                    break;

                case R.styleable.CircleRingGraph_centerText:

                    centerText = typedArray.getString(attr);
                    break;

                case R.styleable.CircleRingGraph_centerTextColor:

                    centerTextColor = typedArray.getColor(attr, context.getResources().getColor(R.color.colorBlack));
                    break;

                case R.styleable.CircleRingGraph_centerTextSize:

                    centerTextSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX,
                            typedArray.getDimensionPixelSize(attr, 16), displayMetrics);
                    break;

                default:
                    break;
            }
        }

        typedArray.recycle();

        initPaint();
    }

    /**
     * init Paint
     */
    private void initPaint() {

        paintOut = new Paint();
        paintOut.setAntiAlias(true);
        paintOut.setStyle(Paint.Style.FILL);
        paintOut.setColor(colorOut);

        paintIn = new Paint();
        paintIn.setAntiAlias(true);
        paintIn.setStyle(Paint.Style.FILL);
        paintIn.setColor(colorIn);

        paintPro = new Paint();
        paintPro.setAntiAlias(true);
        if (roundCap)
            paintPro.setStrokeCap(Paint.Cap.ROUND);
        paintPro.setStyle(Paint.Style.STROKE);
        paintPro.setStrokeWidth(strokeWidth);
        paintPro.setColor(colorPro);

//        setLayerType(LAYER_TYPE_SOFTWARE, null);

        //setMaskFilter
//        paintPro.setMaskFilter(new BlurMaskFilter(1, BlurMaskFilter.Blur.INNER));
        //set shadow
//        paintPro.setShadowLayer(40, 0, 0, getResources().getColor(R.color.black_alpha_3));

        //雷达渐变
//        Shader shaderPro = new SweepGradient(centerX, centerY,
//                getResources().getColor(R.color.colorPink), getResources().getColor(R.color.colorGreen));
//        paintPro.setShader(shaderPro);

        //线性渐变
//        Shader shaderLinear = new LinearGradient(0, 0, 200, 0, Color.WHITE, Color.RED,
//                Shader.TileMode.CLAMP);
//        paintPro.setShader(shaderLinear);

        paintTxt = new Paint();
        paintTxt.setAntiAlias(true);
        paintTxt.setTextSize(centerTextSize);
        paintTxt.setTextAlign(Paint.Align.CENTER);
        paintTxt.setColor(centerTextColor);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {

        radiusOut = Math.min(w, h) / 2 - DEFAULT_PADDING;
        radiusIn = radiusOut - strokeWidth;
        centerX = w / 2;
        centerY = h / 2;

        float left = centerX - radiusOut + strokeWidth / 2;
        float top = centerY - radiusOut + strokeWidth / 2;
        float right = left + radiusOut * 2 - strokeWidth;
        float bottom = top + radiusOut * 2 - strokeWidth;
        rectF = new RectF(left, top, right, bottom);

        textBaseLineX = centerX;
        textBaseLineY = centerY + centerTextSize / 3;

        super.onSizeChanged(w, h, oldw, oldh);
    }

    @Override
    protected void onDraw(Canvas canvas) {

        drawCircleRing(canvas);
        super.onDraw(canvas);
    }

    /**
     * draw circle ring
     */
    private void drawCircleRing(Canvas canvas) {

        // 画灰色的圆
        canvas.drawCircle(centerX, centerY, radiusOut, paintOut);

        // 画进度环
        if (progress != 0f) {

            canvas.drawArc(rectF, -90, progress, false, paintPro);
        }

        // 画内填充圆
        canvas.drawCircle(centerX, centerY, radiusIn, paintIn);

        //画中心位置文字
        canvas.drawText((int) progress + "", textBaseLineX, textBaseLineY, paintTxt);
    }

    public void doAnimation() {

        ValueAnimator animator = ValueAnimator.ofObject(new ProgressEvaluator(), 0f, progress);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {

                progress = (float) animation.getAnimatedValue();
                invalidate();
            }
        });
        animator.setDuration(1000);
        animator.setInterpolator(new OvershootInterpolator());
        animator.start();
    }

    private class ProgressEvaluator implements TypeEvaluator<Float> {
        @Override
        public Float evaluate(float fraction, Float startValue, Float endValue) {

            return startValue + fraction * (endValue - startValue);
        }
    }
}

自定义的额属性相关的标签内容如下:

<!-- 圆形进度条类型图表的相关属性 -->
<declare-styleable name="CircleRingGraph">
    <!-- 进度条的背景色 -->
    <attr name="progressBackgroundColor" format="color" />
    <!-- 进度条颜色 -->
    <attr name="progressColor" format="color" />
    <!-- 中心圆形的颜色 -->
    <attr name="circleHeartColor" format="color" />
    <!-- 进度条的宽度 -->
    <attr name="progressWidth" format="dimension" />
    <!-- 进度条是否显示圆形头部 -->
    <attr name="progressRoundCap" format="boolean" />
    <!-- 圆心显示的文字 -->
    <attr name="centerText" format="string" />
    <!-- 圆心显示的文字颜色 -->
    <attr name="centerTextColor" format="color" />
    <!-- 圆心显示的文字大小 -->
    <attr name="centerTextSize" format="dimension" />
</declare-styleable>

如有问题,欢迎留言评论!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值