自定义View之圆形加载条

 原文:https://www.jianshu.com/p/be71f6ffe512

 

整体流程: 

  1. 在value文件夹下新建 attrs 文件,加入style声明

  2.  在view类中定义 Paint 以及相应的变量 

  3. 在构造函数中,获取由XML文件传来的属性值 ,并完成对不同 Paint的初始化

  4.在onDraw() 方法中,用 canvas.drawArc() 对底层圆圈和进度圆圈以及数字进行绘制

  5. 用属性动画,用 animator.addUpdateListener() 方法将动画的更新与 View中的数值参数关联

  

 View类代码:

public class CircleProgressView extends View {


    private int mCurrent;
    private Paint mPaintOut;
    private Paint mPaintCurrent;
    private Paint mPaintText;
    private float mPaintWidth;
    private int mPaintColor = Color.GREEN;
    private int mTextColor = Color.BLUE;
    private float mTextSize;
    private int location;
    private float startAngle;

    private OnLoadCompleteListener onLoadCompleteListener;

    public CircleProgressView(Context context) {
        this(context,null);

    }

    public CircleProgressView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs,0);
    }

    public CircleProgressView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.CircleProgressView);
        location = array.getInt(R.styleable.CircleProgressView_location, 1);
        mPaintWidth = array.getDimension(R.styleable.CircleProgressView_progress_paint_width, dip2px(context, 8));//默认4dp
        mPaintColor = array.getColor(R.styleable.CircleProgressView_progress_paint_color, mPaintColor);
        mTextSize = array.getDimension(R.styleable.CircleProgressView_progress_text_size, dip2px(context, 18));//默认18sp
        mTextColor = array.getColor(R.styleable.CircleProgressView_progress_text_color, Color.BLACK);
        array.recycle();

        //灰色背景
        mPaintOut = new Paint();
        mPaintOut.setAntiAlias(true);
        mPaintOut.setStrokeWidth(mPaintWidth);
        mPaintOut.setStyle(Paint.Style.STROKE);
        mPaintOut.setColor(Color.GRAY);
        mPaintOut.setStrokeCap(Paint.Cap.ROUND);

        //彩色填充
        mPaintCurrent = new Paint();
        mPaintCurrent.setAntiAlias(true);
        mPaintCurrent.setStrokeWidth(mPaintWidth);
        mPaintCurrent.setStyle(Paint.Style.STROKE);
        mPaintCurrent.setColor(mPaintColor);
        mPaintCurrent.setStrokeCap(Paint.Cap.ROUND);

        mPaintText = new Paint();
        mPaintText.setAntiAlias(true);
        mPaintText.setStyle(Paint.Style.FILL);
        mPaintText.setColor(mTextColor);
        mPaintText.setTextSize(mTextSize);
        if (location == 1) {
            startAngle = -90;
        } else if (location == 2) {
            startAngle = 0;
        } else if (location == 3) {
            startAngle = 90;
        } else if (location == 4) {
            startAngle = 180;
        }
    }
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        int width = MeasureSpec.getSize(widthMeasureSpec);
        int height = MeasureSpec.getSize(heightMeasureSpec);
        int size = width > height ? height : width;

       
        setMeasuredDimension(size, size);

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



        //绘制灰色背景

        RectF rectF = new RectF(mPaintWidth / 2, mPaintWidth / 2,
                getWidth() - mPaintWidth / 2, getHeight() - mPaintWidth / 2);
        RectF rect = new RectF(mPaintWidth / 2, mPaintWidth / 2,
                getWidth() - mPaintWidth / 2, getHeight() - mPaintWidth / 2);

       // canvas.drawArc(rectF, -90, 360, false, mPaintOut);
        canvas.drawArc(rect, 0, 360, false, mPaintOut);

        //绘制进度条
        float sweepAngle = 360 * mCurrent / 100;
        canvas.drawArc(rect, startAngle, sweepAngle, false, mPaintCurrent);

        //绘制进度数字
        String text = mCurrent + "%";
        //获取文字宽度
        float textWidth = mPaintText.measureText(text, 0, text.length());
        float dx = getWidth() / 2 - textWidth / 2;
        Paint.FontMetricsInt fontMetricsInt = mPaintText.getFontMetricsInt();
        float dy = (fontMetricsInt.bottom - fontMetricsInt.top) / 2 - fontMetricsInt.bottom;
        float baseLine = getHeight() / 2 + dy;
        canvas.drawText(text, dx, baseLine, mPaintText);

        if (onLoadCompleteListener != null && mCurrent == 100) {
            onLoadCompleteListener.complete();
        }
    }



    public int getmCurrent() {
        return mCurrent;
    }

    public void setmCurrent(int mCurrent) {
        this.mCurrent = mCurrent;
        invalidate();
    }

    public void setOnLoadCompleteListener(OnLoadCompleteListener loadCompleteListener) {
        this.onLoadCompleteListener = loadCompleteListener;
    }

    public interface OnLoadCompleteListener {
        void complete();
    }

    public static int dip2px(Context context, float dpValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dpValue * scale + 0.5f);
    }
} 


 MainAcitivity 中onCreate() 方法:

  

 protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mCircleProgressView = (CircleProgressView) findViewById(R.id.circle_view);

        //进度条从0到100
        final ValueAnimator animator = ValueAnimator.ofFloat(0, 100);
        animator.setDuration(4000);
        animator.setInterpolator(new LinearInterpolator());
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float current = (float) animation.getAnimatedValue();
                mCircleProgressView.setmCurrent((int) current);
            }
        });
        animator.start();


        mCircleProgressView.setOnLoadCompleteListener(new CircleProgressView.OnLoadCompleteListener() {
            @Override
            public void complete() {
                animator.start();
            }
        });
    }

  

  PS: 在AS中继承View后,会提示生成构造函数,自动生成的构造函数之间不是依次调用关系,若将PAINT初始化放在其中,则不一定会被调用,当onDraw()时,就会有空指针异常。需要手动将其修改。

  如果view不需要从attrs获取属性,则可以另写一个 init()方法,在所有构造函数中都调用init()进行初始化。

posted @ 2019-03-03 20:53  Joooseph  阅读(269)  评论(0编辑  收藏  举报