最近项目里碰上了饼图,顺便整理了下几种Android饼图,以作参考

Posted 原来如此丶

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了最近项目里碰上了饼图,顺便整理了下几种Android饼图,以作参考相关的知识,希望对你有一定的参考价值。

         林林总总,大概用了三种方式来实现饼图        第一种:自定义一个piechart,亲自来画一个饼图,归根结底还是参照老外的思路,但是还是有自己的一点想法在里面,先上代码:
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
 
import java.util.List;
 
public class PieChartView extends View 
    private int arcColos[] = new int[]getResources().getColor(R.color.gold),Color.BLUE,
            getResources().getColor(R.color.coral),Color.RED,
            getResources().getColor(R.color.hotpink), Color.GREEN,
            getResources().getColor(R.color.mintcream),getResources().getColor(R.color.orange);
    private final static String TAG = "PieChartView";
 
    private List<PieMember> data = null;
 
    //圆心位置
    private int centerX, centerY = 0;
    //整个饼图的半径
    private float radius = 0;
    //中间原型半径
    private float hollowRadius = 0;
    //饼图边距
    private float margin = 0;
 
 
    //饼图名称
    private String pieName = "得分比例";
    //lable高度
    private int lableHeight = 80;
 
    private float fontSize = 0;
 
    private Paint bgPaint = null;
    private Paint arcPaint = null;
    private Paint textPaint = null;
 
    public PieChartView(Context context) 
        super(context);
        this.init();
    
 
    public PieChartView(Context context, AttributeSet attrs) 
        super(context, attrs);
        this.init();
    
 
    public PieChartView(Context context, AttributeSet attrs, int defStyle) 
        super(context, attrs, defStyle);
        this.init();
    
 
 
    public void setPieName(String pieName) 
        this.pieName = pieName;
        postInvalidate();
    
 
    private void init() 
        this.bgPaint = new Paint();
        bgPaint.setAntiAlias(true);
        this.bgPaint.setColor(getResources().getColor(R.color.theme_main_blue));
        this.bgPaint.setStyle(Paint.Style.FILL_AND_STROKE);
 
        this.arcPaint = new Paint();
        this.arcPaint.setAntiAlias(true);
        this.arcPaint.setStyle(Paint.Style.FILL_AND_STROKE);
 
        this.textPaint = new Paint();
        this.textPaint.setColor(getResources().getColor(R.color.black_overlay));
        this.textPaint.setAntiAlias(true);//去除锯齿
        this.textPaint.setFilterBitmap(true);//对位图进行滤波处理
    
 
    @Override
    protected void onDraw(Canvas canvas) 
        super.onDraw(canvas);
 
        //canvas.drawColor(getResources().getColor(R.color.pageBgColor));
        canvas.drawColor(getResources().getColor(android.R.color.transparent));//设置背景透明
 
 
        if (margin == 0) 
            margin = getWidth() * 0.05f;
        
        if (radius == 0) 
            if ((getHeight() - lableHeight) < getWidth()) 
                radius = (getHeight() - lableHeight - (margin * 2)) / 2;
             else 
                radius = (getWidth() - (margin * 2)) / 2;
            
         else if ((radius * 2) > (getWidth() - (margin * 2))) 
            radius = (getWidth() - (margin * 2)) / 2;
         else if ((radius * 2 + lableHeight) > (getHeight() - (margin * 2))) 
            ViewGroup.LayoutParams params = getLayoutParams();
            params.height = (int) (radius * 2d + lableHeight + (margin * 2d));
            setLayoutParams(params);
        
        if (hollowRadius == 0) 
            hollowRadius = (int) (radius * 0.5f);
        
        if (this.centerX == 0) 
            this.centerX = getWidth() / 2;
        
        if (this.centerY == 0) 
            this.centerY = (getHeight() - lableHeight) / 2;
        
        this.fontSize = scalaFonts((int) (hollowRadius / this.pieName.length() * 0.6f) * 2);
 
        //画最下一层的圆圈
        this.bgPaint.setColor(getResources().getColor(R.color.theme_main_blue));
        this.bgPaint.setStyle(Paint.Style.FILL_AND_STROKE);
        canvas.drawCircle(centerX, centerY, radius, bgPaint);
 
        //画百分比圆弧
        this.drawArc(canvas);
 
        this.bgPaint.setColor(getResources().getColor(R.color.pageBgColor));
        this.bgPaint.setStyle(Paint.Style.FILL_AND_STROKE);
        canvas.drawCircle(centerX, centerY, hollowRadius, bgPaint);
 
        this.bgPaint.setStyle(Paint.Style.STROKE);
        this.bgPaint.setColor(getResources().getColor(R.color.black_overlay));
        canvas.drawCircle(centerX, centerY, hollowRadius * 0.9f, bgPaint);
 
        drawText(canvas);
 
        canvas.restore();
    
 
    public void drawText(Canvas canvas) 
        int x = getWidth();
        int y = getHeight() - lableHeight;
 
        this.textPaint.setTextSize(this.fontSize);
        float tX = (x - getFontlength(this.textPaint, this.pieName)) / 2;
        float tY = (y - getFontHeight(this.textPaint)) / 2 + getFontLeading(this.textPaint);
        canvas.drawText(this.pieName, tX, tY, this.textPaint);
    
 
    public void drawArc(Canvas canvas) 
        RectF rect = new RectF();
        rect.left = centerX - radius;
        rect.top = centerY - radius;
        rect.right = centerX + radius;
        rect.bottom = centerY + radius;
 
 
        RectF rectText = new RectF();
        rectText.left = centerX - (radius - (radius * 0.4f));
        rectText.top = centerY - (radius - (radius * 0.4f));
        rectText.right = centerX + (radius - (radius * 0.4f));
        rectText.bottom = centerY + (radius - (radius * 0.4f));
 
        if (this.data == null || this.data.isEmpty()) 
            Log.w(TAG, "没有可以绘制的数据");
            arcPaint.setColor(arcColos[0]);
            canvas.drawArc(rect, //弧线所使用的矩形区域大小
                    0,  //开始角度
                    60, //扫过的角度
                    true, //是否使用中心
                    arcPaint);
            return;
        
        int total = 0;//总数
        for (PieMember member : data) 
            total += member.getNumber();
        
        float angle = 0f;
        int i = 0;
        for (int pointer = 0; pointer < data.size(); pointer++) 
            PieMember member = data.get(pointer);
            if (member.getColor() == 0) 
                if (i >= arcColos.length) 
                    i = 0;
                
                member.setColor(arcColos[i++]);
            
            float d = 360f * ((float) member.getNumber() / (float) total);
 
            arcPaint.setColor(member.getColor());
            canvas.drawArc(rect, angle, d, true, arcPaint);  //根据进度画圆弧
 
            Path path = new Path();
            path.addArc(rectText, angle, d);
            Paint citePaint = new Paint();
            citePaint.setTextSize(fontSize * 0.6F);
            citePaint.setStrokeWidth(1);
            citePaint.setColor(getResources().getColor(R.color.black));
            canvas.drawTextOnPath(member.getText(), path, 50, 0, citePaint);
 
            //在底部写文字,此处我们不需要
            float x = getWidth() / data.size() * pointer + margin;
            float y = getHeight() - margin;
            float h = getFontHeight(this.textPaint) * 0.6f;
            canvas.drawText(member.getText() + ":" + member.getNumber(), x + h + (h * 0.3f), y, citePaint);
            RectF r = new RectF(x, y - h, x + h, y);
            canvas.drawRect(r, arcPaint);
            angle += d;
        
 
    
 
    /**
     * 根据屏幕系数比例获取文字大小
     *
     * @return
     */
    private static float scalaFonts(int size) 
        //暂未实现
        return size;
    
 
    /**
     * @return 返回指定笔和指定字符串的长度
     */
    public static float getFontlength(Paint paint, String str) 
        return paint.measureText(str);
    
 
    /**
     * @return 返回指定笔的文字高度
     */
    public static float getFontHeight(Paint paint) 
        Paint.FontMetrics fm = paint.getFontMetrics();
        return fm.descent - fm.ascent;
    
 
    /**
     * @return 返回指定笔离文字顶部的基准距离
     */
    public static float getFontLeading(Paint paint) 
        Paint.FontMetrics fm = paint.getFontMetrics();
        return fm.leading - fm.ascent;
    
 
    public void setData(List<PieMember> data) 
        this.data = data;
        postInvalidate();
    
 
 
 
    public static class PieMember 
        private String text = "";
        private int number = 0;
        private int color = 0;
 
        public void augment(int n) 
            this.number += n;
        
 
        public String getText() 
            return text;
        
 
        public void setText(String text) 
            this.text = text;
        
 
        public int getNumber() 
            return number;
        
 
        public void setNumber(int number) 
            this.number = number;
        
 
        public int getColor() 
            return color;
        
 
        public void setColor(int color) 
            this.color = color;
        
    

