此为效果图
参考:http://blog.csdn.NET/u010053224/article/details/51799523
/** * Created by ${ITcat} on 2017/3/1. * * 自定义的圆形饼图控件 */ public class MyPieChartView extends View { private final float density = getResources().getDisplayMetrics().density; //饼图之间的间隔的角度 private final float LR_PADDING = 25 * density; //顶部的空间距离 private final float TOP_PADDING = 25 * density; //饼图和下面文字之间的距离 private final float PIE_TEXT_DIS = 22 * density; //上下行文字之间的距离 private final float TEXT_TEXT_DIS = 15 * density; //底部和文字的距离,实际为BOTTOM_DIS+TEXT_TEXT_DIS private final float BOTTOM_DIS = 10 * density; //标题和值之间的最小距离 private final float TITLE_VALUE_DIS = 18 * density; //每部分的颜色值,默认有10个颜色 private int[] mColors = new int[]{0xfff5a002,0xfffb5a2f,0xff36bc99,0xff43F90C,0xff181A18,0xffF802F6 ,0xff022DF8,0xffECF802,0xff02F8E8,0xffEA0F8E}; //值 private double[] mValues; //值转换成角度 private float[] mAngles; //饼图直径 private float pieR; //饼图所占的总的角度 private float pieAngle; private String[] mTitles; //每部分的内容 private float mTitleSize; private float mValueSize; private int mTitleColor = 0xFF999999; private int mValueColor = 0xFF999999; private Paint mPiePaint; private Paint mTextPaint; public MyPieChartView(Context context) { this(context,null); } public MyPieChartView(Context context, AttributeSet attrs) { this(context, attrs,0); } public MyPieChartView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mTitleSize = sp2px(12); mValueSize = sp2px(12); mPiePaint = new Paint(); mTextPaint = new Paint(); mTextPaint.setColor(0xff999999); mTextPaint.setTextSize(mTitleSize); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int newHeightSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,MeasureSpec.AT_MOST); super.onMeasure(widthMeasureSpec, newHeightSpec); } /** * 重写onDraw方法 * @param canvas */ @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); mPiePaint.setAntiAlias(true); mTextPaint.setAntiAlias(true); /**------------------------------无数据时的默认显示--------------------------------*/ if (mTitles == null || mTitles.length == 0 || isValueEmpty()) { mTitles = new String[]{"美利金融","微贷网","团贷网","其他"}; mValues = new double[] {999,999,999,999}; mColors = new int[]{0xfff5a002,0xfffb5a2f,0xff36bc99,0xfff7a003}; int textHeight = getTextHeight("00",Math.max(mTitleSize,mValueSize)); float r1 = getWidth() - LR_PADDING*2; float r2 = getHeight() - TOP_PADDING - PIE_TEXT_DIS - (TEXT_TEXT_DIS+textHeight)*(mTitles.length) - BOTTOM_DIS; pieR = Math.min(r1,r2)/2;//为了防止饼图越界,饼图直径选取最小值 //画矩形,左上右下 RectF oval = new RectF(0,TOP_PADDING,pieR/2,TOP_PADDING+pieR/2); mPiePaint.setStyle(Paint.Style.FILL); mAngles = getAngles(); float startAngle = 0; for (int i = 0; i < mAngles.length; i++) { mPiePaint.setColor(mColors[i]); if (mAngles[i] == 0) continue;; canvas.drawArc(oval,startAngle,mAngles[i],true,mPiePaint); startAngle += (mAngles[i]); } /**---画一个遮盖圆在上面形成一个圆环(这里可以修改内圆的大小和圆心坐标)--------------------------------*/ mPiePaint.setColor(0xFFFFFFFF); float cr = (getWidth()<getHeight()?getWidth()/2:getHeight()/2) - LR_PADDING; //获取到圆心的坐标,获取到半径和画笔调用画方法(调节小圆的半径可以调节控件的大小) canvas.drawCircle(pieR/4,TOP_PADDING+pieR/4,cr/8,mPiePaint); /**------------------------------右边的文字,title和value--------------------------------*/ float cr1 = 4 * density;//圆点的半径 float ctd = 8 * density;//圆点和右边文字的距离 float titleLen = getMaxTextWidth(mTitles,mTitleSize) + TITLE_VALUE_DIS; float len = 2*cr1 + ctd + titleLen + getMaxTextWidth(mTitles,mTitleSize); float topDis = TOP_PADDING +TEXT_TEXT_DIS+textHeight;//第一行文字底部和控件顶部的距离 float cX = (getWidth() - len)/2 + cr1 -20; float titleX = cX+cr1+ctd -20; float valueX = titleX + titleLen -40; setPieContentText(); /**---------------循环写入内部文字和所占百分比--------------------------------*/ float yDis = topDis; for (int i = 0; i < mTitles.length; i++) { mPiePaint.setColor(mColors[i]); if (i == 1) { cX = cX + 250; titleX = titleX +250; valueX = valueX +230; } if (i == 2) { yDis = topDis+60; cX = cX - 250; titleX = titleX -250; valueX = valueX -230; } if (i == 3) { yDis = topDis+60; cX = cX + 250; titleX = titleX +250; valueX = valueX +230; } canvas.drawCircle(cX,yDis-textHeight/2,cr1,mPiePaint); mTextPaint.setTextSize(mTitleSize); mTextPaint.setColor(mTitleColor); canvas.drawText(mTitles[i],titleX,yDis,mTextPaint); mTextPaint.setColor(mValueColor); mTextPaint.setTextSize(mValueSize); canvas.drawText("0.00%",valueX,yDis,mTextPaint); } return; } else{ /**------------------------------有数据时的显示--------------------------------*/ /**------------------------------画饼图--------------------------------*/ int textHeight = getTextHeight("00",Math.max(mTitleSize,mValueSize)); float r1 = getWidth() - LR_PADDING*2; float r2 = getHeight() - TOP_PADDING - PIE_TEXT_DIS - (TEXT_TEXT_DIS+textHeight)*(mTitles.length) - BOTTOM_DIS; pieR = Math.min(r1,r2)/2;//为了防止饼图越界,饼图直径选取最小值 //画矩形,左上右下 /**--------控制外圆的圆心,修改距离上的值--------------------------------*/ RectF oval = new RectF(0,TOP_PADDING-20,pieR/2,TOP_PADDING+pieR/2-20); mPiePaint.setStyle(Paint.Style.FILL); mAngles = getAngles(); float startAngle = 0; for (int i = 0; i < mAngles.length; i++) { mPiePaint.setColor(mColors[i]); if (mAngles[i] == 0) continue;; canvas.drawArc(oval,startAngle,mAngles[i],true,mPiePaint); startAngle += (mAngles[i]); } /**---画一个遮盖圆在上面形成一个圆环(这里可以修改内圆的大小和圆心坐标)--------------------------------*/ mPiePaint.setColor(0xFFFFFFFF); float cr = (getWidth()<getHeight()?getWidth()/2:getHeight()/2) - LR_PADDING; //获取到圆心的坐标,获取到半径和画笔调用画方法(调节小圆的半径可以调节控件的大小) /**--------控制内圆的圆心,修改距离上的值--------------------------------*/ canvas.drawCircle(pieR/4,TOP_PADDING+pieR/4-20,cr/8,mPiePaint); /**------------------------------画饼图--------------------------------*/ /**------------------------------右边的文字,title和value--------------------------------*/ float cr1 = 4 * density;//圆点的半径 float ctd = 8 * density;//圆点和右边文字的距离 float titleLen = getMaxTextWidth(mTitles,mTitleSize) + TITLE_VALUE_DIS; float len = 2*cr1 + ctd + titleLen + getMaxTextWidth(mTitles,mTitleSize); /**---------------控制右边的行高度--------------------------------*/ float topDis = TOP_PADDING +TEXT_TEXT_DIS+textHeight -20;//第一行文字底部和控件顶部的距离 float cX = (getWidth() - len)/2 + cr1 -20; float titleX = cX+cr1+ctd -10; float valueX = titleX + titleLen -30; setPieContentText(); /**---------------循环写入内部文字和所占百分比--------------------------------*/ float yDis = topDis; for (int i = 0; i < mTitles.length; i++) { mPiePaint.setColor(mColors[i]); if (i == 1) { cX = cX + 250; titleX = titleX +250; valueX = valueX +230; } if (i == 2) { yDis = topDis+60; cX = cX - 250; titleX = titleX -250; valueX = valueX -230; } if (i == 3) { yDis = topDis+60; cX = cX + 250; titleX = titleX +250; valueX = valueX +230; } setData(canvas,cX,yDis,textHeight,cr1,titleX,valueX,i); } } } /** * 设置饼图左边的文字和圆点 */ private void setData(Canvas canvas,float cX,float yDis,float textHeight,float cr1,float titleX,float valueX,int i) { canvas.drawCircle(cX,yDis-textHeight/2,cr1,mPiePaint); mTextPaint.setTextSize(mTitleSize); mTextPaint.setColor(mTitleColor); canvas.drawText(mTitles[i],titleX,yDis,mTextPaint); mTextPaint.setColor(mValueColor); mTextPaint.setTextSize(mValueSize); canvas.drawText(percents[i],valueX,yDis,mTextPaint); } /** * 设置每个饼图代表的名字 * @param titles */ public void setTitles(List<String> titles){ mTitles = (String[])titles.toArray(); } /** * 设置每个饼图代表的名字 * @param titles */ public void setTitles(String[] titles){ mTitles = titles; } /** * 设置每个名字代表的值 * @param values */ public void setValues(List<Double> values){ mValues = new double[values.size()]; for(int i=0;i<values.size();i++){ mValues[i] = values.get(i); } } /** * 设置每个名字代表的值 * @param values */ public void setValues(double[] values){ mValues = values; } /** * 设置每块饼图的颜色 * @param colors */ public void setColors(List<Integer> colors){ mColors = new int[colors.size()]; for(int i=0;i<colors.size();i++){ mColors[i] = colors.get(i); } } /** * 设置每块饼图的颜色 * @param colors */ public void setColors(int[] colors){ mColors = colors; } /** * 设置名字的字体大小 * @param size */ public void setTitleSize(float size){ mTitleSize = sp2px(size); } /** * 设置数值的大小 * @param size */ public void setValueSize(float size){ mValueSize = sp2px(size); } private float sp2px(float sp){ return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, getResources().getDisplayMetrics()); } /** * 名字的颜色 * @param titleColor */ public void setTitleColor(int titleColor){ mTitleColor = titleColor; } /** * 数值的颜色 * @param valueColor */ public void setValueColor(int valueColor){ mValueColor = valueColor; } /** * 判断是否有数据 * @return */ private boolean isValueEmpty() { if (mValues == null || mValues.length == 0) return true; for (double va:mValues) { if (va > 0 ) { return false; } } return true; } //获取文本Text的宽度 private int getTextWidth(String str,float size){ return getTextRect(str, size).width(); } //获取文本Text的高度 private int getTextHeight(String str,float size){ return getTextRect(str, size).height(); } //获取文本Text的矩形 private Rect getTextRect(String str,float size){ Rect textBound = new Rect(); Paint paint = new Paint(); paint.setTextSize(size); paint.getTextBounds(str, 0, str.length(), textBound); return textBound; } /** * 获取每个值所占角度 */ private float[] getAngles() { if (mValues == null || mValues.length == 0) return null; double sum = 0; int len = mTitles.length; float[] angles = new float[len]; for (int i = 0; i < len; i++) { sum += mValues[i]; } float angle = 0; pieAngle = 360; /**-----把每个值所占的角度赋值给角度的数组,最后返回数组--------*/ for (int i = 0; i < len - 1; i++) { angles[i] = (float) (pieAngle*mValues[i]/sum); angle += angles[i]; } if (mValues[len-1]>0) angles[len-1] = pieAngle - angle; return angles; } private int getMaxTextWidth(double[] ds,float size) { String[] strs = new String[ds.length]; for (int i = 0; i < ds.length; i++) { strs[i] = ds[i]+""; } return getMaxTextWidth(strs,size); } private int getMaxTextWidth(String[] strs,float size) { Rect textBound = new Rect(); Paint paint = new Paint(); paint.setTextSize(size); int len = 0; for (int i = 0; i < strs.length; i++) { paint.getTextBounds(strs[i],0,strs[i].length(),textBound); len = Math.max(len,textBound.width()); } return len; } /** * 获取每块饼图所占的百分比 * @param canvas */ private String[] percents = new String[4]; private void setPieContentText() { float pre = 0; float[] centerAngle = new float[mAngles.length]; for (int i = 0; i < mAngles.length; i++) { if (mAngles[i] == 0) continue; centerAngle[i] = mAngles[i]/2 +pre; pre += mAngles[i]; } float lastPir = 1.0f;//为了使所有的%加起来等于1 mTextPaint.setColor(0xFFFFFFFF); for (int i = 0; i < centerAngle.length; i++) { if (centerAngle[i] == 0) continue; double curPer = numDecimals(mAngles[i]/pieAngle); String perMsg = i == centerAngle.length-1?percentFormat(lastPir):percentFormat(curPer); percents[i]=perMsg; lastPir -= curPer; } } /** * 格式化为百分比格式 * @param num * @return */ private String percentFormat(double num){ DecimalFormat df = new DecimalFormat("#0.00%"); return df.format(num); } /** * 保留四位小数 * @param num * @return */ public static double numDecimals(double num){ return ((int)(num*10000))*1.0d/10000; } }