对于很多android初级程序员来说,自定义view都是一件恐惧的事。但是进阶过程中又不得不克服。所以这几天一直在玩各种自定义view,学了过后才发现,这个也不是那么恐惧的一件事。
之前一直觉得QQ上的很多控件做得不错,所以趁着现在在进阶自定义view,仿一仿QQ上的一些控件。
那么今天我仿的是QQ健康的弧形进度条控件。
先上一下QQ原版的效果:
对于这个自定义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的话,欢迎发出来,我们一起来实现,一起学习。