protected void initData() 
        hashMap = new HashMap<>();
        for (int i = 0; i < 6;i++)
            hashMap.put("科"+ (i+1), i + 5);
        
        float  total = 35.0f;
        pieChart.setPieName("错题分布");
        pieChart.setData(initPie(hashMap, total));

 
private List<PieChartView.PieMember> initPie(HashMap<String,Integer> hashMap,float total) 
        List<PieChartView.PieMember>  data = new ArrayList<>();
        Iterator<Map.Entry<String, Integer>> iterator = hashMap.entrySet().iterator();
        while(iterator.hasNext())
            PieChartView.PieMember member = new PieChartView.PieMember();
            Map.Entry<String, Integer> entry = iterator.next();
            String key = entry.getKey();
            int val = entry.getValue();
            member.setNumber(val);
            member.setText(key +"错"+val);
            // member.setColor(Color.parseColor(Constant.pieColors[index]));
            //index++;
            data.add(member);
        
        return data;
    


画的思路还是一样的,这里我们只使用了一个 PieMember来处理传进来的数据,较为方便一点,主要代码:
protected void initData() 
        hashMap = new HashMap<>();
        for (int i = 0; i < 6;i++)
            hashMap.put("科"+ (i+1), i + 5);
        
        float  total = 35.0f;
        pieChart.setPieName("错题分布");
        pieChart.setData(initPie(hashMap, total));

 
