- 一:概述:
android的自定义view提供了很多很丰富的画布操作,这几天公司的项目需要写一个圆形进度条,借着这个机会,说一下画布的基本操作- 二:预期效果
我要实现的效果如图:
这是一个等级效果图,要求可以根据用户的等级,动态的改变蓝色进度条的停留位置。
三:实现思路:
canvas可以画很多基本的图形:长方形,圆形,圆角长方形,扇形…
根据图片我第一时间想到的就是用画弧度实现,也就是扇形。
由于需要有一个划过的动画,所以不能一次就画完,一次画一点,分多次画完。
这里有两种思路:
1.起始位置一样,每次画的弧度不同。
2.每次画的弧度一样,起始位置动态计算。
第一种思路实现起来简单粗暴,在这里直接选择第一种。
这个自定义view的难点就是在动画完成的时候计算结束位置有些许的麻烦,用到了一些几何知识,如果忘了就去翻翻初中的数学课本吧。
四:代码实现:
我们定义的一些变量,我都写了注释:
private RectF rectF;
private double sweepRadius = 0;//扫过的角度(默认是0)
boolean isRunning;//运行的控制器
float radius = 1f;//每一帧画完之后的角度
float speedRadius = 10f;//每次画的角度(可以理解为速度)
float starttPoint = 270f;//初始化的角度(默认在中间顶部
private Paint mPaint;
private int paintWidth = 2;//画笔的宽度
float finalRadius=0;
double pi =3.14159265358979323846264338327950288419716939937510582097494459230781640628620899;//定义圆周率
首先在初始化的时候,我们做了一些操作
mPaint = new Paint();
mPaint.setColor(0xff1ecfff);//画笔的颜色
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(paintWidth);//画笔的宽度
mPaint.setAntiAlias(true);//设置抗锯齿
在onSizeChanged函数中初始化画笔的绘画区域矩形rectF
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
rectF = new RectF(paintWidth,paintWidth,getWidth()-paintWidth,getHeight()-paintWidth);
}
下边就是我们的核心ondraw方法了,里边有一些逻辑:我都做了详细的注释
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.WHITE);//画布背景颜色
if (sweepRadius==0) return;//如果扫过的角度为0,则不用画
if (finalRadius!=0){
canvas.drawArc(rectF, starttPoint, finalRadius, false, mPaint);
return;
}
canvas.drawArc(rectF, starttPoint, radius, false, mPaint);
radius=radius+speedRadius ;
if (radius >= sweepRadius+1f){
//画圆结束
finalRadius = radius-speedRadius;
isRunning = false;
sweepRadius = finalRadius;//重新确切的告诉view扫过的角度
getStarPos();//计算画完后的坐标的方法
if (listener!=null){
listener.onDrawFinish(starX , starY);
}
}
if (!isRunning) return;
//刷新view,每50毫秒执行一次ondraw()
postInvalidateDelayed(50);
}
在画圈完成后我们有一个计算停留位置的方法getStarPos(),在计算的过程中,我们用到了圆周率,都是一些死方法,代码如下:
private double starX;// x坐标
private double starY;// y坐标
/**
* 得到画圈后的坐标点
*/
public void getStarPos(){
double r = (getWidth()-paintWidth*2)/2;//半径
if (sweepRadius==0){
starX = r;
starY = 0;
}else if (sweepRadius < 90d){
starX = r + Math.sin(sweepRadius*pi/180)*r;
starY = r - Math.cos(sweepRadius*pi/180)*r;
}else if (sweepRadius == 90d){
starX = r*2;
starY = r;
}else if (sweepRadius < 180d){
starX = r+Math.cos((sweepRadius-90d)*pi/180)*r;
starY = r+Math.sin((sweepRadius-90d)*pi/180)*r;
}else if (sweepRadius == 180d){
starX = r;
starY = r*2;
}else if (sweepRadius < 270d){
starX = r - Math.sin((sweepRadius-180d)*pi/180)*r;
starY = r + Math.cos((sweepRadius-180d)*pi/180)*r;
}else if (sweepRadius == 270d){
starX = 0;
starY = r;
}else if (sweepRadius < 360d){
starX = r - Math.cos((sweepRadius-270d)*pi/180)*r;
starY = r - Math.sin((sweepRadius-270d)*pi/180)*r;
}
}
为了把画完后的坐标传给外界,定义了一个借口监听
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
}
private OnDrawingFinishListener listener;
public void setOnDrawingFinishListener(OnDrawingFinishListener onDrawingFinishListener){
this.listener = onDrawingFinishListener;
}
public interface OnDrawingFinishListener{
void onDrawFinish(double xPos, double yPos);
}
以上就是我们的全部实现过程了。如有那些不理解或认为不妥的地方,请在下方给我留言。
下边是源码地址,里边有详细的用法。
github下载源码