自定义view之仿QQ健康ArcProgressbar

前言

对于很多android初级程序员来说,自定义view都是一件恐惧的事。但是进阶过程中又不得不克服。所以这几天一直在玩各种自定义view,学了过后才发现,这个也不是那么恐惧的一件事。

之前一直觉得QQ上的很多控件做得不错,所以趁着现在在进阶自定义view,仿一仿QQ上的一些控件。

那么今天我仿的是QQ健康的弧形进度条控件。
先上一下QQ原版的效果:

下面是我仿的效果:
自定义view之仿QQ健康ArcProgressbar_第1张图片

步骤

对于这个自定义View,暂时不打算加入监听事件,主要是实现了里面的小动画和一些界面的东西。

自定义view的那几个步骤:

  • 自定义View的属性
  • 在构造函数里面获取自定义的属性
  • 重写onMesure
  • 重写ondraw

要实现这个界面:
1、首先我们得在attr.xml中添加我们的一些自定义的属性

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="CircleProgressBar">
        <attr name="progressbar_width" format="dimension" />
        <attr name="progressbar_color" format="color" />
        <attr name="title_text" format="string" />
        <attr name="text_size" format="dimension" />
        <attr name="text_color" format="color" />
        <attr name="progressbar_unreached_color" format="color" />
        <attr name="max_value" format="integer" />
        <attr name="text_unit" format="string" />
        <attr name="progress_num_size" format="dimension" />
        <attr name="progress_num_color" format="color" />
            <attr name="max_progressbar_angle" format="integer" />//圆弧角度0~360
    </declare-styleable>
</resources>

2、创建我们的customView,并在构造函数里面初始化我们的画笔还有我们的自定义属性:

    public CircleProgressBar(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public CircleProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.mContext = context;
        init(attrs);
        initView();
    }
   private void init(AttributeSet attrs) {

        TypedArray typedArray = mContext.obtainStyledAttributes(attrs, R.styleable.CircleProgressBar);
        this.mProgressBarColor = typedArray.getColor(R.styleable.CircleProgressBar_progressbar_color, Color.BLUE);
        this.mProgressbarWidth = typedArray.getDimension(R.styleable.CircleProgressBar_progressbar_width, dipToPx(10));
        this.mTitleText = typedArray.getString(R.styleable.CircleProgressBar_title_text);
        this.textSize = typedArray.getDimension(R.styleable.CircleProgressBar_text_size, dipToPx(15));
        this.textColor = typedArray.getColor(R.styleable.CircleProgressBar_text_color, Color.BLACK);
        this.mProgressbarUnreachedColor = typedArray.getColor(R.styleable.CircleProgressBar_progressbar_unreached_color, Color.GRAY);
        this.maxValue = typedArray.getInteger(R.styleable.CircleProgressBar_max_value, 100);
        this.mTextUnit = typedArray.getString(R.styleable.CircleProgressBar_text_unit);
        this.numSize = typedArray.getDimension(R.styleable.CircleProgressBar_progress_num_size, dipToPx(80));
        this.numColor = typedArray.getColor(R.styleable.CircleProgressBar_progress_num_color, Color.BLACK);
        setCircleAngle(typedArray.getInteger(R.styleable.CircleProgressBar_max_progressbar_angle, 300));
        typedArray.recycle();
    }

    private void initView() {

        mCircleProgressBarPaint = new Paint();
        mCircleProgressBarPaint.setStyle(Paint.Style.STROKE);
        mCircleProgressBarPaint.setColor(mProgressBarColor);
        mCircleProgressBarPaint.setStrokeWidth(mProgressbarWidth);
        mCircleProgressBarPaint.setStrokeCap(Paint.Cap.ROUND);
        mCircleProgressBarPaint.setAntiAlias(true);

        mCircleProgressBarUnreachedPaint = new Paint();
        mCircleProgressBarUnreachedPaint.setStyle(Paint.Style.STROKE);
        mCircleProgressBarUnreachedPaint.setColor(mProgressbarUnreachedColor);
        mCircleProgressBarUnreachedPaint.setStrokeCap(Paint.Cap.ROUND);
        mCircleProgressBarUnreachedPaint.setStrokeWidth(mProgressbarWidth);
        mCircleProgressBarUnreachedPaint.setAntiAlias(true);

        //除了数字的其他文字
        mTextPaint = new Paint();
        mTextPaint.setColor(textColor);
        mTextPaint.setTextSize(textSize);
        mTextPaint.setTextAlign(Paint.Align.CENTER);
        mTextPaint.setAntiAlias(true);

        //数字
        mNumPaint = new Paint();
        mNumPaint.setColor(numColor);
        mNumPaint.setTextSize(numSize);
        mNumPaint.setTextAlign(Paint.Align.CENTER);
        mNumPaint.setAntiAlias(true);
    }

