遇到复杂的问题,先把复杂的问题分解成一个一个小问题,然后一个一个的解决,复杂问题也就显得不再复杂。
先看看最后的=实现的效果图:
对于这个功能的实现,可以分为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