遇到复杂的问题,先把复杂的问题分解成一个一个小问题,然后一个一个的解决,复杂问题也就显得不再复杂。
先看看最后的=实现的效果图:
对于这个功能的实现,可以分为3个部分 就可以搞定。
part1:
3个部分中 最难分析的是part1:因为android提供的api中画扇形代码如下,
public void drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint)
四个参数分别为:
- oval: 圆弧所在的椭圆。
- startAngle: 圆弧起始角度,单位为度。
- sweepAngle: 圆弧扫过的角度,顺时针方向,单位为度。
- useCenter: 如果为True时,在绘制圆弧时将圆心包括在内,通常用来绘制扇形。
- paint: 绘制圆弧的画板属性,如颜色,是否填充等。
注意:通过面向对象写圆弧的javaBean的时候,要根据onDraw中画图的参数(startAngle,sweepAngle)去确定,不能凭空想象(自己凭想象犯了错).
javaBean:
public class PiePartBean {
int color;//扇形区域的颜色
double percent;//百分比 每个部分占总的百分比 如0.2
int endPointX;//扇形末端的点 用来链接圆心画直线
int endPointY;//扇形末端的点 用来链接圆心画直线
int radius;//扇形所对圆的半径
public int startArc;//
public int moveArc;//划过的角度
public boolean drawLine = false;
}
实现逻辑:
这里需要区分2个大情况,①:是否是第一个扇形,因为第一个扇形的固定从-90°(也就是12点方向开始的),而其他圆弧都是依赖于第一个扇形和之前的扇形。②:在扇形百分比递增的时候,考虑有没有超过最大百分比。先确定基准扇形的位置和大小,这里也就是i=0的情况,i=1的扇形的起始位置就是i=0的结束位置(startArc = -90+moveArc),i=1的扇形moveArc 也为递增percent360°,当超过上限的percent的时候就为上限percent360°;同理i=2的扇形的起始角度(startArc = -90+(i=0的moveArc)+(i=1的moveArc)),划过的扇形范围和i=1的情况相同.
可得结论:每个扇形的起始位置(startArc)都与之前部分的扇形的moveArc相关.
/**先判断satarArc 再判断moveArc*/
if (i == 0){
//第一个arc的起始角度 startArc = -90°
piePartBeanArrayList.get(i).startArc = -90;
//再判断moveArc
if(mCurrentPercent<piePartBeanArrayList.get(i).percent){
//还没有达到上限百分比
//
piePartBeanArrayList.get(i).moveArc =(int) (mCurrentPercent*360);
}else{
Log.d(TAG,"第"+i+"个达到上限");
piePartBeanArrayList.get(i).moveArc =(int) (piePartBeanArrayList.get(i).percent*360);
piePartBeanArrayList.get(i).drawLine = true;
}
piePartBeanArrayList.get(i).endPointX = (int) (centerPoint.x + mRadius * Math.sin(Math.PI * piePartBeanArrayList.get(i).percent*360/ 180));
piePartBeanArrayList.get(i).endPointY = (int) (centerPoint.y - mRadius * Math.cos(Math.PI * piePartBeanArrayList.get(i).percent*360/ 180));
}else{
//第2,3,4....part
pastTotalArc = 0;//之前arc所划过的角度
for (int j= 0;j<i;j++){ //1+2...个的扇形总角度
pastTotalArc+=piePartBeanArrayList.get(j).moveArc;
}
//startArc = -90°+之前arc所划过的角度总和
piePartBeanArrayList.get(i).startArc = -90+pastTotalArc;
//确定直线的位置要放在这里 因为这里可以得到每个部分的角度总和
piePartBeanArrayList.get(i).endPointX = (int) (centerPoint.x + mRadius * Math.sin(Math.PI * pastTotalArc/ 180));
piePartBeanArrayList.get(i).endPointY = (int) (centerPoint.y - mRadius * Math.cos(Math.PI * pastTotalArc/ 180));
//求i的moveArc
if(mCurrentPercent<piePartBeanArrayList.get(i).percent){
//还没有达到上限百分比
piePartBeanArrayList.get(i).moveArc =(int) (mCurrentPercent*360);
}else{
Log.d(TAG,"第"+i+"个达到上限");
piePartBeanArrayList.get(i).moveArc =(int) (piePartBeanArrayList.get(i).percent*360);
//当最后一个扇形区域+起始角度+(--90°) = 360的时候 就说明已经绘制完整 Running = false;跳出递归
int i1 = piePartBeanArrayList.get(i).moveArc + piePartBeanArrayList.get(i).startArc;
piePartBeanArrayList.get(i).drawLine = true;
if(i1>=360+(-90)){
//此处需要invalidate 不然会出现微小空缺。
invalidate();
Running = false;
}
}
}
part2的实现:
刚开始是这样想的:区分的直线和扇形一起移动,但是代码实现很麻烦,不知道哪个地方出了问题,奇奇怪怪的样子,后来通过另外一种方式实现了这个功能:就假设直线的位置固定不动,为扇形走完360°区域后的位置,当每个part走到最大值的时候,绘制出来就可以了,这里需要给PiePartBean添加一个drawLine的boolean值,当每个扇形区域当达到最大值时候,把drawLine=ture;同时invalidate()即可实现:
//画线
if(piePartBeanArrayList.get(i).drawLine) {
paint.setStrokeWidth(10);
if (i == 0) {
paint.setColor(Color.WHITE);
canvas.drawLine(centerPoint.x, centerPoint.y, piePartBeanArrayList.get(i).endPointX, piePartBeanArrayList.get(i).endPointY, paint);
} else {
paint.setColor(Color.WHITE);
canvas.drawLine(centerPoint.x, centerPoint.y, piePartBeanArrayList.get(i).endPointX, piePartBeanArrayList.get(i).endPointY, paint);
}
if (i == piePartBeanArrayList.size() - 1) {
canvas.drawLine(centerPoint.x, centerPoint.y, centerPoint.x, centerPoint.y - mRadius, paint);
}
}
part3的实现:
最后通过画圆的API就可以实现。不过,为了防止一进入界面调用ondraw方法就去画圆,还行添加数据非空判断。
if(piePartBeanArrayList.size()!=0) {///防止一进界面未设置数据 就已经画圆
//画圆心
paint.setColor(Color.WHITE);
canvas.drawCircle(centerPoint.x, centerPoint.y, mRadius / 2.5f, paint);
}
gitHub地址:PieChartView




