【Android开发日记】AndroidCharts 饼状图 PieView修改:添加数值和颜色

本文记录了如何修改AndroidCharts库中的PieView,以显示饼状图上每个扇区的数值并按比例涂色。通过自定义PieHelper类计算扇形的起始角度和终止角度,并在饼图上标记数据。最后展示了修改后的效果和关键代码片段。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言:前段时间做的工程,自己一个人小打小闹的修改着,代码不完善之处请在评论指出,谢谢!

工程来源:github AndroidCharts:https://github.com/dacer/AndroidCharts

修改:使得PieView可以显示数字和颜色

我的需求:将每间教室的名称显示在图上,同时根据数值 按比例涂色

效果图:


代码1:PieView修改后的代码:

package com.androidcharts;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;

import java.util.ArrayList;


/**
 * Created by Dacer on 11/13/13.
 */
public class PieView extends View {

    private Paint textPaint;
    private Paint redPaint;
    private Paint linePaint;
    private Paint whitePaint;
    private int mViewWidth;
    private int mViewHeight;
    private int textSize;
    private int pieRadius;
    private Point pieCenterPoint;
    private Point tempPoint;
    private Point tempPointRight;
    private int lineLength;
    private float leftTextWidth;
    private float rightTextWidth;
    private float topTextHeight;
    private int lineThickness;
    private RectF cirRect;
    private Rect textRect;
    private ArrayList<String> areanameList;
    
    private ArrayList<PieHelper> pieArrayList = new ArrayList<PieHelper>();
    private ArrayList<PieHelper> pieArrayList_ = new ArrayList<PieHelper>();  //单纯备份传进的角度
    
    private final int TEXT_COLOR = Color.parseColor("#9B9A9B");
    private final int GRAY_COLOR = Color.parseColor("#D4D3D4");
    private final int RED_COLOR = Color.argb(50, 255, 0, 51);
    private String[] colorArray = {"#e74c3c","#2980b9","#1abc9c"};

    private Runnable animator = new Runnable() {
        @Override
        public void run() {
            boolean needNewFrame = false;
            for(PieHelper pie : pieArrayList){
                pie.update();
                if(!pie.isAtRest()){
                    needNewFrame = true;
                }
            }
            if (needNewFrame) {
                postDelayed(this, 10);
            }
            invalidate();
        }
    };

    public PieView(Context context){
        this(context,null);
    }
    public PieView(Context context, AttributeSet attrs){
        super(context, attrs);
        textSize = MyUtils.sp2px(context, 15);
        lineThickness = MyUtils.dip2px(context, 1);
        lineLength = MyUtils.dip2px(context, 10);

        textPaint = new Paint();
        textPaint.setAntiAlias(true);
        textPaint.setColor(TEXT_COLOR);
        textPaint.setTextSize(textSize);
        textPaint.setTextAlign(Paint.Align.CENTER);
        Paint.FontMetrics fm = new Paint.FontMetrics();
        textPaint.getFontMetrics(fm);
        textRect = new Rect();
        textPaint.getTextBounds("18",0,1,textRect);
        redPaint = new Paint(textPaint);
        redPaint.setColor(RED_COLOR);
        linePaint = new Paint(textPaint);
        linePaint.setColor(GRAY_COLOR);
        linePaint.setStrokeWidth(lineThickness);
        whitePaint = new Paint(linePaint);
        whitePaint.setColor(Color.WHITE);
        tempPoint = new Point();
        pieCenterPoint = new Point();
        tempPointRight = new Point();
        cirRect = new RectF();
        leftTextWidth = textPaint.measureText("18");
        rightTextWidth = textPaint.measureText("6");
        topTextHeight = textRect.height();
    }
    
    //传进画图需要的数据
    public void setDate(ArrayList<PieHelper> helperList){
        if(helperList != null && !helperList.isEmpty()){
        	this.pieArrayList_ = helperList;
            int pieSize = pieArrayList.isEmpty()? 0:pieArrayList.size();
            for(int i=0;i<helperList.size();i++){
                if(i>pieSize-1){
//                    float mStart = helperList.get(i).getStart();
                    pieArrayList.add(new PieHelper(0,0,helperList.get(i)));
                }else{
                    pieArrayList.set(i, pieArrayList.get(i).setTarget(helperList.get(i)));
                }
            }
            int temp = pieArrayList.size() - helperList.size();
            for(int i=0; i<temp; i++){
                pieArrayList.remove(pieArrayList.size()-1);
            }
        }else {
            pieArrayList.clear();
        }
        
        removeCallbacks(animator);
        post(animator);
    }
    
