Android扇形图(饼状图)

关于Android的图形控件,市场上是有很多的开源库的
我个人用的比较多的就是:MPAndroidChart,个人感觉挺好用的,但是有写时候,满足不了需求,就需要自己去写了。
先看看我们的效果图:

TIM图片20171010170541.jpg

先要想好画一个扇形的步骤
1:画圆
2:画扇形
3:画白色边框
4:画指示线和文字
第一步画圆就不讲解了,直接说第二步
画扇形:

/**
    * 画扇形
    * @param canvas
    */
   private void initPie(Canvas canvas) {
       int anglesSize = list.size();
       if (list != null && anglesSize > 0){
           float pieStart = START_DEGREE;
           if (mSweep == null) {
               mSweep = new Float[list.size()];
           }
           for (int i = 0;i < anglesSize; i++){
               piePaint.setColor(getResources().getColor(list.get(i).getColor()));
               canvas.drawArc(rectF, pieStart, mSweep[i], true, piePaint);             //扇形
               canvas.drawArc(rectF, pieStart, mSweep[i], true, outerLinePaint);           //边框线
               pieStart +=  mSweep[i];
           }
       }
   }

看到里面是根据扇形的角度去绘制出来扇形的大小,然后根据扇形的角度绘制每个paint的颜色,边框也是根据角度来定的。
然后就开始画指示线和文字:

private void initLineAndText(Canvas canvas, float statrAngles, float angles, int color, String text) {
       float stopX,stopY;
       float ceterX = getMeasuredWidth() / 2;
       float ceterY = getMeasuredHeight() / 2;
       linePaint.setColor(getResources().getColor(color));
       textPaint.setColor(getResources().getColor(color));
       //  半径加上多出的20个像素的位置,去根据角度算出 转点的X,Y轴
       float cosX = (float) Math.cos(( 2 * statrAngles + angles) /  2 * Math.PI /180 );
       float sinY = (float) Math.sin((2 * statrAngles + angles) / 2 * Math.PI /180 );
       stopX =  (radius + dip2px(10)) * cosX;
       stopY =  (radius + dip2px(10)) * sinY;
       //扇形弧边的中点的X,Y 为起点,然后算出中间点的角度,加上半径+10个像素 得出终点的XY轴,
       canvas.drawLine(ceterX + (radius - dip2px(10)) * cosX, ceterY + (radius - dip2px(10))* sinY,stopX+ceterX,stopY+ceterY,linePaint);
       Rect rect = new Rect();
       textPaint.getTextBounds(text, 0, text.length(), rect);
       int h = rect.height();
       int w = rect.width();
       //画第二根线,第二个根线的起点是第一根线的终点,然后终点,根据X轴来定直接加25个像素
       //如果是右边,减去25个像素
       //文字的位置,Y轴根据第二根线的Y轴+文字的高度的一半,这样就能居中  X轴,左边加30个像素,根第二个线的有5个像素的距离
       //文字如果在右边,减去文字的宽度 - 30个像素
       if (stopX > 0) {
           //50为横线的长度 60 为文字的偏移量
           canvas.drawLine(ceterX +stopX, ceterY +stopY, ceterX +stopX + dip2px(25), ceterY +stopY, linePaint);
           canvas.drawText(text, 0, text.length(), ceterX +stopX +dip2px(30)  ,ceterY +stopY + h/2, textPaint);

       } else {
           canvas.drawLine(ceterX +stopX, ceterY +stopY, ceterX +stopX - dip2px(25), ceterY +stopY, linePaint);
           canvas.drawText(text, 0, text.length(), ceterX +stopX - w - dip2px(30)  ,ceterY+stopY + h/2 , textPaint);

       }




   }

这个在哪里调用的,当然不是在onDraw方法里面,是在画扇形的里面,因为里面的角度是根据扇形的角度来定的。具体代码就不详细讲解了,注释已经说的很明白了,在initPie的for循环里面去调用这个方法。
这样,我们的扇形就已经出来了。
为了效果,加上了动画


 private float PIE_ANIMATION_VALUE = 100;
    private class PieChartAnimation extends Animation {
        @Override
        protected void applyTransformation(float interpolatedTime, Transformation t) {
            super.applyTransformation(interpolatedTime, t);
            mSweep = new Float[list.size()];
            if (interpolatedTime < 1.0f) {
                for (int i = 0; i < list.size(); i++) {
                    //根据传进来的比例,算出在圆中所占的角度
                    mSweep[i] = list.get(i).getAngle()  * interpolatedTime / PIE_ANIMATION_VALUE * 360;
                }
            } else {
                for (int i = 0; i < list.size(); i++) {
                    mSweep[i] = list.get(i).getAngle()  /PIE_ANIMATION_VALUE * 360;
                }
            }
            invalidate();
        }
    }

