要完成一个饼状图,其实就是将一个360度分成很多份,然后每一份绘制一个扇形,这些扇形加起来正好是一个整圆。
效果:
手撸一个Android饼状图表_第1张图片手撸一个Android饼状图表_第2张图片

android中绘制扇形 我们可以用绘制弧形的api

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

也可以用绘制path的api

canvas.drawPath(mPath,mPaint); 

path确定路径的时候需要用到 mPath.arcTo(mRectF,startAngle,sweepAngle); 来却定path的路径。

无论是绘制圆弧还是绘制path 我们都需要有一个绘制的区域 这就需要我们定义一个RectF来确定绘制的区域在onSizeChanged()方法中初始化

@Override    protected void onSizeChanged(int w, int h, int oldw, int oldh) {        super.onSizeChanged(w, h, oldw, oldh);        mTotalWidth = w - getPaddingLeft() - getPaddingRight();        mTotalHeight = h - getPaddingTop() - getPaddingBottom();        mRadius = (float) (Math.min(mTotalWidth,mTotalHeight)/2*0.7);        mRectF.left = -mRadius;        mRectF.top = -mRadius;        mRectF.right = mRadius;        mRectF.bottom = mRadius;        mRectFTouch.left = -mRadius-16;        mRectFTouch.top = -mRadius-16;        mRectFTouch.right = mRadius+16;        mRectFTouch.bottom = mRadius+16;    }

mRectFTouch 是当我们手指点击到某一个扇形的时候,我们需要这个扇形突出一点,这时候我们需要将这个矩形的长宽都扩大一点。

然后就是在onDraw()中绘制了

 @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        if(mDataList==null)            return;        canvas.translate(mTotalWidth/2,mTotalHeight/2);        //绘制饼图的每块区域        drawPiePath(canvas);    }

这里先把画布移到屏幕的中间,这样坐标的原点就在屏幕中间了,绘制起来方便一些。

