1.首先看效果图

2.自定义PieChartView,继承自View,下边为PieChartView代码

package com.yingjinbao.im.peach.customview;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View; /**
* Created by wanger on 07/19/2018.
*/ public class PieChartView extends View { private int mHeight, mWidth;//宽高
private Paint mPaint;//扇形的画笔
private Paint mTextPaint;//画文字的画笔 private int centerX, centerY;//中心坐标 private int maxNum = 4;//扇形图的最大块数,超出部分自动合并到最后一块上去 double total;//数据的总和
double[] datas;//数据集
String[] texts;//每个数据对应的文字集 //颜色 默认的颜色
private int[] mColors = {
Color.parseColor("#FFC65B"), Color.parseColor("#FD5998"),
Color.parseColor("#8971FB"), Color.parseColor("#676974")
}; private int mTextSize;//文字大小 private int radius = 1000;//半径 public PieChartView(Context context) {
super(context);
} public PieChartView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
} //初始化
private void init() {
mTextSize = 25; mPaint = new Paint();
//当画笔样式为STROKE或FILL_OR_STROKE时,设置笔刷的图形样式, 如圆形样Cap.ROUND,或方形样式Cap.SQUARE
mPaint.setStrokeCap(Paint.Cap.ROUND);
//设置是否使用抗锯齿功能,会消耗较大资源,绘制图形速度会变慢。
mPaint.setAntiAlias(true); mTextPaint = new Paint();
//设置绘制文字的字号大小
mTextPaint.setTextSize(mTextSize);
//当画笔样式为STROKE或FILL_OR_STROKE时,设置笔刷的粗细度
mTextPaint.setStrokeWidth(3);
//设置是否使用抗锯齿功能,会消耗较大资源,绘制图形速度会变慢。
mTextPaint.setAntiAlias(true);
//设置绘制的颜色,使用颜色值来表示,该颜色值包括透明度和RGB颜色。
mTextPaint.setColor(Color.WHITE);
} @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//获取宽高 不要设置wrap_content
mHeight = MeasureSpec.getSize(heightMeasureSpec);
mWidth = MeasureSpec.getSize(widthMeasureSpec);
} @Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//无数据
if (datas == null || datas.length == 0) return; centerX = (getRight() - getLeft()) / 2;
centerY = (getBottom() - getTop()) / 2;
int min = mHeight > mWidth ? mWidth : mHeight;
if (radius > min / 2) {
radius = (int) ((min - getPaddingTop() - getPaddingBottom()) / 3.5);
} //画扇形
canvas.save();
drawCircle(canvas);
canvas.restore(); //线与文字
canvas.save();
drawLineAndText(canvas);
canvas.restore(); } //画线与文字
private void drawLineAndText(Canvas canvas) {
int start = 0;
canvas.translate(centerX, centerY);//平移画布到中心
mPaint.setStrokeWidth(4);
for (int i = 0; i < (maxNum < datas.length ? maxNum : datas.length); i++) { if (i == (maxNum < datas.length ? maxNum : datas.length) - 1){
drawLine(canvas, start, 360 - start, texts[i], mColors[i % mColors.length],i);
}else {
float angles = (float) ((datas[i] * 1.0f / total) * 360);
drawLine(canvas, start, angles, texts[i], mColors[i % mColors.length],i);
start += angles;
}
}
} private void drawLine(Canvas canvas, int start, float angles, String text, int color,int position) {
mPaint.setColor(color);
//mTextPaint.setColor(color);
float stopX, stopY;
stopX = (float) ((radius + 40) * Math.cos((2 * start + angles) / 2 * Math.PI / 180));
stopY = (float) ((radius + 40) * Math.sin((2 * start + angles) / 2 * Math.PI / 180)); switch (position){
case 0:
canvas.drawLine((float) ((radius * 0.5) * Math.cos((2 * start + angles) / 2 * Math.PI / 180)),
(float) ((radius * 0.5) * Math.sin((2 * start + angles) / 2 * Math.PI / 180)),
stopX, stopY, mPaint);
break;
case 1:
canvas.drawLine((float) ((radius * 0.7) * Math.cos((2 * start + angles) / 2 * Math.PI / 180)),
(float) ((radius * 0.7) * Math.sin((2 * start + angles) / 2 * Math.PI / 180)),
stopX, stopY, mPaint);
break;
case 2:
canvas.drawLine((float) ((radius * 1.1) * Math.cos((2 * start + angles) / 2 * Math.PI / 180)),
(float) ((radius * 1.1) * Math.sin((2 * start + angles) / 2 * Math.PI / 180)),
stopX, stopY, mPaint);
break;
case 3:
canvas.drawLine((float) ((radius * 0.9) * Math.cos((2 * start + angles) / 2 * Math.PI / 180)),
(float) ((radius * 0.9) * Math.sin((2 * start + angles) / 2 * Math.PI / 180)),
stopX, stopY, mPaint);
break;
default:
canvas.drawLine((float) (radius * Math.cos((2 * start + angles) / 2 * Math.PI / 180)),
(float) (radius * Math.sin((2 * start + angles) / 2 * Math.PI / 180)),
stopX, stopY, mPaint);
break;
}
//canvas.drawLine(0, 0, stopX, stopY, mPaint);
//画横线
int dx;//判断横线是画在左边还是右边
int endX;
if (stopX > 0) {
endX = (int) (stopX + 110);
} else {
endX = (int) (stopX - 110);
}
//画横线
canvas.drawLine(stopX, stopY,
endX, stopY, mPaint
);
dx = (int) (endX - stopX); //测量文字大小
Rect rect = new Rect();
mTextPaint.getTextBounds(text, 0, text.length(), rect);
int w = rect.width();
int h = rect.height();
int offset = 20;//文字在横线的偏移量
//画文字
canvas.drawText(text, 0, text.length(), dx > 0 ? stopX + offset : stopX - w - offset, stopY - 5, mTextPaint); //测量百分比大小
String percentage = (datas[position] / total) * 100 + "";
percentage = percentage.substring(0, percentage.length() > 4 ? 4 : percentage.length()) + "%";
mTextPaint.getTextBounds(percentage, 0, percentage.length(), rect);
w = rect.width() - 10;
//画百分比
canvas.drawText(percentage, 0, percentage.length(), dx > 0 ? stopX + offset : stopX - w - offset, stopY + h, mTextPaint); } //画扇形
private void drawCircle(Canvas canvas) {
RectF rect = null;
int start = 0;
for (int i = 0; i < (maxNum < datas.length ? maxNum : datas.length); i++) {
if (i == 0){
rect = new RectF((float) (centerX - radius * 0.5), (float) (centerY - radius * 0.5),
(float) (centerX + radius * 0.5), (float) (centerY + radius * 0.5));
}else if (i == 1){
rect = new RectF((float) (centerX - radius * 0.7), (float) (centerY - radius * 0.7),
(float) (centerX + radius * 0.7), (float) (centerY + radius * 0.7));
}else if (i == 2){
rect = new RectF((float) (centerX - radius * 1.1), (float) (centerY - radius * 1.1),
(float) (centerX + radius * 1.1), (float) (centerY + radius * 1.1));
}else if (i == 3){
rect = new RectF((float) (centerX - radius * 0.9), (float) (centerY - radius * 0.9),
(float) (centerX + radius * 0.9), (float) (centerY + radius * 0.9));
}else {
rect = new RectF((float) (centerX - radius * 1), (float) (centerY - radius * 1),
(float) (centerX + radius * 1), (float) (centerY + radius * 1));
} if (i == (maxNum < datas.length ? maxNum : datas.length) - 1){
float angles = 360 - start;
mPaint.setColor(mColors[i % mColors.length]); if (i == 2){
canvas.drawArc(rect, start, angles, true, mPaint); mPaint.setColor(Color.parseColor("#34374A"));
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(8);
rect = new RectF((float) (centerX - radius * 1.03), (float) (centerY - radius * 1.03),
(float) (centerX + radius * 1.03), (float) (centerY + radius * 1.03)); canvas.drawArc(rect, start, angles, false, mPaint);
init();
}else {
canvas.drawArc(rect, start, angles, true, mPaint);
} start += angles;
}else {
float angles = (float) ((datas[i] * 1.0f / total) * 360);
mPaint.setColor(mColors[i % mColors.length]); if (i == 2){
canvas.drawArc(rect, start, angles, true, mPaint); mPaint.setColor(Color.parseColor("#34374A"));
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(8);
rect = new RectF((float) (centerX - radius * 1.03), (float) (centerY - radius * 1.03),
(float) (centerX + radius * 1.03), (float) (centerY + radius * 1.03)); canvas.drawArc(rect, start, angles, false, mPaint);
init();
}else {
canvas.drawArc(rect, start, angles, true, mPaint);
} start += angles;
}
}
} //setter
public void setColors(int[] mColors) {
this.mColors = mColors;
invalidate();
} public void setTextSize(int mTextSize) {
this.mTextSize = mTextSize;
mTextPaint.setTextSize(mTextSize);
invalidate();
} public void setRadius(int radius) {
this.radius = radius;
invalidate();
} public void setMaxNum(int maxNum) {
this.maxNum = maxNum;
invalidate();
} public void setDatas(double[] datas) {
this.datas = datas;
total = 0;
for (int i = 0;i < datas.length;i++){
total += datas[i];
}
} public void setTexts(String[] texts) {
this.texts = texts;
} }
3.使用,首先在XML文件中调用

 之后,在activity中,只需找到组件,传入数据,调用 invalidate() 进行重绘即可。

												