其实这个扇形是根据另外一个大佬写的,我在上面改了
大佬的传送门:http://www.jianshu.com/p/f50dbae3a07f
好了,附上源码:

public class PieChart extends View {

    private Paint paint;            //圆画笔

    private Paint piePaint;        //扇形画笔
    private Paint outerLinePaint;       //轮廓画笔
    private Paint linePaint;            //指示线
    private Paint textPaint;                //文字画笔

    private float radius;           //半径

    private static final int PAINT_COLOR = 0xed3535;
    private static final float OUTER_LINE_WIDTH = 3f;
    private static final float START_DEGREE = -90f;         //开始绘制角度

    //饼状图动画效果
    private float X;
    private float Y;
    private RectF rectF;
    private Float mSweep[];
    //饼状图动画效果
    private PieChartAnimation mAnimation;
    private Context context;
    private List<PieChartBean> list;
    public PieChart(Context context) {
        this(context,null);
    }

    public PieChart(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs,0);
    }

    public PieChart(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.context = context;
        init();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        radius = Math.min(getMeasuredWidth(),getMeasuredHeight()) / 3;

        X = getMeasuredWidth() / 2;
        Y = getMeasuredHeight() / 2;
        rectF.left = X  - radius;
        rectF.top = Y - radius;
        rectF.right = X + radius;
        rectF.bottom = Y +  radius;
    }

    private void init() {

        paint = new Paint();
        paint.setColor(PAINT_COLOR);
        paint.setAntiAlias(true);
        paint.setAlpha(110);
        paint.setStyle(Paint.Style.FILL);
        piePaint = new Paint();
        piePaint.setStyle(Paint.Style.FILL);
        piePaint.setAntiAlias(true);
        piePaint.setAlpha(255);
        outerLinePaint = new Paint();
        outerLinePaint.setAntiAlias(true);
        outerLinePaint.setAlpha(255);
        outerLinePaint.setStyle(Paint.Style.STROKE);
        outerLinePaint.setStrokeWidth(OUTER_LINE_WIDTH);
        outerLinePaint.setColor(Color.WHITE);
        rectF = new RectF();
        linePaint = new Paint();
        linePaint.setAntiAlias(true);
        linePaint.setStrokeWidth(4);
        textPaint = new Paint();
        textPaint.setAntiAlias(true);
        textPaint.setStrokeWidth(30);
        textPaint.setTextSize(25);
        mAnimation = new PieChartAnimation();
        mAnimation.setDuration(2000);
    }

    public void setDate(List<PieChartBean> list){
        this.list = list;
        if (mSweep == null){
            mSweep = new Float[list.size()];
        }
        if (mAnimation != null)
         setAnimation(mAnimation);
    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        initPie(canvas);
    }
    /**
     * 画扇形
     * @param canvas
     */
    private void initPie(Canvas canvas) {
        int anglesSize = list.size();
        if (list != null && anglesSize > 0){
            float pieStart = START_DEGREE;
            if (mSweep == null) {
                mSweep = new Float[list.size()];
            }
            for (int i = 0;i < anglesSize; i++){
                piePaint.setColor(getResources().getColor(list.get(i).getColor()));
                canvas.drawArc(rectF, pieStart, mSweep[i], true, piePaint);             //扇形
                canvas.drawArc(rectF, pieStart, mSweep[i], true, outerLinePaint);           //边框线
                initLineAndText(canvas, pieStart, mSweep[i], list.get(i).getColor(), list.get(i).getValuer());
                pieStart +=  mSweep[i];
            }
        }
    }
    /**
     * 画指示线 和文字
     * @param canvas
     * @param text
     */
    private void initLineAndText(Canvas canvas, float statrAngles, float angles, int color, String text) {
        float stopX,stopY;
        float ceterX = getMeasuredWidth() / 2;
        float ceterY = getMeasuredHeight() / 2;
        linePaint.setColor(getResources().getColor(color));
        textPaint.setColor(getResources().getColor(color));
        //  半径加上多出的20个像素的位置,去根据角度算出 转点的X,Y轴
        float cosX = (float) Math.cos(( 2 * statrAngles + angles) /  2 * Math.PI /180 );
        float sinY = (float) Math.sin((2 * statrAngles + angles) / 2 * Math.PI /180 );
        stopX =  (radius + dip2px(10)) * cosX;
        stopY =  (radius + dip2px(10)) * sinY;
        //扇形弧边的中点的X,Y 为起点,然后算出中间点的角度,加上半径+10个像素 得出终点的XY轴,
        canvas.drawLine(ceterX + (radius - dip2px(10)) * cosX, ceterY + (radius - dip2px(10))* sinY,stopX+ceterX,stopY+ceterY,linePaint);
        Rect rect = new Rect();
        textPaint.getTextBounds(text, 0, text.length(), rect);
        int h = rect.height();
        int w = rect.width();
        //画第二根线,第二个根线的起点是第一根线的终点,然后终点,根据X轴来定直接加25个像素
        //如果是右边,减去25个像素
        //文字的位置,Y轴根据第二根线的Y轴+文字的高度的一半,这样就能居中  X轴,左边加30个像素,根第二个线的有5个像素的距离
        //文字如果在右边,减去文字的宽度 - 30个像素
        if (stopX > 0) {
            //50为横线的长度 60 为文字的偏移量
            canvas.drawLine(ceterX +stopX, ceterY +stopY, ceterX +stopX + dip2px(25), ceterY +stopY, linePaint);
            canvas.drawText(text, 0, text.length(), ceterX +stopX +dip2px(30)  ,ceterY +stopY + h/2, textPaint);

        } else {
            canvas.drawLine(ceterX +stopX, ceterY +stopY, ceterX +stopX - dip2px(25), ceterY +stopY, linePaint);
            canvas.drawText(text, 0, text.length(), ceterX +stopX - w - dip2px(30)  ,ceterY+stopY + h/2 , textPaint);

        }




    }
    /**
     * dip转为 px
     */
    private  int dip2px(float dipValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dipValue * scale + 0.5f);
    }



    private float PIE_ANIMATION_VALUE = 100;
    private class PieChartAnimation extends Animation {
        @Override
        protected void applyTransformation(float interpolatedTime, Transformation t) {
            super.applyTransformation(interpolatedTime, t);
            mSweep = new Float[list.size()];
            if (interpolatedTime < 1.0f) {
                for (int i = 0; i < list.size(); i++) {
                    //根据传进来的比例,算出在圆中所占的角度
                    mSweep[i] = list.get(i).getAngle()  * interpolatedTime / PIE_ANIMATION_VALUE * 360;
                }
            } else {
                for (int i = 0; i < list.size(); i++) {
                    mSweep[i] = list.get(i).getAngle()  /PIE_ANIMATION_VALUE * 360;
                }
            }
            invalidate();
        }
    }




}

PieChartBean实体类参数:

   private String valuer;      //说明
    private Float angle;    //占的大小
    private Integer color;      //颜色值

好了,自己动手试试吧。所有的源码都在这里。用的时候,直接穿一个list进来就可以了...
你离成功只差一个动手的距离

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,793评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,567评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,342评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,825评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,814评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,680评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,033评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,687评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,175评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,668评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,775评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,419评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,020评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,206评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,092评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,510评论 2 343

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,454评论 25 707
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,019评论 4 62
  • 当所罗门站在演讲台开始讲述因钻石开采政府军与反政府军相互残杀以及无数非洲小孩被反政府军锻造成可怕的战争傀儡这一真实...
    惇华阅读 254评论 0 1
  • 走你走过的路,哼你听过的歌,我在想,明年今日,我能否拥有和你一样的心情,踏着你的曾赴往的征程,去追求自己的梦。 ...
    南音lzj阅读 432评论 0 0
  • 对于“飞机上不能使用手机”这项禁令的合理性 不少人深表怀疑其实不仅是公众这也是民航领域最有争议的问题之一 在航空业...
    SmartNews阅读 469评论 0 1