Android:使用canvas绘制饼状统计图(自动适应条目数量/大小)

本例的目的是实现一个简单的饼状统计图,效果如下:

        

特点:

1.使用非常方便,可放在xml布局文件中,然后在代码中设置内容,即:

PieChartView pieChartView = (PieChartView) findViewById(R.id.pie_chart);
        PieChartView.PieItemBean[] items = new PieChartView.PieItemBean[]{
                new PieChartView.PieItemBean("娱乐", 200),
                new PieChartView.PieItemBean("旅行", 100),
                new PieChartView.PieItemBean("学习", 120),
                new PieChartView.PieItemBean("人际关系", 160),
                new PieChartView.PieItemBean("交通", 100),
                new PieChartView.PieItemBean("餐饮", 480)
        };
        pieChartView.setPieItems(items);

2.条目数量,大小及折线位置,长度均自适应。左侧条目往左侧划线,右侧条目往右侧划线,文字描述与百分比居中对齐,并且文字“下划线”与文字长度自适应。对于很小的条目,将自动将折线延长以尽可能避免文字遮盖

核心代码:PieChartView.java:

public class PieChartView extends View {
    private int screenW, screenH;
    /**
     * The paint to draw text, pie and line.
     */
    private Paint textPaint, piePaint, linePaint;

    /**
     * The center and the radius of the pie.
     */
    private int pieCenterX, pieCenterY, pieRadius;
    /**
     * The oval to draw the oval in.
     */
    private RectF pieOval;

    private float smallMargin;

    private int[] mPieColors = new int[]{Color.RED, Color.GREEN, Color.BLUE, Color.YELLOW, Color.MAGENTA, Color.CYAN};

    private PieItemBean[] mPieItems;
    private float totalValue;

    public PieChartView(Context context) {
        super(context);

        init(context);
    }

    public PieChartView(Context context, AttributeSet attrs) {
        super(context, attrs);

        init(context);
    }

    public PieChartView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        init(context);
    }

    private void init(Context context) {
        //init screen
        screenW = ScreenUtils.getScreenW(context);
        screenH = ScreenUtils.getScreenH(context);

        pieCenterX = screenW / 2;
        pieCenterY = screenH / 3;
        pieRadius = screenW / 4;
        smallMargin = ScreenUtils.dp2px(context, 5);

        pieOval = new RectF();
        pieOval.left = pieCenterX - pieRadius;
        pieOval.top = pieCenterY - pieRadius;
        pieOval.right = pieCenterX + pieRadius;
        pieOval.bottom = pieCenterY + pieRadius;

        //The paint to draw text.
        textPaint = new Paint();
        textPaint.setAntiAlias(true);
        textPaint.setTextSize(ScreenUtils.dp2px(context, 16));

        //The paint to draw circle.
        piePaint = new Paint();
        piePaint.setAntiAlias(true);
        piePaint.setStyle(Paint.Style.FILL);

        //The paint to draw line to show the concrete text
        linePaint = new Paint();
        linePaint.setAntiAlias(true);
        linePaint.setStrokeWidth(ScreenUtils.dp2px(context, 1));

    }

    //The degree position of the last item arc's center.
    private float lastDegree = 0;
    //The count of the continues 'small' item.
    private int addTimes = 0;

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        if (mPieItems != null && mPieItems.length > 0) {
            float start = 0.0f;
            for (int i = 0; i < mPieItems.length; i++) {
                //draw pie
                piePaint.setColor(mPieColors[i % mPieColors.length]);
                float sweep = mPieItems[i].getItemValue() / totalValue * 360;
                canvas.drawArc(pieOval, start, sweep, true, piePaint);

                //draw line away from the pie
                float radians = (float) ((start + sweep / 2) / 180 * Math.PI);
                float lineStartX = pieCenterX + pieRadius * 0.7f * (float) (Math.cos(radians));
                float lineStartY = pieCenterY + pieRadius * 0.7f * (float) (Math.sin(radians));

                float lineStopX, lineStopY;
                float rate;
                if (getOffset(start + sweep / 2) > 60) {
                    rate = 1.3f;
                } else if (getOffset(start + sweep / 2) > 30) {
                    rate = 1.2f;
                } else {
                    rate = 1.1f;
                }
                //If the item is very small, make the text further away from the pie to avoid being hided by other text.
                if (start + sweep / 2 - lastDegree < 30) {
                    addTimes++;
                    rate += 0.2f * addTimes;
                } else {
                    addTimes = 0;
                }
                lineStopX = pieCenterX + pieRadius * rate * (float) (Math.cos(radians));
                lineStopY = pieCenterY + pieRadius * rate * (float) (Math.sin(radians));
                canvas.drawLine(lineStartX, lineStartY, lineStopX, lineStopY, linePaint);

                //write text
                String itemTypeText = mPieItems[i].getItemType();
                String itemPercentText = Utility.formatFloat(mPieItems[i].getItemValue() / totalValue * 100) + "%";

                float itemTypeTextLen = textPaint.measureText(itemTypeText);
                float itemPercentTextLen = textPaint.measureText(itemPercentText);
                float lineTextWidth = Math.max(itemTypeTextLen, itemPercentTextLen);

                float textStartX = lineStopX;
                float textStartY = lineStopY - smallMargin;
                float percentStartX = lineStopX;
                float percentStartY = lineStopY + textPaint.getTextSize();
                if (lineStartX > pieCenterX) {
                    textStartX += (smallMargin + Math.abs(itemTypeTextLen - lineTextWidth) / 2);
                    percentStartX += (smallMargin + Math.abs(itemPercentTextLen - lineTextWidth) / 2);
                } else {
                    textStartX -= (smallMargin + lineTextWidth - Math.abs(itemTypeTextLen - lineTextWidth) / 2);
                    percentStartX -= (smallMargin + lineTextWidth - Math.abs(itemPercentTextLen - lineTextWidth) / 2);
                }
                canvas.drawText(itemTypeText, textStartX, textStartY, textPaint);
                //draw percent text
                canvas.drawText(itemPercentText, percentStartX, percentStartY, textPaint);

                //draw text underline
                float textLineStopX = lineStopX;
                if (lineStartX > pieCenterX) {
                    textLineStopX += (lineTextWidth + smallMargin * 2);
                } else {
                    textLineStopX -= (lineTextWidth + smallMargin * 2);
                }
                canvas.drawLine(lineStopX, lineStopY, textLineStopX, lineStopY, linePaint);

                lastDegree = start + sweep / 2;
                start += sweep;
            }
        }
    }

    public PieItemBean[] getPieItems() {
        return mPieItems;
    }

    public void setPieItems(PieItemBean[] pieItems) {
        this.mPieItems = pieItems;

        totalValue = 0;
        for (PieItemBean item : mPieItems) {
            totalValue += item.getItemValue();
        }

        invalidate();
    }

    private float getOffset(float radius) {
        int a = (int) (radius % 360 / 90);
        switch (a) {
            case 0:
                return radius;
            case 1:
                return 180 - radius;
            case 2:
                return radius - 180;
            case 3:
                return 360 - radius;
        }

        return radius;
    }

    static class PieItemBean {
        private String itemType;
        private float itemValue;

        PieItemBean(String itemType, float itemValue) {
            this.itemType = itemType;
            this.itemValue = itemValue;
        }

        public String getItemType() {
            return itemType;
        }

        public void setItemType(String itemType) {
            this.itemType = itemType;
        }

        public float getItemValue() {
            return itemValue;
        }

        public void setItemValue(float itemValue) {
            this.itemValue = itemValue;
        }
    }
}
时间: 2024-08-30 05:32:52