android 每个块半径不同的扇形图,自定义view的更多相关文章

  1. Android零基础入门第24节:自定义View简单使用

    原文:Android零基础入门第24节:自定义View简单使用 当我们开发中遇到Android原生的组件无法满足需求时,这时候就应该自定义View来满足这些特殊的组件需求. 一.概述 很多初入Andr ...

  2. Android UI 绘制过程浅析(五)自定义View

    前言 这已经是Android UI 绘制过程浅析系列文章的第五篇了,不出意外的话也是最后一篇.再次声明一下,这一系列文章,是我在拜读了csdn大牛郭霖的博客文章<带你一步步深入了解View> ...

  3. [转]Android自定义控件三部曲系列完全解析(动画, 绘图, 自定义View)

    来源:http://blog.csdn.net/harvic880925/article/details/50995268 一.自定义控件三部曲之动画篇 1.<自定义控件三部曲之动画篇(一)—— ...

  4. Android开发之漫漫长途 番外篇——自定义View的各种姿势1

    该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列.该系列引用了<Android开发艺术探索>以及<深入理解And ...

  5. Android开发之漫漫长途 番外篇——自定义View的各种姿势2

    该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列.该系列引用了<Android开发艺术探索>以及<深入理解And ...

  6. Android简易实战教程--第二十七话《自定义View入门案例之开关按钮详细分析》

    转载此博客请注明出处点击打开链接       http://blog.csdn.net/qq_32059827/article/details/52444145 对于自定义view,可能是一个比较大的 ...

  7. Android 麦克风录音带音量大小动态显示的圆形自定义View

    1.所谓无图无真相,先上效果图.我们要实现的就是中间那个录音的按钮,周边会显示一圈音量大小的波形 2.VolumCircleBar继承自View,我们进行了自定义,代码如下 package com.r ...

  8. Android零基础入门第52节:自定义酷炫进度条

    原文:Android零基础入门第52节:自定义酷炫进度条 Android系统默认的ProgressBar往往都不能满足实际开发需要,一般都会开发者自定义ProgressBar. 在Android开发中 ...

  9. 【朝花夕拾】Android自定义View篇之(八)多点触控(上)MotionEvent简介

    前言 在前面的文章中,介绍了不少触摸相关的知识,但都是基于单点触控的,即一次只用一根手指.但是在实际使用App中,常常是多根手指同时操作,这就需要用到多点触控相关的知识了.多点触控是在Android2 ...