绘制扇形的方法:

 /**     * 绘制饼图的每块区域 和文本     * @param canvas     */    private void drawPiePath(Canvas canvas) {        //起始地角度        float startAngle = 0;        for(int i = 0;ifloat sweepAngle = mDataList.get(i).getValue()/mTotalValue*360-1;//每个扇形的角度            mPath.moveTo(0,0);            if(position-1==i){                mPath.arcTo(mRectFTouch,startAngle,sweepAngle);            }else {                mPath.arcTo(mRectF,startAngle,sweepAngle);            }            mPaint.setColor(mDataList.get(i).getColor());            canvas.drawPath(mPath,mPaint);            mPath.reset();            startAngle += sweepAngle+1;        }    }

也可以使用drawArc的方法。

for(int i = 0;ifloat sweepAngle = mDataList.get(i).getValue()/mTotalValue*360-1;//每个扇形的角度            if(position-1==i){               canvas.drawArc(mRectFTouch,startAngle,sweepAngle,true,mPaint);            }else {                canvas.drawArc(mRectF,startAngle,sweepAngle,true,mPaint);            }            mPaint.setColor(mDataList.get(i).getColor());            canvas.drawArc(mRectF,startAngle,sweepAngle,true,mPaint);            }

效果:
手撸一个Android饼状图表_第3张图片
正常情况下我们根据传入的数据计算出其在360度中所占的度数后绘制出来的应该是每个扇形无缝连接的,有时候我们感觉上图中的每个扇形间有个小间距会更好看一点,要做到这点很简单,绘制的时候每个扇形的度数减去一度就好了。

然后就是将每个扇形所占的百分比数字绘制上去。我们可以将其绘制在每个扇形的中间,但是因为现实中可能有的扇形很大有的扇形很小,当一个扇形很小的时候在其上面写字有可能就覆盖到别的扇形了,假如连着好几个都很小呢,往上面写字更会覆盖到一起。体验无疑会很差。

所以这里从每个扇形中指出一条小直线,在外面绘制我们的百分比数字。

要绘制直线就得确定每个直线的起始点和终止点的坐标。

            float pxs = (float) (mRadius*Math.cos(Math.toRadians(startAngle+sweepAngle/2)));            float pys = (float) (mRadius*Math.sin(Math.toRadians(startAngle+sweepAngle/2)));            float pxt = (float) ((mRadius+30)*Math.cos(Math.toRadians(startAngle+sweepAngle/2)));            float pyt = (float) ((mRadius+30)*Math.sin(Math.toRadians(startAngle+sweepAngle/2)));            canvas.drawLine(pxs,pys,pxt,pyt,mLinePaint);

Math.toRadians() 是将角度转换为弧度
1弧度=180/π度
1度=π/180弧度
弧度和角度的转换可以参考:弧度和角度的转换
然后是绘制文本:

 //提供精确的小数位四舍五入处理。            double resToRound = CalculateUtil.round(res,2);            float v = startAngle % 360;            if (startAngle % 360.0 >= 90.0 && startAngle % 360.0 <= 270.0) {                canvas.drawText(resToRound+"%",pxt-mTextPaint.measureText(resToRound+"%"),pyt,mTextPaint);            }else {                canvas.drawText(resToRound+"%",pxt,pyt,mTextPaint);            }

这里我们绘制文字的时候需要注意下坐标轴的象限,再一和四象限,我们直接在直线的终点处开始绘制就好了,但是在二和三象限中如果这么绘制的话,文字就绘制到图像上面来了。所以我们需要将绘制的起点往左移动text的大小的距离在绘制。
然后就算绘制完成了:
手撸一个Android饼状图表_第4张图片

有时候我们需要点击某个部分来查看此部分的详情。所以需要对其设置点击事件,点击事件无非就是写个接口给外面调用就好了,关键是我们怎么确定我们所点击的地方是哪个扇形。

 @Override    public boolean onTouchEvent(MotionEvent event) {        switch (event.getAction()){            case MotionEvent.ACTION_DOWN:                float x = event.getX()-(mTotalWidth/2);                float y = event.getY()-(mTotalHeight/2);                float touchAngle = 0;                if (x<0&&y<0){  //2 象限                    touchAngle += 180;                }else if (y<0&&x>0){  //1象限                    touchAngle += 360;                }else if (y>0&&x<0){  //3象限                    touchAngle += 180;                }                //Math.atan(y/x) 返回正数值表示相对于 x 轴的逆时针转角,返回负数值则表示顺时针转角。                //返回值乘以 180/π,将弧度转换为角度。                touchAngle +=Math.toDegrees(Math.atan(y/x));                if (touchAngle<0){                    touchAngle = touchAngle+360;                }                float touchRadius = (float) Math.sqrt(y*y+x*x);                if (touchRadius< mRadius){                    position = -Arrays.binarySearch(angles,(touchAngle))-1;                    invalidate();                    if(mOnItemPieClickListener!=null){                        mOnItemPieClickListener.onClick(position-1);                    }                }                break;        }        return super.onTouchEvent(event);    }

在绘制扇形的地方将每个扇形的起始角度保存到一个数组中,
Math.toDegrees(Math.atan(y/x));将我们点击的地方转换成角度,
通过-Arrays.binarySearch(angles,(touchAngle))-1;方法计算出position,
Arrays.binarySearch的用法可以百度下。
计算出pisition后再接口中返回点击事件就OK了。

源码地址:https://github.com/chsmy/EasyChartWidget

更多相关文章

  1. Android 属性动画实现的扇形菜单效果
  2. android扇形菜单
  3. Android 带有弹出收缩动画的扇形菜单实例
  4. Android属性动画应用超简单代码打造酷炫扇形(卫星),圆形菜单
  5. android(6) 扇形菜单实现
  6. Android自定义Button背景色,弧度
  7. android给View设置边框 填充颜色 弧度
  8. android(6) 扇形菜单实现

随机推荐

  1. Android有用代码片段(零)
  2. 谷歌手机地图android 设置 出现xxxx进程
  3. ANDROID BASE64编码解码
  4. Reader读取TcpDump读取的Pcap包
  5. Android(安卓)Studio 第一次安装可能遇到
  6. ubuntu 下android真机测试配置
  7. React native 报错:index.android.js` was
  8. android SMS以及其他消息推送机制的相关
  9. Android(安卓)Studio开发应用
  10. Android调用startForeground 但不显示通