概述
这是一个自定义实线分数,进度展示的progressBar。
实现的效果差强人意,但我们可以一起探究下自定义控件的过程。
除此之外,我发现绘制弧线的过程,相对于绘制其他的图形,是比较难以理解的。我后面总结下绘制弧线的过程。
效果图
因为第一次做gif图形,效果不好。其实图形的变化是连贯的。
1. 实线实现的效果
2. 虚线实线的效果
实现过程分析
1.自定义控件,有两个方法几乎是必写的,那就是onMeasure(int widthMeasureSpec, int heightMeasureSpec)和onDraw(Canvas canvas)方法。
onMeasure()方法实现测量。如果我们直接将控件的大小给一个确定值或者match_parent,就可以不用重写该方法。
如果我们将控件的宽高属性设置为wrap_content,那么我们就必须重写该方法了,毕竟,若你不告诉系统,系统怎么会知道这个content是多大呢。
/**
* 如果测量模式不是EXACTLY(即精确测量),我们就将该View的长和宽都设置为250dp。
*
* @param widthMeasureSpec
* @param heightMeasureSpec
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int viewWidth,viewHeight;
/**
* 设置宽度
* 获取测量模式和测量的大小
*/
int specMode = MeasureSpec.getMode(widthMeasureSpec);
int specSize = MeasureSpec.getSize(widthMeasureSpec);
if (specMode == MeasureSpec.EXACTLY)// match_parent , accurate
{
viewWidth = specSize;
} else{
/**
* 如果view直接赋值为250,则是250px,通过下面的代码,将sp转化为dip(dp).
*/
viewWidth=(int)(2*TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,250,getResources().getDisplayMetrics()));
}
/**
* 设置高度
*/
specMode = MeasureSpec.getMode(heightMeasureSpec);
specSize = MeasureSpec.getSize(heightMeasureSpec);
if (specMode == MeasureSpec.EXACTLY)// match_parent , accurate
{
viewHeight = specSize;
Log.d("liang","viewHeight"+viewHeight);
} else{
viewHeight=(int)(2*TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,250,getResources().getDisplayMetrics()));
}
setMeasuredDimension(viewWidth, viewHeight);
}
onDraw()方法是绘制图形的时候调用的方法。我们需要绘制的元素有三个,圆弧(实现或者虚线),图片,分数。
在绘制过程中,需要实现颜色渐变的效果。我们借助Shader进行渲染。
mLinearGradient1 = new LinearGradient(0, 0, 0, 2*radius, new int[] {
firstColor, secondColor, thirdColor }, null,
Shader.TileMode.CLAMP);
mPaint.setShader(mLinearGradient1);
数组前的四个参数确定渲染的范围,int数组里面的三个元素是渐变的颜色。最后一个参数是渲染模式,感兴趣的读者可以自行查询LinearGradient的用法,这里不做过多的表述。
整个onDraw()方法的代码段:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPaint.setShader(mLinearGradient1);
RectF rectF=new RectF(50,50,2*radius,2*radius);
//Rect rect=new Rect(50,50,2*radius,2*radius);
//canvas.drawRect(rect,mPaint);
mPaint.setColor(Color.BLUE);
canvas.drawArc(rectF,-90,progress,false,mPaint);
Paint paint=new Paint();
paint.setColor(textColor);
paint.setTextAlign(Paint.Align.CENTER);
paint.setTextSize(textSize);
paint.setSubpixelText(false);
Paint.FontMetrics fontMetrics = paint.getFontMetrics();
float y = radius - fontMetrics.descent + (fontMetrics.bottom - fontMetrics.top) / 2;
int margin=(int)(radius*0.2);
int offest=(int)(radius*0.28);
Rect rect1=new Rect(radius-margin,radius-margin-offest,
radius+margin,radius+margin-offest);
canvas.drawBitmap(icon,null,rect1,paint);
canvas.drawText(grade2+"",radius,y+70,paint);
}
2.进度和数字变化
为了实现进度和数字变化,这里开了两个线程。并且在每个线程里面用postInvalidate()更新画布。
为了实现这个功能,我首先记录了圆弧画完一圈所用的时间,然后用这一圈所花费的时间除以分数的最大值,所得的结果就是每次更新分数所需要的时间。
这么说太过抽象,还是看代码吧:
private int progress=0;
/**
* 绘制的速度
*/