美文网首页UI
Android扇形图(饼状图)

Android扇形图(饼状图)

作者: MinRookie | 来源:发表于2017-10-10 17:33 被阅读152次

关于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进来就可以了...
你离成功只差一个动手的距离

相关文章

  • Android扇形图(饼状图)

    关于Android的图形控件,市场上是有很多的开源库的我个人用的比较多的就是:MPAndroidChart,个人感...

  • 扇形图(饼状图)的实现

    废话不多说,先直接上最终的效果图(完整代码在底部) 我把这个view拆分理解,主要的思路如下:1.通过 canva...

  • iOS - 环形图、饼状图、扇形图

    可定制性较强,使用简单的 环形图、饼状图、扇形图 效果预览 写在前面 很久没有更新文章了,最近一年 996,前段时...

  • Android14个常用的图表库

    推荐Android14个常用的图表库,包含线性,条形柱状,饼状图,扇形,雷达,股票,折线,散点,仪表盘等。 详细了...

  • 【R语言】--- 饼状图

    概述 饼状图,又称为饼图,能够划分几个扇形的圆形统计图。可以描述量、频率、占比的相对关系。R语言中,可以用grap...

  • R006 基本图形-饼图pie()与扇形图fan.plot()

    饼图 饼图可由函数pie()创建 例 扇形图 饼图让比较各扇形的值变得困难(除非这些值被附加在标签上),为改善这种...

  • R语言绘制饼图,扇图,直方图

    模型 数据 饼图 扇形图 直方图 数据 作图

  • iOS - ORCharts

    ORCharts 一个可定制性较强,使用起来非常简单的 轻量级图表库。包括 环形图、饼状图、扇形图、折线图、曲线图...

  • iOS CGContextRef

    一、绘制饼状图 饼状图的简单实现代码:

  • Echarts

    http://echarts.baidu.com/api.html#echarts 柱状图: 饼状图: 饼状图2:...

网友评论

  • winelx:再问一下,动态数据怎么刷新控件,重新绘制
    MinRookie:@winelx 把数据传进去,然后刷新一下就好了,不过实时动态的话,我建议你设置一下动画的次数,第一次进来的时候有动画就好了,刷新的时候不要动画,会一有数据,就会执行动画
  • winelx:博主,这个调用方法只用setDate吗?
    MinRookie:嗯嗯
  • 7cdd35e70eba:好文啊,老铁继续加油

本文标题:Android扇形图(饼状图)

本文链接:https://www.haomeiwen.com/subject/fzfbyxtx.html