这里没有什么要说的,主要是熟悉下对Paint类的应用:

  • setTextAlign是设置文字的基准(横向)。后面会用到
  • setAntiAlias是设置抗锯齿,效果很明显的,哈哈。
  • setStrokeCap是设置画笔末端的效果,我这里设置的是圆形,所以就有进度条末端是圆形的那种效果。
  • setStrokeWidth是设置画笔的宽度,效果对应上图中,进度条的宽度。

Paint类还有很多的效果,大家可以去试下,本篇博客就不详细讲了。

3、有必要的话,重写onMesure

  @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int width = MeasureSpec.getSize(widthMeasureSpec);
        int height = MeasureSpec.getSize(heightMeasureSpec);
        int min = Math.min(width, height);
        setMeasuredDimension(min, min);
    }

这里我把这个view固定成了一个正方形,因为这个view相当于一个圆,所以说一个正方形足已。
4、重写ondraw

 @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //第一次draw的时候,构造一个`RectF`矩形,后面可以在这个矩形里面画圆弧
        if ((centerX & centerY) == 0) {
            radius = (int) (getWidth() / 2 - mProgressbarWidth / 2);
            centerX = radius + (int) mProgressbarWidth / 2;
            centerY = radius + (int) mProgressbarWidth / 2;
            rectF = new RectF();
            rectF.left = mProgressbarWidth / 2;
            rectF.top = mProgressbarWidth / 2;
            rectF.right = getWidth() - mProgressbarWidth / 2;
            rectF.bottom = getHeight() - mProgressbarWidth / 2;
        }
        //画后面灰色的圆弧
        canvas.drawArc(rectF, startCircleAngle, maxProgressbarAngle, false, mCircleProgressBarUnreachedPaint);
        if (currentProgress >= progress) {
            animateFinish = true;
        }
        //画progressbar的圆弧
        canvas.drawArc(rectF, startCircleAngle, getProgressAngle(), false, mCircleProgressBarPaint);
        //把文字画上去
        if (mTitleText != null && !mTitleText.trim().isEmpty()) {
            canvas.drawText(mTitleText,
                    centerX, centerY - 2 * numSize / 3, mTextPaint);
            canvas.drawText(String.valueOf(currentProgress),
                    centerX, centerY + numSize / 3, mNumPaint);
            canvas.drawText(mTextUnit, centerX, centerY + 2 * numSize / 3 + 2 * textSize / 3, mTextPaint);
        }
        if (!animateFinish)
            invalidate();
    }

思路还是挺简单的, 主要是最后一句invalidate(),因为我们要实现动画效果,那么就得不断绘制,所以在ondraw运行完的时候,调用invalidate(),继续循环调用draw,然后在setProgress的时候:

  public void setProgress(int value) {
        if (value > maxValue) {
            value = maxValue;
        }
        if (value < 0) {
            value = 0;
        }
        this.progress = value;
        setAnimation(0, value, animationDuring);

    }

    private void setAnimation(int start, int end, int during) {
        ValueAnimator animator = new ValueAnimator();
        animator.setDuration(during);
        animator.setIntValues(start, end);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                currentProgress = (int) animation.getAnimatedValue();
            }
        });
        animateFinish = false;
        postInvalidate();
        animator.start();
    }

这里我们用了ValueAnimator动画,使currentProgress 动态变化,那么我们每次在ondraw的时候,就会绘制出不同长度的progressbar,达到动画效果。

思路很简单,代码写得很乱,如果要查看源代码的童鞋也以传送到这里。

后面我还会写一些自定义view的博客,如果大家有好看的view的话,欢迎发出来,我们一起来实现,一起学习。

你可能感兴趣的:(android,控件,自定义view)