Android 自定义圆形图表
<com...SignChartView
android:id="@+id/signChart"
android:layout_width="265dp"
android:layout_height="265dp"
attrview:centerTextColor="@color/colorWhite"
attrview:centerTextSize="@dimen/font40"
attrview:circleWidth="20dp"
attrview:dataTextColor="@color/colorWhite"
attrview:dataTextSize="@dimen/font14" />
package com.signin; import android.content.Context;
import android.content.res.TypedArray;
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; import java.util.Random; /**
* Created by xxxx on 2018/8/29.
*/ public class SignChartView extends View { /**
* 使用wrap_content时默认的尺寸
*/
private static final int DEFAULT_WIDTH = 220;
private static final int DEFAULT_HEIGHT = 220; /**
* 中心坐标
*/
private int centerX;
private int centerY; /**
* 半径
*/
private float radius; /**
* 弧形外接矩形
*/
private RectF rectF; /**
* 中间文本的大小
*/
private Rect centerTextBound = new Rect(); /**
* 数据文本的大小
*/
private Rect dataTextBound = new Rect(); /**
* 扇形画笔
*/
private Paint mArcPaint; /**
* 中心文本画笔
*/
private Paint centerTextPaint; /**
* 数据画笔
*/
private Paint dataPaint; /**
* 数据源数字数组
*/
private int[] numbers; /**
* 数据源名称数组
*/
private String[] names; /**
* 数据源总和
*/
private int sum; /**
* 颜色数组
*/
private int[] colors; private Random random = new Random(); //自定义属性 Start /**
* 中间字体大小
*/
private float centerTextSize = 80; /**
* 数据字体大小
*/
private float dataTextSize = 30; /**
* 中间字体颜色
*/
private int centerTextColor = Color.BLACK; /**
* 数据字体颜色
*/
private int dataTextColor = Color.BLACK; /**
* 圆圈的宽度
*/
private float circleWidth = 100; //自定义属性 End public SignChartView(Context context) {
super(context);
init();
} public SignChartView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
} public SignChartView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.PieView);
centerTextSize = typedArray.getDimension(R.styleable.PieView_centerTextSize, centerTextSize);
dataTextSize = typedArray.getDimension(R.styleable.PieView_dataTextSize, dataTextSize);
circleWidth = typedArray.getDimension(R.styleable.PieView_circleWidth, circleWidth);
centerTextColor = typedArray.getColor(R.styleable.PieView_centerTextColor, centerTextColor);
dataTextColor = typedArray.getColor(R.styleable.PieView_dataTextColor, dataTextColor);
typedArray.recycle();
init();
} /**
* 初始化
*/
private void init() {
mArcPaint = new Paint();
mArcPaint.setStrokeWidth(circleWidth);
mArcPaint.setAntiAlias(true);
mArcPaint.setStyle(Paint.Style.STROKE); centerTextPaint = new Paint();
centerTextPaint.setTextSize(centerTextSize);
centerTextPaint.setAntiAlias(true);
centerTextPaint.setTextAlign(Paint.Align.CENTER);
centerTextPaint.setColor(centerTextColor); dataPaint = new Paint();
dataPaint.setStrokeWidth(2);
dataPaint.setTextSize(dataTextSize);
dataPaint.setAntiAlias(true);
dataPaint.setColor(dataTextColor);
} @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int measureWidthSize = MeasureSpec.getSize(widthMeasureSpec);
int measureHeightSize = MeasureSpec.getSize(heightMeasureSpec);
int measureWidthMode = MeasureSpec.getMode(widthMeasureSpec);
int measureHeightMode = MeasureSpec.getMode(heightMeasureSpec);
if (measureWidthMode == MeasureSpec.AT_MOST
&& measureHeightMode == MeasureSpec.AT_MOST) {
setMeasuredDimension(DEFAULT_WIDTH, DEFAULT_HEIGHT);
} else if (measureWidthMode == MeasureSpec.AT_MOST) {
setMeasuredDimension(DEFAULT_WIDTH, measureHeightSize);
} else if (measureHeightMode == MeasureSpec.AT_MOST) {
setMeasuredDimension(measureWidthSize, DEFAULT_HEIGHT);
}
} @Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
centerX = getMeasuredWidth() / 2;
centerY = getMeasuredHeight() / 2;
//设置半径为宽高最小值的1/4
radius = Math.min(getMeasuredWidth(), getMeasuredHeight()) / 4;
//设置扇形外接矩形
rectF = new RectF(centerX - radius,
centerY - radius,
centerX + radius,
centerY + radius);
} @Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
calculateAndDraw(canvas);
} /**
* 计算比例并且绘制扇形和数据
*/
private void calculateAndDraw(Canvas canvas) {
if (numbers == null || numbers.length == 0) {
return;
}
//扇形开始度数
int startAngle = 0;
//所占百分比
float percent;
//所占度数
float angle;
for (int i = 0; i < numbers.length; i++) {
percent = numbers[i] / (float) sum;
//获取百分比在360中所占度数
if (i == numbers.length - 1) {//保证所有度数加起来等于360
angle = 360 - startAngle;
} else {
angle = (float) Math.ceil(percent * 360);
}
//绘制第i段扇形
drawArc(canvas, startAngle, angle, colors[i]);
startAngle += angle; //绘制数据
if (numbers[i] <= 0) {
continue;
}
//当前弧线中心点相对于纵轴的夹角度数,由于扇形的绘制是从三点钟方向开始,所以加90
float arcCenterDegree = 90 + startAngle - angle / 2;
drawData(canvas, arcCenterDegree, i, percent);
}
//绘制中心数字总和
canvas.drawText(sum + "", centerX, centerY + centerTextBound.height() / 2, centerTextPaint);
} /**
* 计算每段弧度的中心坐标
*
* @param degree 当前扇形中心度数
*/
private float[] calculatePosition(float degree) {
//由于Math.sin(double a)中参数a不是度数而是弧度,所以需要将度数转化为弧度
//而Math.toRadians(degree)的作用就是将度数转化为弧度
//sin 一二正,三四负 sin(180-a)=sin(a)
//扇形弧线中心点距离圆心的x坐标
float x = (float) (Math.sin(Math.toRadians(degree)) * radius);
//cos 一四正,二三负
//扇形弧线中心点距离圆心的y坐标
float y = (float) (Math.cos(Math.toRadians(degree)) * radius); //每段弧度的中心坐标(扇形弧线中心点相对于view的坐标)
float startX = centerX + x;
float startY = centerY - y; float[] position = new float[2];
position[0] = startX;
position[1] = startY;
return position;
} /**
* 绘制数据
*
* @param canvas 画布
* @param degree 第i段弧线中心点相对于纵轴的夹角度数
* @param i 第i段弧线
* @param percent 数据百分比
*/
private void drawData(Canvas canvas, float degree, int i, float percent) {
//弧度中心坐标
float startX = calculatePosition(degree)[0];
float startY = calculatePosition(degree)[1]; //获取名称文本大小
dataPaint.getTextBounds(names[i], 0, names[i].length(), dataTextBound);
// //绘制名称数据,20为纵坐标偏移量
// canvas.drawText(names[i],
// startX - dataTextBound.width() / 2,
// startY + dataTextBound.height() / 2 - 20,
// dataPaint);
//
//
// //拼接百分比并获取文本大小
// DecimalFormat df = new DecimalFormat("0.0");
// String percentString = df.format(percent * 100) + "%";
// dataPaint.getTextBounds(percentString, 0, percentString.length(), dataTextBound);
//
// //绘制百分比数据,20为纵坐标偏移量
// canvas.drawText(percentString,
// startX - dataTextBound.width() / 2,
// startY + dataTextBound.height() * 2 - 20,
// dataPaint);
} /**
* 绘制扇形
*
* @param canvas 画布
* @param startAngle 开始度数
* @param angle 扇形的度数
* @param color 颜色
*/
private void drawArc(Canvas canvas, float startAngle, float angle, int color) {
mArcPaint.setColor(color);
//-0.5和+0.5是为了让每个扇形之间没有间隙
canvas.drawArc(rectF, startAngle - 0.5f, angle + 0.5f, false, mArcPaint);
} /**
* 生成随机颜色
*/
private int randomColor() {
int red = random.nextInt(256);
int green = random.nextInt(256);
int blue = random.nextInt(256);
return Color.rgb(red, green, blue);
} /**
* 设置数据
*
* @param numbers 数字数组
* @param names 名称数组
*/
public void setData(int[] numbers, String[] names) {
sum = 0;
if (numbers == null || numbers.length == 0 || names == null || names.length == 0) {
return;
}
if (numbers.length != names.length) {
//名称个数与数字个数不相等
return;
}
this.numbers = numbers;
this.names = names;
colors = new int[numbers.length];
for (int i = 0; i < this.numbers.length; i++) {
//计算总和
sum += numbers[i];
//随机颜色
if(numbers.length == 3){
colors[0] = Color.rgb(43,218,97);
colors[1] = Color.rgb(43,218,204);
colors[2] = Color.rgb(228,91,73);
}else{
colors[i] = randomColor();
} }
//计算总和数字的宽高
centerTextPaint.getTextBounds(sum + "", 0, (sum + "").length(), centerTextBound);
invalidate();
}
}
Android 自定义圆形图表的更多相关文章
- android 自定义动画
android自定义动画注意是继承Animation,重写里面的initialize和applyTransformation,在initialize方法做一些初始化的工作,在applyTransfor ...
- Android自定义View 画弧形,文字,并增加动画效果
一个简单的Android自定义View的demo,画弧形,文字,开启一个多线程更新ui界面,在子线程更新ui是不允许的,但是View提供了方法,让我们来了解下吧. 1.封装一个抽象的View类 B ...
- Android自定义View4——统计图View
1.介绍 周末在逛慕课网的时候,看到了一张学习计划报告图,详细记录了自己一周的学习情况,天天都是0节课啊!正好在学习Android自定义View,于是就想着自己去写了一个,这里先给出一张慕课网的图,和 ...
- (转)[原] Android 自定义View 密码框 例子
遵从准则 暴露您view中所有影响可见外观的属性或者行为. 通过XML添加和设置样式 通过元素的属性来控制其外观和行为,支持和重要事件交流的事件监听器 详细步骤见:Android 自定义View步骤 ...
- Android 自定义View合集
自定义控件学习 https://github.com/GcsSloop/AndroidNote/tree/master/CustomView 小良自定义控件合集 https://github.com/ ...
- Android 自定义View (五)——实践
前言: 前面已经介绍了<Android 自定义 view(四)-- onMeasure 方法理解>,那么这次我们就来小实践下吧 任务: 公司现有两个任务需要我完成 (1)监测液化天然气液压 ...
- Android 自定义 view(四)—— onMeasure 方法理解
前言: 前面我们已经学过<Android 自定义 view(三)-- onDraw 方法理解>,那么接下我们还需要继续去理解自定义view里面的onMeasure 方法 推荐文章: htt ...
- Android 自定义 view(三)—— onDraw 方法理解
前言: 上一篇已经介绍了用自己定义的属性怎么简单定义一个view<Android 自定义view(二) -- attr 使用>,那么接下来我们继续深究自定义view,下一步将要去简单理解自 ...
- Android 自定义view(二) —— attr 使用
前言: attr 在前一篇文章<Android 自定义view -- attr理解>已经简单的进行了介绍和创建,那么这篇文章就来一步步说说attr的简单使用吧 自定义view简单实现步骤 ...
随机推荐
- python基础02—运算符与流程控制
运算符与流程控制 运算符 赋值运算 用'='表示,'='的左边只能是变量 算术运算 +.-.*:加.减.乘 /:除法运算,运算结果为浮点数 //:除法运算,运算结果为整数(商) %:求余 **:求幂 ...
- 使用Swagger生成简单接口文档
使用swagger通过简单的配置可以生成简单的接口文档: 依赖包: // Swagger2 compile 'io.springfox:springfox-swagger2:2.8.0' compil ...
- POJ 2983 M × N Puzzle
M × N Puzzle Time Limit: 4000MS Memory Limit: 131072K Total Submissions: 4860 Accepted: 1321 Des ...
- vuex的小疑问记录
actions和mutations的区别是Action提交的是Mutation,不能够直接修改state中的状态,而Mutations是可以直接修改state中状态的:Action是支持异步操作的,而 ...
- 2017西安区域赛A / UVALive - 8512 线段树维护线性基合并
题意:给定\(a[1...n]\),\(Q\)次询问求\(A[L...R]\)的异或组合再或上\(K\)的最大值 本题是2017的西安区域赛A题,了解线性基之后你会发现这根本就是套路题.. 只要用线段 ...
- 经常谈到Oracle的权限,你究竟知道多少
作者: 三十而立 时间:2009年10月28日 9:41:15 请尊重原创作品.转载请保持文章完整性,并以超链接形式注明原始作者"inthirties(三十而立)" 接着上面谈到的 ...
- 【记录】sqli-labs-master搭建
附上:链接:http://pan.baidu.com/s/1bpCRzl1 密码:ep48 下载完成后直接解压到phpstudy(该工具之前分享过,直接搜索下)的WWW目录下,启动phpstudy, ...
- kvm 虚拟网络命令操作
2018-11-06 ```使用brctl命令创建网桥br1```# brctl addbr br1``` 删除网桥br1```# brctl delbr br1``` 将eth0端口加入网桥br1 ...
- PIE SDK定向滤波
1. 算法功能简介 定向滤波又称为匹配滤波,是通过一定尺寸的方向模板对图像进行卷积计算,并以卷积值代替各像元点灰度值,强调的是某一些方向的地面形迹,例如水系.线性影像等. 方向模板是一个各元素大小按照 ...
- C++指针传递和引用传递的区别 (转载整理)
区别1:指针传递和引用传递是以不同的方式实现相同的效果,但是指针传递的本质还是值传递,只是传递的值是地址. 就拿 交换两个数的函数来举例: // 指针传递 void swap(int * val1, ...