    //传进要画在图上的数据
    public void setareanameList(ArrayList<String> AreaNameList){
        this.areanameList = AreaNameList;
      }
    
    
    @Override
    protected void onDraw(Canvas canvas) {
        drawBackground(canvas);
        if(pieArrayList != null){
        	//涂色
        	for(int k=0; k<pieArrayList.size(); k++){
        		redPaint.setColor(Color.parseColor(colorArray[k%3]));
        		canvas.drawArc(cirRect,pieArrayList.get(k).getStart(),pieArrayList.get(k).getSweep(),true,redPaint);	   
        	}
        	
        	//画数据
        	for(int k=0; k< pieArrayList_.size(); k++){
        		//angle:12点方向起始,顺时针的角度
                double angle = pieArrayList_.get(k).getStart()-270+pieArrayList_.get(k).getSweep()/2;

                //90-angle: 360-(angle-90) 转化成平面坐标系中角度,3点起始,逆时针
   	            float x = (float) (pieCenterPoint.x+Math.cos(Math.toRadians(90-angle))*pieRadius*0.8);
   	            float y = (float) (pieCenterPoint.y-Math.sin(Math.toRadians(90-angle))*pieRadius*0.8);
   	            canvas.drawText(areanameList.get(k),x, y, textPaint);
           }
        }
    }

    //背景,主要是外圈圆
    private void drawBackground(Canvas canvas){
    	
        //外圈白
        //canvas.drawCircle(pieCenterPoint.x,pieCenterPoint.y,pieRadius+lineLength/2, whitePaint);
        //圆
        canvas.drawCircle(pieCenterPoint.x,pieCenterPoint.y,pieRadius+lineThickness,linePaint);
        //内圈白
        canvas.drawCircle(pieCenterPoint.x,pieCenterPoint.y,pieRadius,whitePaint);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        mViewWidth = measureWidth(widthMeasureSpec);
        mViewHeight = measureHeight(heightMeasureSpec);
        pieRadius = mViewWidth*5/12-lineLength*2-(int)(textPaint.measureText("18")/2);
        pieCenterPoint.set(mViewWidth/2-(int)rightTextWidth/2+(int)leftTextWidth/2,
                mViewHeight/2+textSize/2-(int)(textPaint.measureText("18")/2));
        cirRect.set(pieCenterPoint.x-pieRadius,
                pieCenterPoint.y-pieRadius,
                pieCenterPoint.x+pieRadius,
                pieCenterPoint.y+pieRadius);
        setMeasuredDimension(mViewWidth, mViewHeight);
    }

    private int measureWidth(int measureSpec){
        int preferred = 3;
        return getMeasurement(measureSpec, preferred);
    }

    private int measureHeight(int measureSpec){
        int preferred = mViewWidth;
        return getMeasurement(measureSpec, preferred);
    }

    private int getMeasurement(int measureSpec, int preferred){
        int specSize = View.MeasureSpec.getSize(measureSpec);
        int measurement;

        switch(View.MeasureSpec.getMode(measureSpec)){
            case View.MeasureSpec.EXACTLY:
                measurement = specSize;
                break;
            case View.MeasureSpec.AT_MOST:
                measurement = Math.min(preferred, specSize);
                break;
            default:
                measurement = preferred;
                break;
        }
        return measurement;
    }
}

代码2:实例化PieView,传递数据

final PieView pieView = (PieView)this.findViewById(R.id.pie_view);   

Setpie(pieView);

private void Setpie(PieView pieView){
	        ArrayList<PieHelper> pieHelperArrayList = new ArrayList<PieHelper>();
	        int SHour =0;
	        int SMin =0;
	        int EHour =0;
	        int EMin =0;
	        int degree =0;
	        int degreenew =0;
	        int r = dataList.length;
	        for (int i=0; i<r/3; i++){
	        	Log.e("totalblanknum", "=" + totalblanknum);
	        	Log.e("dataList[1+i*3]", "=" + dataList[1+i*3]);
	        	degree = Integer.parseInt(dataList[1+i*3])*360/totalblanknum ;
	        	Log.e("degree", "=" + degree);
	        	degreenew = degree+ degreenew;
	        	Log.e("degreenew", "=" + degreenew);
	        	EHour = degreenew/30;
	        	EMin = degreenew%30*2; 
	        	if(i+1 == r/3)
	        	{
	        		EHour = 12;
		        	EMin = 0; 
	        	}
	        	//Log.e("EHour", "=" + EHour);
	        	//Log.e("EMin", "=" + EMin);

	        	pieHelperArrayList.add(new PieHelper(SHour,SMin,EHour,EMin));
	        	SHour =EHour;
	        	SMin =EMin;
	        }
	        pieView.setareanameList(bottomList); //传进area名称
	        pieView.setDate(pieHelperArrayList);
	    }

说明:

1.dataList数组是一个 字符串数组,数据结构:name1,blanknum1,totalnum1,name2,blanknum2,totalnum2,,,(参考上篇文章BarView的修改)

    这里根据blanknum1,blanknum2,blanknum3...来确定PieView的比例,将name1,name2,name3...绘制在图上对应扇区