private List<PieChartView.PieMember> initPie(HashMap<String,Integer> hashMap,float total) 
        List<PieChartView.PieMember>  data = new ArrayList<>();
        Iterator<Map.Entry<String, Integer>> iterator = hashMap.entrySet().iterator();
        while(iterator.hasNext())
            PieChartView.PieMember member = new PieChartView.PieMember();
            Map.Entry<String, Integer> entry = iterator.next();
            String key = entry.getKey();
            int val = entry.getValue();
            member.setNumber(val);
            member.setText(key +"错"+val);
            // member.setColor(Color.parseColor(Constant.pieColors[index]));
            //index++;
            data.add(member);
        
        return data;
    

上图: 但是我们要知道的是:这种方式不能实现旋转、手势、点击饼块变大等情况,只能满足基本需求。
    第二种方式:我们采用hellochart,相比前者,尽管需要做的很多,但是能满足我们的很多需求 需要导包: hellocharts-library-1.5.3.jar nineoldandroids-2.4.0.jar 前者实现饼图,后者实现拖拽、旋转、放大等动画 数据填充代码
       arcColos = new int[]getResources().getColor(R.color.gold), Color.BLUE,
                getResources().getColor(R.color.coral),Color.RED,
                getResources().getColor(R.color.hotpink), Color.GREEN,
                getResources().getColor(R.color.mintcream),getResources().getColor(R.color.orange);
        setPieChartData();
        initPieChart();
     
      
          
       
     /**
      * 获取数据
      */
    private void setPieChartData()
        for (int i = 0; i < data.length; ++i) 
            SliceValue sliceValue = new SliceValue((float) data[i], arcColos[i]);//这里的颜色是我写了一个工具类 是随机选择颜色的
            values.add(sliceValue);
        
    
 
 
     /**
      * 初始化PieChart
      */
    private void initPieChart() 
        pieChardata = new PieChartData();
        pieChardata.setHasLabels(true);//显示表情
        pieChardata.setHasLabelsOnlyForSelected(false);//不用点击显示占的百分比
        pieChardata.setHasLabelsOutside(false);//占的百分比是否显示在饼图外面
        pieChardata.setHasCenterCircle(true);//是否是环形显示
        pieChardata.setValues(values);//填充数据
        pieChardata.setCenterCircleColor(Color.WHITE);//设置环形中间的颜色
        pieChardata.setCenterCircleScale(0.3f);//设置环形的大小级别
        pieChardata.setCenterText1("饼图测试");//环形中间的文字1
        pieChardata.setCenterText1Color(Color.BLACK);//文字颜色
        pieChardata.setCenterText1FontSize(14);//文字大小
 
        pieChardata.setCenterText2("饼图测试");
        pieChardata.setCenterText2Color(Color.BLACK);
        pieChardata.setCenterText2FontSize(18);
        //这里也可以自定义你的字体   Roboto-Italic.ttf这个就是你的字体库