Android:使用canvas绘制饼状统计图(自动适应条目数量/大小)的相关文章

第166天:canvas绘制饼状图动画

canvas绘制饼状图动画 1.HTML 1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no"> 6

canvas绘制饼状图

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>06绘制饼状图</title> </head> <body> <canvas id="canvas"> 抱歉,您的浏览器不支持Canvas.请升级您的浏览器! </canvas> <s

Android:使用canvas绘柱状统计图(自动计算宽高及分度值、可左右滑动)

本例实现了一个简单的柱状统计图,如下:        特点: 1.根据数据源自动计算每个条目的高度.宽度.间距,自动计算分度值. 2.当条目数较多时,可左右滑动查看全部内容,图形.文字同步滑动,并且松手后会渐渐的停下来(而不是立刻停下来). 代码: (1)核心代码:BarChartView.java package com.sina.appbarchart; import android.app.Activity; import android.content.Context; import a

canvas动态绘制饼状图,

当我们使用Echrts很Highcharts的时候,总是觉得各种统计图表是多么神奇,今天我就用现代浏览器支持的canvas来绘制饼状统计图,当然仅仅是画出图并没什么难度,但是统计图一般都有输入,根据不同的输入来绘制,需要发挥你脑力. 1.canvas简单使用 1.1先看我们的html,需要一个绘图的区域 <canvas id="drawing" width="500px" height="500px"></canvas>

绘制饼状图

1 import matplotlib.pyplot as plt 2 import numpy as np 3 #准备女生的人数及比例 4 man = 71351 5 women = 68187 6 man_perc = man/(women+man) 7 women_perc = women/(women+man) 8 #添加名称 9 labels = ['男','女'] 10 #添加颜色 11 colors = ['blue','red'] 12 #绘制饼状图 pie 13 paches,

skyline应用-BS绘制饼状图

这个有现成的代码,把代码做一个简要的说明,具体实现效果,是在TE的地图画一个饼状图. <script type="text/javascript"> function createpie() { //设置简单参数,包括半径等参数 var sum = 0; var radius = 100; var angle = 0; var i; //根据饼状图的分块,设置不同的颜色 var color = [0x0000a0, 0x00ff60, 0x8000a0, 0xa0ff00,

[Echarts]用Echarts绘制饼状图

在项目网站的网页中,有这样一幅图: 心血来潮,想使用百度Echarts来绘制一下,可是没能绘制得完全一样,Echarts饼状图的label不能在图形下面放成一行,最后的效果是这样子的: 鼠标移动到items上,可动态显示百分比: 另外,还了解到了一种特殊的饼状图:南丁格尔图,就是用扇形半径的大小来表示百分比,对于相差比较大的items,看起来会有些不平衡: 最后,上代码: 1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta ch

绘制饼状图效果

<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> <style> #canvas{ background: #988E75; margin: 0 auto; display: block; position: relative; } </style> </head> <body>

svg动态绘制饼状图

1.<path>绘制圆弧曲线 语法:d="A rx ry x-axis-rotation large-arc-flag sweep-flag x y" A  命令绘制椭圆弧. 参数含义: rx,ry:椭圆的长半轴.短半轴: x-axis-rotation:该段弧 所在椭圆的x轴与水平方向的夹角: large-arc-flag:值可为0或1,0为小弧度角,1为大弧度角: sweep-flag:绘制方向,0为逆时针,1为顺时针: x,y椭圆弧终点坐标. 2.js动态svg va