2.totalblanknum是 数据的总和,因为这个pieview是我界面的一部分,这个值在之前已经求出,就是相加:

 

 for (int i=0; i<r/3; i++)
	        	totalblanknum = totalblanknum + Integer.parseInt(dataList[1+i*3]);

3.PieHelper 是AndroidCharts中的一个数据类型,因为话扇形需要知道 起始角度和终止角度 以及方向坐标轴等,因此原作者自定义了一个数据类型

   现将PieHelper.java贴出:

package com.androidcharts;


/**
 * Created by Dacer on 11/14/13.
 */
public class PieHelper {

    private float start;
    private float end;
    private float targetStart;
    private float targetEnd;
    int velocity = 5;

    public PieHelper(float startDegree, float endDegree, PieHelper targetPie){
        start = startDegree;
        end = endDegree;
        targetStart = targetPie.getStart();
        targetEnd = targetPie.getEnd();
    }

    public PieHelper(int startHour,int startMin,int endHour,int endMin){
        start = 270+startHour*30+startMin*30/60;
        end = 270+endHour*30+endMin*30/60;
        while(end<start){
            end+=360;
        }
    }

    public PieHelper(int startHour,int startMin,int startSec,int endHour,int endMin,int endSec){
        start = 270+startHour*15+startMin*15/60+startSec*15/3600;
        end = 270+endHour*15+endMin*15/60+endSec*15/3600;
        while(end<start){
            end+=360;
        }
    }

    PieHelper setTarget(float targetStart,float targetEnd){
        this.targetStart = targetStart;
        this.targetEnd = targetEnd;
        return this;
    }

    PieHelper setTarget(PieHelper targetPie){
        targetStart = targetPie.getStart();
        targetEnd = targetPie.getEnd();
        return this;
    }

    boolean isAtRest(){
        return (start==targetStart)&&(end==targetEnd);
    }

    void update(){
        start = updateSelf(start, targetStart, velocity);
        end = updateSelf(end, targetEnd, velocity);
    }

    public float getSweep(){
        return end-start;
    }

    public float getStart(){
        return start;
    }

    public float getEnd(){
        return end;
    }

    private float updateSelf(float origin, float target, int velocity){
        if (origin < target) {
            origin += velocity;
        } else if (origin > target){
            origin-= velocity;
        }
        if(Math.abs(target-origin)<velocity){
            origin = target;
        }
        return origin;
    }
}
说明:

1. 每一个PieHelper数据包括四个参数:

    new PieHelper(SHour,SMin,EHour,EMin)

    起始小时,起始分钟,终止小时,终止分钟 来确定一个扇区,12小时计时法

2. 根据数据得到起始角度:

    首先根据某个数据占总数的比例得到degree,即扇形圆心角:degree = Integer.parseInt(dataList[1+i*3])*360/totalblanknum ;

   在上一个扇形的角度基础上加上这个圆心角作为起始角度:degreenew = degree+ degreenew;

   由起始角度得到终止hour,min

   EHour = degreenew/30;
   EMin = degreenew%30*2; 

   起始hour,min是上一个扇形的终止hour,min

   SHour =EHour;
   SMin =EMin;

3.由于默认0度 是x轴正方向,即3点钟钟方向,因此在PieHelper中将SHour,EHour,SMin ,EMin 化成角度后 都加上了 270°

4.PieView.java 中拿到 ArrayList<PieHelper> helperList 如何确定 标记数据的坐标

for(int k=0; k< pieArrayList_.size(); k++){
        		//angle:12点方向起始,顺时针的角度
                double angle = pieArrayList_.get(k).getStart()-270+pieArrayList_.get(k).getSweep()/2;
                //90-angle: 360-(angle-90) 转化成平面坐标系中角度,3点起始,逆时针
   	            float x = (float) (pieCenterPoint.x+Math.cos(Math.toRadians(90-angle))*pieRadius*0.8);
   	            float y = (float) (pieCenterPoint.y-Math.sin(Math.toRadians(90-angle))*pieRadius*0.8);
   	            canvas.drawText(areanameList.get(k),x, y, textPaint);
           }

因为要用到正余弦,故将角度转换回  平面直角坐标系中的角度。然后利用公式,根据圆心角坐标以及半径和角度 求出要绘制的数据的坐标

注意这里的

Math.cos(Math.toRadians(90-angle))
中的Math.toRadians()函数。

5:我将画数据的步骤和画外围圆圈的步骤放在一起,绘制的时候是数据和圆圈先显示出来,然后扇形绘制,看过demo的都知道pieview和barview绘制都是一个动态的过程。

相当于拿一条带颜色的线扫过圆面,涂上颜色,得到图形。我用了一个和 pieArrayList 数据完全相同的 list  pieArrayList_来标记要显示的数据,而用pieArrayList来绘制图形

防止每一次圆面刷新数据都会重新显示因此闪烁的后果,如果大家有别的办法可以在评论里贴出来。






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值