运行效果:
关键效果描述:半径不同的扇形、扇形的弧度根据占比决定、有展示动画、有阴影、有指示线、有点击事件。
直接上代码:
不规则扇形饼图
package demo.hanli.hldemo.modules.custompie.widget;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.BlurMaskFilter;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
/**
* 不规则扇形饼图
*/
public class IrregularPieView extends View {
private int ANIM_DURATION = 1000;//动画时长,默认1秒
private float maxRadiusScale = 1.2f;//扇形最大缩放比例
private float minRadiusScale = 0.5f;//扇形最小缩放比例
private float unitRadiusScale = 0.1f;//扇形递增缩放比例
private int mHeight, mWidth;//宽高
private Paint mPaint;//扇形的画笔
private Paint mShadowPaint;//阴影的画笔
private Paint mTextPaint;//画文字的画笔
private int centerX, centerY;//中心坐标
private double total;//数据的总和
private double[] datas;//数据集
private int[] dataCells;//数据扇形缩放基数集
private String[] texts;//每个数据对应的文字集
private float currentPercent;//当前展示角度的百分比
private List<Region> regions = new ArrayList<>();//扇形区域
private Region centerRegion;//中心圆区域
private IrregularPieListener listener;//饼图监听
//颜色 默认的颜色
private int[] mColors = {
Color.parseColor("#FFC65B"), Color.parseColor("#FD5998"),
Color.parseColor("#8971FB"), Color.parseColor("#676974")
};
private int mTextSize;//文字大小
private int radius = 1000;//半径
public IrregularPieView(Context context) {
super(context);
}
public IrregularPieView(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);
//阴影
mShadowPaint = new Paint();
mShadowPaint.setAntiAlias(true);
mTextPaint = new Paint();
//设置绘制文字的字号大小
mTextPaint.setTextSize(mTextSize);
//当画笔样式为STROKE或FILL_OR_STROKE时,设置笔刷的粗细度
mTextPaint.setStrokeWidth(2);
//设置是否使用抗锯齿功能,会消耗较大资源,绘制图形速度会变慢。
mTextPaint.setAntiAlias(true);
//设置绘制的颜色,使用颜色值来表示,该颜色值包括透明度和RGB颜色。
mTextPaint.setColor(Color.BLACK);
}
@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();
drawShadow(canvas);
canvas.restore();
//画扇形
canvas.save();
drawCircle(canvas);
canvas.restore();
//线与文字
canvas.save();
drawLineAndText(canvas);
canvas.restore();
//画中心圆
canvas.save();
drawCenterCircle(canvas);
canvas.restore();
}
//画中心圆
private void drawCenterCircle(Canvas canvas) {
//设置画笔属性
mPaint.setColor(Color.WHITE);
mPaint.setStyle(Paint.Style.FILL);//画笔属性是实心圆
canvas.drawCircle(centerX, centerY, radius * 0.3f, mPaint);
if(currentPercent == 1){
centerRegion = new Region();
Path path = new Path();
path.addCircle(centerX, centerY, radius * 0.3f, Path.Direction.CW);
RectF r = new RectF();
path.computeBounds(r, true);
centerRegion.setPath(path, new Region((int)r.left, (int) r.top, (int) r.right,(int)r.bottom));
}
}
//画线与文字
private void drawLineAndText(Canvas canvas) {
int start = 0;
canvas.translate(centerX, centerY);//平移画布到中心
mPaint.setStrokeWidth(4);
for (int i = 0; i < datas.length; i++) {
String textLabel;
if(texts == null || texts.length <= i){
textLabel = "";
}else{
textLabel = texts[i];
}
if (i == datas.length - 1) {
drawLine(canvas, (int) (start*currentPercent), 360 - start*currentPercent, textLabel, mColors[i % mColors.length], i);
} else {
float angles = (float) ((datas[i] * 1.0f / total) * 360);
drawLine(canvas, (int) (start*currentPercent), angles*currentPercent, textLabel, 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);
mPaint.setStrokeWidth(2);
mTextPaint.setColor(color);
float stopX, stopY;
stopX = (float) ((radius + 20) * Math.cos((2 * start + angles) / 2 * Math.PI / 180));
stopY = (float) ((radius + 20) * Math.sin((2 * start + angles) / 2 * Math.PI / 180));
double scale = (0.5 + (dataCells[position] * unitRadiusScale)) > maxRadiusScale ?
maxRadiusScale : (0.5 + (dataCells[position] * unitRadiusScale));
canvas.drawLine((float) ((radius * scale) * Math.cos((2 * start + angles) / 2 * Math.PI / 180)),
(float) ((radius * scale) * Math.sin((2 * start + angles) / 2 * Math.PI / 180)),
stopX, stopY, mPaint);
//测量百分比大小
String percentage = new BigDecimal((datas[position] / total) * 100)
.setScale(0, BigDecimal.ROUND_HALF_UP) + "";
percentage = percentage.substring(0, percentage.length() > 4 ? 4 : percentage.length()) + "%";
//测量文字大小
Rect rect = new Rect();
String data = new BigDecimal(datas[position])
.setScale(0, BigDecimal.ROUND_HALF_UP) + "";
String textInfo = percentage + "(" + data + ")";
mTextPaint.getTextBounds(textInfo, 0, textInfo.length(), rect);
int w = rect.width();
int h = rect.height();
int offset = 10;//文字在横线的偏移量
//画横线
int dx;//判断横线是画在左边还是右边
int endX;
int lineW = (w + offset) > 110 ? (w + offset) : 110;//横线宽度
if (stopX > 0) {
endX = (int) (stopX + lineW);
} else {
endX = (int) (stopX - lineW);
}
//画横线
canvas.drawLine(stopX, stopY,
endX, stopY, mPaint
);
dx = (int) (endX - stopX);
//画横线顶端的小圈
mPaint.setStyle(Paint.Style.STROKE);
canvas.drawCircle(dx > 0 ? endX + 4 : (stopX - lineW) - 4, stopY, 4, mPaint);
mPaint.setStyle(Paint.Style.FILL);
//画文字
canvas.drawText(textInfo, 0, textInfo.length(), dx > 0 ? stopX + offset : stopX - w - offset, stopY - 10, mTextPaint);
}
//画扇形
private void drawCircle(Canvas canvas) {
RectF rect = null;
int start = 0;
regions.clear();
for (int i = 0; i < datas.length; i++) {
double scale = (0.5 + (dataCells[i] * unitRadiusScale)) > maxRadiusScale ?
maxRadiusScale : (0.5 + (dataCells[i] * unitRadiusScale));
rect = new RectF((float) (centerX - radius * scale), (float) (centerY - radius * scale),
(float) (centerX + radius * scale), (float) (centerY + radius * scale));
float angles = (float) ((datas[i] * 1.0f / total *currentPercent) * 360);
mPaint.setColor(mColors[i % mColors.length]);
canvas.drawArc(rect, start, angles, true, mPaint);
if(currentPercent == 1){
Region mRegion = new Region();
Path path = new Path();
path.moveTo(centerX, centerY);
double radian = Math.toRadians(start);
float x = (float) (centerX + Math.cos(radian)*angles);
float y = (float) (centerY + Math.sin(radian)*angles);
path.lineTo(x, y);
path.addArc(rect, start, angles);
path.lineTo(centerX, centerY);
RectF r = new RectF();
path.computeBounds(r, true);
mRegion.setPath(path, new Region((int)r.left, (int) r.top, (int) r.right,(int)r.bottom));
regions.add(mRegion);
}
start += angles;
}
}
//画阴影
private void drawShadow(Canvas canvas) {
RectF rect = null;
int start = 0;
for (int i = 0; i < datas.length; i++) {
double scale = (0.5 + (dataCells[i] * unitRadiusScale)) > maxRadiusScale ?
maxRadiusScale : (0.5 + (dataCells[i] * unitRadiusScale));
rect = new RectF((float) (centerX - radius * scale), (float) (centerY - radius * scale),
(float) (centerX + radius * scale), (float) (centerY + radius * scale));
RectF shadowRect = new RectF(rect.left + 5, rect.top + 5, rect.right + 5, rect.bottom + 5);
float angles = (float) ((datas[i] * 1.0f / total * currentPercent) * 360);
// 绘制阴影,首先将画笔设置颜色色
mPaint.setColor(Color.LTGRAY);
// 就是添加内外发光效果
mPaint.setMaskFilter(new BlurMaskFilter(10, BlurMaskFilter.Blur.SOLID));
// 绘制阴影在矩形框里面 左上角坐标 10,10,右下角坐标width,height
canvas.drawArc(shadowRect, start + 2, angles, true, mPaint);
start += angles;
}
}
//冒泡排序
public void bubbleSort(double[] arr) {
if (arr == null || arr.length <= 1) return;
for (int i = 0; i < arr.length; ++i) {
boolean flag = false;
for (int j = 0; j < arr.length - i - 1; ++j) {
if (arr[j] > arr[j + 1]) {
double temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
flag = true;
}
}
if (!flag) break;//没有数据交换,数组已经有序,退出排序
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
float x = event.getX();
float y = event.getY();
boolean isCenter = centerRegion.contains((int) x, (int) y);
int index = -1;
if(!isCenter && regions != null && regions.size() != 0){
for(int i=0; i<regions.size(); i++){
Region item = regions.get(i);
boolean isContains = item.contains((int) x, (int) y);
if(isContains){
index = i;
break;
}
}
}
if(index >= 0 && index < datas.length){
if(listener != null){
listener.irregularClick(index);
}
}
return true;
}
return super.onTouchEvent(event);
}
//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 setDatas(double[] datas) {
this.datas = datas;
double[] temps = null;
if (datas != null) {
temps = new double[datas.length];
dataCells = new int[datas.length];
for (int i = 0; i < datas.length; i++) {
temps[i] = datas[i];
}
bubbleSort(temps);
for (int i = 0; i < datas.length; i++) {
for (int j = 0; j < temps.length; j++) {
if (datas[i] == temps[j]) {
dataCells[i] = j;
break;
}
}
}
}
total = 0;
for (int i = 0; i < datas.length; i++) {
total += datas[i];
}
if(total == 0) total = 1;
unitRadiusScale = (maxRadiusScale - minRadiusScale) / datas.length;
//属性动画
ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
animator.setDuration(ANIM_DURATION); //设置动画时间
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
currentPercent = (float) animation.getAnimatedValue(); //获取变动后的值
if(currentPercent == 1){//动画结束后,允许点击
setClickable(true);
}
invalidate(); //刷新布局
}
});
setClickable(false);
animator.start(); //启动动画
}
public void setTexts(String[] texts) {
this.texts = texts;
}
public void setIrregularPieListener(IrregularPieListener listener){
this.listener = listener;
}
public interface IrregularPieListener{
void irregularClick(int index);
}
}
使用方式:
第一步,构造布局文件:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/wide_divider">
<include
android:id="@+id/title"
layout="@layout/layout_main_title" />
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="none">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<demo.hanli.hldemo.modules.custompie.widget.IrregularPieView
android:id="@+id/irregularPieView"
android:layout_width="match_parent"
android:layout_height="300dp"
android:background="#ffffff"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="数值(多个数据则“,”隔开):"
android:textSize="@dimen/text_size_small"
android:textColor="@color/text_black"/>
<EditText
android:id="@+id/etData"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:hint="请输入数据"
android:text="16,18,33,33"
android:textColor="@color/text_black"
android:textSize="@dimen/text_size_small"/>
<TextView
android:id="@+id/tvWarning"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="数据格式不正确"
android:textSize="@dimen/text_size_small"
android:textColor="@color/red"/>
</LinearLayout>
<TextView
android:id="@+id/update"
android:layout_width="130dp"
android:layout_height="wrap_content"
android:paddingTop="10dp"
android:paddingBottom="10dp"
android:layout_marginTop="10dp"
android:gravity="center"
android:layout_gravity="center_horizontal"
android:background="@drawable/bg_round_purple"
android:text="刷新"
android:textSize="@dimen/text_size_small"
android:textColor="@color/white"/>
</LinearLayout>
</ScrollView>
</LinearLayout>
第二步,初始化控件:
//初始化图表
private void iniChart() {
int[] colors = {
Color.parseColor("#fbb132"), Color.parseColor("#b1d0ff"),
Color.parseColor("#fddd71"), Color.parseColor("#54a3ff"),
Color.parseColor("#32c5e9"), Color.parseColor("#9fe6b8"),
Color.parseColor("#b4d236"), Color.parseColor("#ff9f7f"),
Color.parseColor("#e7bcf3"), Color.parseColor("#fb7293")};
irregularPieView.setIrregularPieListener(new IrregularPieView.IrregularPieListener() {
@Override
public void irregularClick(int index) {
if (index < 0 || index >= datasList.size()) {
return;
}
Toast.makeText(this, "点击了第" + (index + 1) + "个扇形,扇形值为:" + datasList.get(index),
Toast.LENGTH_SHORT).show();
}
});
irregularPieView.setColors(colors);
irregularPieView.setTextSize(getResources().getDimensionPixelOffset(R.dimen.text_size_more_small));
}
第三步,获取网络数据后,添加进控件:
//此处获取EditText的输入内容,实际情况请改为网络数据
datasList.clear();
String dataStr = etData.getText().toString();
String[] dataArr = null;
if (dataStr != null && dataStr.trim().length() != 0) {
dataArr = dataStr.trim().split(",");
if (dataArr != null && dataArr.length != 0) {
for (int i = 0; i < dataArr.length; i++) {
double dataItem = -1;
try {
dataItem = Double.parseDouble(dataArr[i]);
datasList.add(dataItem);
} catch (Exception e) {
}
}
}
}
double[] datas = new double[datasList.size()];
for (int i = 0; i < datasList.size(); i++) {
datas[i] = datasList.get(i);
}
//数据设置到控件
irregularPieView.setDatas(datas);
注意:使用时请不要设置warp_content
圆柱图
package demo.hanli.hldemo.modules.custompie.widget;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.BlurMaskFilter;
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.math.BigDecimal;
/**
* 圆柱形柱状图
*/
public class CylinderBarView extends View {
private int ANIM_DURATION = 1000;//动画时长,默认1秒
private int maxNum = 4;//扇形图的最大块数,超出部分自动合并到最后一块上去
private int maxBarWidth;//柱体最大宽度
private int maxBarSpace;//柱体最大、最小间距
private int ovalH;//椭圆高度
private int topOffset;//柱体头部预留的文本高度
private int axisMarginH;//数轴的左、右边距
private int axisMarginV;//数轴的上、下边距
private int mHeight, mWidth;//宽高
private Paint mPaint;//扇形的画笔
private Paint mTextPaint;//画文字的画笔
private Paint mLinePaint;//画数轴的画笔
private Context context;
private double maxData;//最大数据
private double[] datas;//数据集
private int barWidth = maxBarWidth;//默认柱体宽度
private int barSpace;//柱体间隔
private float currentPercent;//当前展示数据的百分比
//颜色 默认柱体颜色
private int[] mColors = {
Color.parseColor("#FFC65B"), Color.parseColor("#FD5998"),
Color.parseColor("#8971FB"), Color.parseColor("#676974")
};
//颜色 默认的头部颜色
private int[] mTopColors = {
Color.parseColor("#f3ad2b"), Color.parseColor("#f5317c"),
Color.parseColor("#765afe"), Color.parseColor("#5c5d64")
};
//数轴颜色
private int mLineColor = Color.parseColor("#34374A");
private int mTextSize;//文字大小
public CylinderBarView(Context context) {
super(context);
this.context = context;
}
public CylinderBarView(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
init();
}
//初始化
private void init() {
mTextSize = 25;
maxBarWidth = dip2px(24);
ovalH = dip2px(9);
axisMarginV = dip2px(15);
axisMarginH = dip2px(15);
barSpace = dip2px(7);
maxBarSpace = dip2px(34);
topOffset = dip2px(30);
mPaint = new Paint();
//当画笔样式为STROKE或FILL_OR_STROKE时,设置笔刷的图形样式, 如圆形样Cap.ROUND,或方形样式Cap.SQUARE
mPaint.setStrokeCap(Paint.Cap.ROUND);
//设置是否使用抗锯齿功能,会消耗较大资源,绘制图形速度会变慢。
mPaint.setAntiAlias(true);
//数轴
mLinePaint = new Paint();
//当画笔样式为STROKE或FILL_OR_STROKE时,设置笔刷的图形样式, 如圆形样Cap.ROUND,或方形样式Cap.SQUARE
mLinePaint.setStrokeCap(Paint.Cap.ROUND);
mLinePaint.setStyle(Paint.Style.STROKE);
mLinePaint.setStrokeWidth(2);
mLinePaint.setColor(mLineColor);
//设置是否使用抗锯齿功能,会消耗较大资源,绘制图形速度会变慢。
mLinePaint.setAntiAlias(true);
//文字
mTextPaint = new Paint();
//设置绘制文字的字号大小
mTextPaint.setTextSize(mTextSize);
//当画笔样式为STROKE或FILL_OR_STROKE时,设置笔刷的粗细度
mTextPaint.setStrokeWidth(2);
//设置是否使用抗锯齿功能,会消耗较大资源,绘制图形速度会变慢。
mTextPaint.setAntiAlias(true);
//设置绘制的颜色,使用颜色值来表示,该颜色值包括透明度和RGB颜色。
mTextPaint.setColor(Color.BLACK);
}
@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;
barWidth = mWidth / (datas.length + 1) > maxBarWidth ? maxBarWidth : mWidth / (datas.length + 1);
if (datas.length > 1) {
barSpace = (mWidth - barWidth * (datas.length + 1)) / (datas.length) < 0 ? 0 : (mWidth - barWidth * (datas.length + 1)) / (datas.length);
barSpace = maxBarSpace < barSpace ? maxBarSpace : barSpace;
}
axisMarginH = (mWidth - barSpace * datas.length - barWidth * (datas.length + 1)) / 2;
//画数轴
canvas.save();
drawLine(canvas);
canvas.restore();
//画圆柱
canvas.save();
drawBar(canvas);
canvas.restore();
//文字
canvas.save();
drawText(canvas);
canvas.restore();
}
//画文字
private void drawText(Canvas canvas) {
for (int i = 0; i < (maxNum < datas.length ? maxNum : datas.length); i++) {
mTextPaint.setColor(mColors[i % mColors.length]);
//测量文字大小
Rect rect = new Rect();
String textInfo = new BigDecimal(datas[i])
.setScale(0, BigDecimal.ROUND_HALF_UP) + "";
mTextPaint.getTextBounds(textInfo, 0, textInfo.length(), rect);
int w = rect.width();
int h = rect.height();
int offset = dip2px(5);//文字在柱体头部的偏移量
//画文字
float top = (float) ((getBottom() - axisMarginV) - (datas[i] * 1.0f * currentPercent / maxData) * (mHeight - axisMarginV - topOffset) + ovalH / 2);
float bottom = getBottom() - axisMarginV - ovalH / 2;
float topT = top - ovalH / 2;
float bottomT = top + ovalH / 2;
float topB = bottom - ovalH / 2;
if (bottomT > (getBottom() - axisMarginV)) {
topT = topB;
}
float x = (float) (barWidth * (i + 1) + barSpace * (i)) + barWidth / 2 - w / 2 + axisMarginH;
float y = topT - offset;
canvas.drawText(textInfo, 0, textInfo.length(),
x, y,
mTextPaint);
}
}
//画圆柱
private void drawBar(Canvas canvas) {
RectF rect = null, rectOvalBottom, rectOvalTop;
for (int i = 0; i < (maxNum < datas.length ? maxNum : datas.length); i++) {
float left = (float) (barWidth * (i + 1) + barSpace * (i) + axisMarginH);
float top = (float) ((getBottom() - axisMarginV) - (datas[i] * 1.0f * currentPercent / maxData) * (mHeight - axisMarginV - topOffset) + ovalH / 2);
float right = (float) (barWidth * (i + 1) + barSpace * (i) + barWidth + axisMarginH);
float bottom = getBottom() - axisMarginV - ovalH / 2;
if(top > bottom) top = bottom;
//画矩形
rect = new RectF(left, top, right, bottom);
mPaint.setColor(mColors[i % mColors.length]);
canvas.drawRect(rect, mPaint);
//画底部椭圆
float topB = bottom - ovalH / 2;
float bottomB = getBottom() - axisMarginV;
rectOvalBottom = new RectF(left, topB, right, bottomB);
canvas.drawOval(rectOvalBottom, mPaint);
//画头部椭圆
mPaint.setColor(mTopColors[i % mTopColors.length]);
float topT = top - ovalH / 2;
float bottomT = top + ovalH / 2;
if (bottomT > (getBottom() - axisMarginV)) {
topT = topB;
bottomT = bottomB;
}
rectOvalTop = new RectF(left, topT, right, bottomT);
canvas.drawOval(rectOvalTop, mPaint);
}
}
//画数轴
private void drawLine(Canvas canvas) {
mLinePaint.setColor(mLineColor);
//横轴
canvas.drawLine(getLeft() + 10 + axisMarginH, getBottom() - axisMarginV, getRight() - axisMarginH, getBottom() - axisMarginV + 2, mLinePaint);
//竖轴
canvas.drawLine(getLeft() + barWidth / 2 + axisMarginH, getTop() + axisMarginV, getLeft() + barWidth / 2 + axisMarginH - 2, getBottom() - 10, mLinePaint);
}
/**
* dp转px
*/
public int dip2px(float dp) {
float density = context.getResources().getDisplayMetrics().density;
return (int) (density * dp + 0.5);
}
//柱体颜色
public void setColors(int[] mColors) {
this.mColors = mColors;
invalidate();
}
//柱头颜色
public void setTopColors(int[] mColors) {
this.mTopColors = mColors;
invalidate();
}
//数轴颜色
public void setLineColor(int mLineColor) {
this.mLineColor = mLineColor;
invalidate();
}
public void setTextSize(int mTextSize) {
this.mTextSize = mTextSize;
mTextPaint.setTextSize(mTextSize);
invalidate();
}
public void setMaxNum(int maxNum) {
this.maxNum = maxNum;
invalidate();
}
public void setDatas(double[] datas) {
this.datas = datas;
maxData = 0;
for (int i = 0; i < datas.length; i++) {
if (maxData < datas[i]) {
maxData = datas[i];
}
}
//属性动画
ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
animator.setDuration(ANIM_DURATION); //设置动画时间
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
currentPercent = (float) animation.getAnimatedValue(); //获取变动后的值
invalidate(); //刷新布局
}
});
animator.start(); //启动动画
}
}
圆柱图使用:
//初始化图表
private void iniChart(){
int[] colors = {
Color.parseColor("#fbb132"), Color.parseColor("#b1d0ff"),
Color.parseColor("#fddd71"), Color.parseColor("#54a3ff"),
Color.parseColor("#32c5e9"), Color.parseColor("#9fe6b8"),
Color.parseColor("#b4d236"), Color.parseColor("#ff9f7f"),
Color.parseColor("#e7bcf3"), Color.parseColor("#fb7293")};
int[] topColors = {
Color.parseColor("#f7a00e"), Color.parseColor("#75aafb"),
Color.parseColor("#fdce2f"), Color.parseColor("#3490fb"),
Color.parseColor("#14b9e1"), Color.parseColor("#6ae495"),
Color.parseColor("#98bc03"), Color.parseColor("#fe7b4f"),
Color.parseColor("#dc84f5"), Color.parseColor("#f83867")};
cylinderBarView.setColors(colors);
cylinderBarView.setTopColors(topColors);
cylinderBarView.setLineColor(Color.parseColor("#68c8c7"));
cylinderBarView.setTextSize(getResources().getDimensionPixelOffset(R.dimen.text_size_more_small));
}
//刷新数据
private void updateChart(){
datasList.clear();
tvWarning.setVisibility(View.GONE);
String dataStr = etData.getText().toString();
String[] dataArr = null;
if(dataStr != null && dataStr.trim().length() != 0){
dataArr = dataStr.trim().split(",");
if(dataArr != null && dataArr.length != 0){
for(int i=0; i<dataArr.length; i++){
double dataItem = -1;
try {
dataItem = Double.parseDouble(dataArr[i]);
datasList.add(dataItem);
}catch (Exception e){
tvWarning.setVisibility(View.VISIBLE);
return;
}
}
}
}
double[] datas = new double[datasList.size()];
for(int i=0; i<datasList.size(); i++){
datas[i] = datasList.get(i);
}
cylinderBarView.setMaxNum(datas.length);
cylinderBarView.setDatas(datas);
}