Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

liaopen123/PieChartView

Repository files navigation

PieChartView

饼状图View image

缘由

3.1.0版本的开发当中涉及到了饼状体,小组有人认为可以用MPAndroidChart 实现这个功能,但是我的内心自始至终是拒绝的,因为MPAndroidChart的确很强大,但是项目中并不需要太多功能,不能因为只需要十分之一的功能就导入一个包,于是在星期四就开始构思这个事情。对于动态处理扇形起始角度这个东西有点迷糊,重写了3次扇形部分的代码,改了几个bug才将这个功能做好。

遇到复杂的问题,先把复杂的问题分解成一个一个小问题,然后一个一个的解决,复杂问题也就显得不再复杂。

先看看最后的=实现的效果图:

功能实现:

对于这个功能的实现,可以分为3个部分 就可以搞定。

part1:

20161204111555

part2&part3:20161204112014

3个部分中 最难分析的是part1:因为android提供的api中画扇形代码如下,

public void drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint)

四个参数分别为:

  • oval: 圆弧所在的椭圆。
  • startAngle: 圆弧起始角度,单位为度。
  • sweepAngle: 圆弧扫过的角度,顺时针方向,单位为度。
  • useCenter: 如果为True时,在绘制圆弧时将圆心包括在内,通常用来绘制扇形。
  • paint: 绘制圆弧的画板属性,如颜色,是否填充等。
其中难点在于startAngle,因为它是一直再变化,并且依赖于相邻圆弧的结束边,就是在这个地方翻了一点点迷糊,该用手写的还是得做做草稿啊,最后一个版本的代码还是在草稿纸完成的...

注意:通过面向对象写圆弧的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个大情况,1:是否是第一个扇形,因为第一个扇形的固定从-90°(也就是12点方向开始的),而其他圆弧都是依赖于第一个扇形和之前的扇形。2:在扇形百分比递增的时候,考虑有没有超过最大百分比。先确定基准扇形的位置和大小,这里也就是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);
}

最后附上最后一版的草稿(我已经看不懂我在写什么。。)826453987486950133

gitHub地址:PieChartView

About

饼状图View

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

AltStyle によって変換されたページ (->オリジナル) /