随机推荐

  1. 移动端h5直播项目|html5直播实战开发|h5仿陌陌

    最近一些直播.小视频什么的都比较火,像陌陌.抖音.火山短视频… 于是空闲时间自己也利用html5技术也试着倒腾了下直播项目,使用到了h5+css3+iscroll+zepot+swiper+wlsPo ...

  2. css之px自动转rem—sublime 插件CSSREM

    CSSREM CSSREM 是一个CSS的 px 值转 rem 值的Sublime Text3自动完成插件.先来看看插件的效果: 一个CSS的px值转rem值的Sublime Text 3自动完成插件 ...

  3. Linux下面安装swoole

    需要安装php7 新建一个文件夹,作为存储swoole的文件夹 然后执行下面相对应的命令,这里是我执行的命令 新建文件夹 mkdir swoole 切入到文件夹中,进行下载安装包 wget http: ...

  4. 剑指offer五十一之构建乘积数组

    一.题目 给定一个数组A[0,1,...,n-1],请构建一个数组B[0,1,...,n-1],其中B中的元素B[i]=A[0]*A[1]*...*A[i-1]*A[i+1]*...*A[n-1].不 ...

  5. Android 开发工具类 04_KeyBoardUtils

    打开或关闭软键盘: 1.打卡软键盘: 2.关闭软键盘. import android.content.Context; import android.view.inputmethod.InputMet ...

  6. MySQL的Sleep进程占用大量连接解决方法

    第一部分为产生大量sleep进程的原理及对应解决方法第二部分为设置wait_timeout值,有效减少sleep进程 ========================================= ...

  7. 机器学习--聚类系列--DBSCAN算法

    DBSCAN算法 基本概念:(Density-Based Spatial Clustering of Applications with Noise) 核心对象:若某个点的密度达到算法设定的阈值则其为 ...

  8. 使用DataTrigger来代替Triggerr

    普通的Trigger监听鼠标移入的代码如下: <Trigger Property="IsMouseOver" Value="true">     & ...

  9. Core中使用Hangfire

    之前使用Quartz.Net,后来发现hangfire对Core的继承更加的好,而且自带管理后台,这就比前者好用太多了. 安装注册 安装 PM> Install-Package Hangfire ...

  10. JS的作用域和声明提前

    首先介绍下Javascript的函数作用域的概念,然后了解下什么是作用域和声明提前,最后通过一个例子剖析Javascript的作用域链. 1.变量的作用域 稍微有些编程背景的都知道,变量的作用域分为两 ...