//		Typeface tf = Typeface.createFromAsset(this.getAssets(), "Roboto-Italic.ttf");
//		data.setCenterText1Typeface(tf);


 
        pieChart.setPieChartData(pieChardata);
        pieChart.setValueSelectionEnabled(true);//选择饼图某一块变大
        pieChart.setAlpha(0.8f);//设置透明度
        pieChart.setCircleFillRatio(1f);//设置饼图大小
 
    <code class="language-java" style="margin:8px 0px; font-family:Consolas,'Liberation Mono',Menlo,Courier,monospace; word-wrap:break-word"></code>
上图: 如图所示:饼块能点击方法,未展示出来的还有旋转等效果,但是个人认为真的很出,也可能是配色的原因
第三种方式:是我个人比较喜欢的方式,前两者功能都能实现,而且界面也好很多 需要导入mpAndroidChart包,这个功能相当强大,可以做一系列的线性、柱形、饼型等图,在这里只讨论饼图
https://github.com/PhilJay/MPAndroidChart
另外和前者相同,需要导入nineold包,在这里不提供了,网上应有尽有 线上数据填充的代码,基本上每一句我都加上了注释:
 PieData mPieData = getPieData(4, 100);
 showChart(pieChart, mPieData);
 
 
   /**
     *
     * @param count 分成几部分
     * @param range
     */
    private PieData getPieData(int count, float range) 
 
        ArrayList<String> xValues = new ArrayList<String>();  //xVals用来表示每个饼块上的内容
 
        xValues.add(0,"优");
        xValues.add(1,"良");
        xValues.add(2, "中");
        xValues.add(3, "差");
 
        ArrayList<Entry> yValues = new ArrayList<Entry>();  //yVals用来表示封装每个饼块的实际数据
 
        // 饼图数据
        //若想变成百分比需要设置pieChart.setUsePercentValues(true);
        float quarterly1 = 46;
        float quarterly2 = 18;
        float quarterly3 = 20;
        float quarterly4 = 15;
 
        //饼块数据
        yValues.add(new Entry(quarterly1, 0));
        yValues.add(new Entry(quarterly2, 1));
        yValues.add(new Entry(quarterly3, 2));
        yValues.add(new Entry(quarterly4, 3));
 
        //y轴的集合
        PieDataSet pieDataSet = new PieDataSet(yValues, "作业报告");//显示在比例图上
        pieDataSet.setSliceSpace(1f); //设置各饼状图之间的距离
 
        // 饼图颜色
        ArrayList<Integer> colors = new ArrayList<Integer>();
        colors.add(Color.parseColor("#b284f3"));
        colors.add(Color.parseColor("#fe7c2e"));
        colors.add(Color.parseColor("#7494d6"));
        colors.add(Color.parseColor("#42c0fa"));
        pieDataSet.setColors(colors);
 
        DisplayMetrics metrics = getResources().getDisplayMetrics();
        float px = 2 * (metrics.densityDpi / 160f);
 
        pieDataSet.setSelectionShift(px); // 选中态多出的长度
 
        PieData pieData = new PieData(xValues, pieDataSet);
 
        return pieData;
    
 
 
 
 /**初始化并填充数据*/
 private void showChart(PieChart pieChart, PieData pieData) 
        pieChart.setHoleColorTransparent(true);
        pieChart.setHoleRadius(60f);  //半径
        pieChart.setTransparentCircleRadius(64f); // 半透明圈
        pieChart.setDrawCenterText(true);  //饼状图中间可以添加文字
        pieChart.setDrawHoleEnabled(true);//饼状图中心圆是否存在
        pieChart.setCenterText("提交作业3次");  //饼状图中间的文字
        pieChart.setCenterTextSize(10);//饼状图中间的文字的大小,但是没有该字体颜色的设置
        pieChart.setValueTextSize(8);//饼图中的内容字体大小
        pieChart.setRotationAngle(90); // 初始旋转角度
 
        pieChart.setNoDataText("数据加载中...");
 
        pieChart.setRotationEnabled(true); // 可以手动旋转
        pieChart.setUsePercentValues(true);  //显示成百分比
        pieChart.setDrawXValues(true);
 
        pieChart.setDescription(null);
        //设置数据
        pieChart.setData(pieData);


 
        pieChart.highlightValues(null);
 
        //设置数据汇总,比例图
        Legend mLegend = pieChart.getLegend();
        mLegend.setPosition(Legend.LegendPosition.RIGHT_OF_CHART_CENTER); //最右边显示
        mLegend.setTextColor(getResources().getColor(R.color.dark_gray));
        mLegend.setTextSize(10);
        mLegend.setXEntrySpace(8f);
        mLegend.setYEntrySpace(8f);
 
        pieChart.animateXY(1000, 1000);  //设置动画
    


上图: 相比前者,不仅外观上好很多,而且还有分析比例等图,旋转、放大是非常流畅的
分享一下自己使用过程中出现的问题: 在hellochart中有设置改变饼块缩放大小的方法。但是我在使用mpchart的时候因为屏幕适配问题,点击放大后的饼块会被”切“掉一部分, 导致显示不完整,这在项目中肯定是不允许的,最终我玩命的查找mpchart中关于饼块缩放的方法,结果没找到,都在自己要放弃的时候, 我才发现我自己已经标记出来了 pieDataSet . setSelectionShift ( px ); // 选中态多出的长度 ,狠狠的被打了脸。原来它不是在控件piechart控制的,而是由 pieDataSet来处理,这就是先用了hellochart,在使用mpachart的毛病了

以上是关于最近项目里碰上了饼图,顺便整理了下几种Android饼图,以作参考的主要内容,如果未能解决你的问题,请参考以下文章

Linux下几种文件传输命令

Linux下几种文件传输命令 sz rz sftp scp

增加饼图matplotlib中标签的字体大小[重复]

如何在 d3 中悬停时增加饼图段的大小

如何在PIE图表中显示图表从图表到饼图钻取高图

点击获取li下标的几种方式