package txkj.xian.com.newzjdemo.tubiao;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Paint.Align;
import android.graphics.Paint.Style;
import android.graphics.Path;
import android.graphics.PathMeasure;
import android.graphics.Point;
import android.graphics.RectF;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.LinearInterpolator;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* Created by AnOnYm on 2019/10/21.
*/
public class PieChartView extends View {
private Paint mPaint;
private Path mPath;
private Path drawLinePath = new Path();
private PathMeasure mPathMeasure = new PathMeasure();
private Canvas mCanvas;
private int width;
private int height;
private RectF pieRectF;
private RectF tempRectF;
private int radius;
private List<ItemType> itemTypeList;
private List<PieChartView.ItemType> leftTypeList;
private List<PieChartView.ItemType> rightTypeList;
private List<Point> itemPoints;
private int cell = 0;
private float innerRadius = 0.0F;
private float offRadius = 0.0F;
private float offLine;
private int textAlpha;
private Point firstPoint;
private int backGroundColor = -1;
private int itemTextSize = 30;
private int textPadding = 8;
private int defaultStartAngle = -90;
private float pieCell;
private ValueAnimator animator;
private long animDuration = 1000L;
private Point startPoint = new Point();
private Point centerPoint = new Point();
private Point endPoint = new Point();
private Point tempPoint = new Point();
public PieChartView(Context context) {
super(context);
this.init();
}
public PieChartView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
this.init();
}
private void init() {
this.mPaint = new Paint(5);
this.mPath = new Path();
this.pieRectF = new RectF();
this.tempRectF = new RectF();
this.itemTypeList = new ArrayList();
this.leftTypeList = new ArrayList();
this.rightTypeList = new ArrayList();
this.itemPoints = new ArrayList();
}
protected void onAttachedToWindow() {
super.onAttachedToWindow();
this.startAnim();
}
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
if(this.animator != null) {
this.animator.cancel();
}
}
private void startAnim() {
this.animator = ValueAnimator.ofFloat(new float[]{0.0F, 720.0F});
this.animator.setDuration(this.animDuration);
this.animator.setInterpolator(new LinearInterpolator());
this.animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
public void onAnimationUpdate(ValueAnimator animation) {
float value = ((Float)animation.getAnimatedValue()).floatValue();
if(value < 360.0F) {
PieChartView.this.offRadius = value;
PieChartView.this.offLine = 0.0F;
PieChartView.this.textAlpha = 0;
} else if(value >= 360.0F) {
PieChartView.this.offRadius = 360.0F;
PieChartView.this.offLine = (value - 360.0F) / 360.0F;
if(PieChartView.this.offLine > 0.5F) {
PieChartView.this.textAlpha = (int)(255.0F * ((PieChartView.this.offLine - 0.5F) / 0.5F));
} else {
PieChartView.this.textAlpha = 0;
}
} else if(value == 720.0F) {
PieChartView.this.offRadius = 360.0F;
PieChartView.this.offLine = 1.0F;
PieChartView.this.textAlpha = 255;
}
PieChartView.this.postInvalidate();
}
});
this.animator.start();
}
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
this.width = w;
this.height = h;
this.radius = Math.min(this.width, this.height) / 4;
this.pieRectF.set((float)(this.width / 2 - this.radius), (float)(this.height / 2 - this.radius), (float)(this.width / 2 + this.radius), (float)(this.height / 2 + this.radius));
}
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
try {
this.mCanvas = canvas;
this.drawPie();
if(this.offRadius == 360.0F) {
this.drawTitle();
}
} catch (Exception var3) {
var3.printStackTrace();
}
}
private void drawTitle() {
this.resetPaint();
float startRadius = (float)this.defaultStartAngle;
int count = this.rightTypeList.size();
int h;
if(count > 1) {
h = this.radius * 2 / (count - 1);
} else {
h = this.radius;
}
int i;
PieChartView.ItemType itemType;
double angle;
int x;
int y;
for(i = 0; i < count; ++i) {
this.mPath.reset();
itemType = (PieChartView.ItemType)this.rightTypeList.get(i);
angle = 6.283185307179586D * ((double)(startRadius + itemType.radius / 2.0F) / 360.0D);
x = (int)((double)(this.width / 2) + (double)this.radius * Math.cos(angle));
y = (int)((double)(this.height / 2) + (double)this.radius * Math.sin(angle));
this.startPoint.set(x, y);
this.centerPoint.set((int)((float)(this.width / 2) + (float)this.radius * 1.2F), this.height / 2 - this.radius + h * i);
this.endPoint.set((int)((float)this.width * 0.98F), this.centerPoint.y);
this.mPath.moveTo((float)this.startPoint.x, (float)this.startPoint.y);
this.mPath.lineTo((float)this.centerPoint.x, (float)this.centerPoint.y);
this.mPath.lineTo((float)this.endPoint.x, (float)this.endPoint.y);
this.resetPaint();
this.mPaint.setStrokeWidth(2.0F);
this.mPaint.setColor(itemType.color);
this.mPaint.setStyle(Style.STROKE);
this.mPathMeasure.setPath(this.mPath, false);
this.drawLinePath.reset();
this.mPathMeasure.getSegment(0.0F, this.mPathMeasure.getLength() * this.offLine, this.drawLinePath, true);
this.mCanvas.drawPath(this.drawLinePath, this.mPaint);
startRadius += itemType.radius;
if(this.textAlpha > 0) {
this.mPaint.setTextSize((float)this.itemTextSize);
this.mPaint.setStyle(Style.FILL);
this.mPaint.setTextAlign(Align.CENTER);
this.mPaint.setAlpha(this.textAlpha);
this.mCanvas.drawText(itemType.type, (float)(this.centerPoint.x + (this.endPoint.x - this.centerPoint.x) / 2), (float)(this.centerPoint.y - this.textPadding), this.mPaint);
this.mPaint.setTextSize((float)(this.itemTextSize * 4 / 5));
this.mCanvas.drawText(itemType.getPercent(), (float)(this.centerPoint.x + (this.endPoint.x - this.centerPoint.x) / 2), (float)(this.centerPoint.y + (this.itemTextSize + this.textPadding) * 4 / 5), this.mPaint);
}
}
count = this.leftTypeList.size();
if(count > 1) {
h = this.radius * 2 / (count - 1);
} else {
h = this.radius;
}
for(i = 0; i < count; ++i) {
this.mPath.reset();
itemType = (PieChartView.ItemType)this.leftTypeList.get(i);
angle = 6.283185307179586D * ((double)(startRadius + itemType.radius / 2.0F) / 360.0D);
x = (int)((double)(this.width / 2) + (double)this.radius * Math.cos(angle));
y = (int)((double)(this.height / 2) + (double)this.radius * Math.sin(angle));
this.startPoint.set(x, y);
this.centerPoint.set((int)((float)(this.width / 2) - (float)this.radius * 1.2F), this.height / 2 - this.radius + h * (count - 1 - i));
this.endPoint.set((int)((float)this.width * 0.02F), this.centerPoint.y);
this.mPath.moveTo((float)this.startPoint.x, (float)this.startPoint.y);
this.mPath.lineTo((float)this.centerPoint.x, (float)this.centerPoint.y);
this.mPath.lineTo((float)this.endPoint.x, (float)this.endPoint.y);
this.resetPaint();
this.mPaint.setStrokeWidth(2.0F);
this.mPaint.setColor(itemType.color);
this.mPaint.setAntiAlias(true);
this.mPaint.setDither(true);
this.mPaint.setStyle(Style.STROKE);
this.mPathMeasure.setPath(this.mPath, false);
this.drawLinePath.reset();
this.mPathMeasure.getSegment(0.0F, this.mPathMeasure.getLength() * this.offLine, this.drawLinePath, true);
this.mCanvas.drawPath(this.drawLinePath, this.mPaint);
startRadius += itemType.radius;
if(this.textAlpha > 0) {
this.mPaint.setTextSize((float)this.itemTextSize);
this.mPaint.setStyle(Style.FILL);
this.mPaint.setTextAlign(Align.CENTER);
this.mPaint.setAlpha(this.textAlpha);
this.mCanvas.drawText(itemType.type, (float)(this.centerPoint.x + (this.endPoint.x - this.centerPoint.x) / 2), (float)(this.centerPoint.y - this.textPadding), this.mPaint);
this.mPaint.setTextSize((float)(this.itemTextSize * 4 / 5));
this.mCanvas.drawText(itemType.getPercent(), (float)(this.centerPoint.x + (this.endPoint.x - this.centerPoint.x) / 2), (float)(this.centerPoint.y + (this.itemTextSize + this.textPadding) * 4 / 5), this.mPaint);
}
}
if((float)this.textAlpha == 1.0F) {
this.itemTypeList.clear();
this.leftTypeList.clear();
this.rightTypeList.clear();
this.itemPoints.clear();
}
}
private void drawPie() {
if(this.mCanvas != null) {
this.mCanvas.drawColor(this.backGroundColor);
this.mPaint.setStyle(Style.FILL);
int sum = 0;
PieChartView.ItemType startRadius;
for(Iterator a = this.itemTypeList.iterator(); a.hasNext(); sum += startRadius.widget) {
startRadius = (PieChartView.ItemType)a.next();
}
float a1 = 360.0F / (float)sum;
float startRadius1 = (float)this.defaultStartAngle;
float sumRadius = 0.0F;
this.leftTypeList.clear();
this.rightTypeList.clear();
this.itemPoints.clear();
Iterator var5 = this.itemTypeList.iterator();
while(var5.hasNext()) {
PieChartView.ItemType itemType = (PieChartView.ItemType)var5.next();
itemType.radius = (float)itemType.widget * a1;
double al = 6.283185307179586D * ((double)(startRadius1 + 90.0F) / 360.0D);
this.tempPoint.set((int)((double)(this.width / 2) + (double)this.radius * Math.sin(al)), (int)((double)(this.height / 2) - (double)this.radius * Math.cos(al)));
if(this.cell > 0 && startRadius1 == (float)this.defaultStartAngle) {
this.firstPoint = this.tempPoint;
}
double angle = 6.283185307179586D * ((double)(startRadius1 + itemType.radius / 2.0F) / 360.0D);
double sin = -Math.sin(angle);
double cos = -Math.cos(angle);
if(cos > 0.0D) {
this.leftTypeList.add(itemType);
} else {
this.rightTypeList.add(itemType);
}
sumRadius += Math.abs(itemType.radius);
this.mPaint.setStyle(Style.FILL);
this.mPaint.setColor(itemType.color);
if(this.pieCell > 0.0F) {
if(sumRadius > this.offRadius) {
this.mCanvas.drawArc(this.tempRectF, startRadius1, itemType.radius - Math.abs(this.offRadius - sumRadius), true, this.mPaint);
break;
}
this.tempRectF.set(this.pieRectF.left - (float)((double)this.pieCell * cos), this.pieRectF.top - (float)((double)this.pieCell * sin), this.pieRectF.right - (float)((double)this.pieCell * cos), this.pieRectF.bottom - (float)((double)this.pieCell * sin));
this.mCanvas.drawArc(this.tempRectF, startRadius1, itemType.radius, true, this.mPaint);
} else {
if(sumRadius > this.offRadius) {
this.mCanvas.drawArc(this.pieRectF, startRadius1, itemType.radius - Math.abs(this.offRadius - sumRadius), true, this.mPaint);
break;
}
this.mCanvas.drawArc(this.pieRectF, startRadius1, itemType.radius, true, this.mPaint);
}
startRadius1 += itemType.radius;
if(this.cell > 0 && this.pieCell == 0.0F) {
this.mPaint.setColor(this.backGroundColor);
this.mPaint.setStrokeWidth((float)this.cell);
this.mCanvas.drawLine((float)(this.getWidth() / 2), (float)(this.getHeight() / 2), (float)this.tempPoint.x, (float)this.tempPoint.y, this.mPaint);
}
}
if(this.cell > 0 && this.firstPoint != null && this.pieCell == 0.0F) {
this.mPaint.setColor(this.backGroundColor);
this.mPaint.setStrokeWidth((float)this.cell);
this.mCanvas.drawLine((float)(this.getWidth() / 2), (float)(this.getHeight() / 2), (float)this.firstPoint.x, (float)this.firstPoint.y, this.mPaint);
}
this.mPaint.setStyle(Style.FILL);
this.mPaint.setColor(this.backGroundColor);
if(this.innerRadius > 0.0F && this.pieCell == 0.0F) {
this.mCanvas.drawCircle((float)(this.width / 2), (float)(this.height / 2), (float)this.radius * this.innerRadius, this.mPaint);
}
}
}
public void resetPaint() {
this.mPaint.reset();
this.mPaint.setAntiAlias(true);
this.mPaint.setDither(true);
this.mPaint.setAlpha(256);
}
public void addItemType(PieChartView.ItemType itemType) {
if(this.itemTypeList != null) {
this.itemTypeList.add(itemType);
}
}
public void setCell(int cell) {
this.cell = cell;
}
public void setInnerRadius(float innerRadius) {
if(innerRadius > 1.0F) {
innerRadius = 1.0F;
} else if(innerRadius < 0.0F) {
innerRadius = 0.0F;
}
this.innerRadius = innerRadius;
}
public void setBackGroundColor(int backGroundColor) {
this.backGroundColor = backGroundColor;
}
public void setItemTextSize(int itemTextSize) {
this.itemTextSize = itemTextSize;
}
public void setTextPadding(int textPadding) {
this.textPadding = textPadding;
}
public void setAnimDuration(long animDuration) {
this.animDuration = animDuration;
}
/** @deprecated */
@Deprecated
public void setPieCell(int pieCell) {
this.cell = pieCell;
}
public static class ItemType {
private static final DecimalFormat df = new DecimalFormat("0.0%");
String type;
int widget;
int color;
float radius;
public ItemType(String type, int widget, int color) {
this.type = type;
this.widget = widget;
this.color = color;
}
public String getPercent() {
return df.format((double)(this.radius / 360.0F));
}
}
}
1,布局文件中定义
<txkj.xian.com.newzjdemo.tubiao.PieChartView
android:id="@+id/pie_chart_view"
android:layout_marginTop="20dp"
android:layout_width="200dp"
android:layout_height="200dp" />
2,Activity中设置:
public class ChartActivity extends BaseActivity {
@BindView(R.id.pie_chart_view)
PieChartView pieChartView;
@Override
protected int setLayout() {
return R.layout.activity_chart;
}
@Override
protected void initView() {
pieChartView.addItemType(new PieChartView.ItemType("苹果", 25, 0xff20B2AA));
pieChartView.addItemType(new PieChartView.ItemType("华为", 17, 0xff68228B));
pieChartView.addItemType(new PieChartView.ItemType("小米", 13, 0xff8B5A00));
pieChartView.addItemType(new PieChartView.ItemType("其他品牌", 45, 0xff999999));
pieChartView.setCell(2); //设置环形图的间距
pieChartView.setInnerRadius(0.7f); //设置环形图内环半径比例 0 - 1.0f
pieChartView.setBackGroundColor(0xffFFFFE0); //设置背景颜色
pieChartView.setItemTextSize(30); //设置字体大小
pieChartView.setTextPadding(10); //设置字体与横线距离
pieChartView.invalidate();
}
}
1,有些需求是需要在外圈内显示一些文本的,可借助相对布局或者帧布局在控件中心覆盖的方式即可。
2,如果不需要显示内圆的,设置 pieChartView.setInnerRadius(0f);
即可。
3,默认的显示是带动画效果的,如果不需要可以设置 pieChartView.setAnimDuration(0);
即可。
4,默认的设置文字大小的下面百分比的大小为文字大小的 itemTextSize * 4 / 5,如有特殊需求假如要修改百分比大小和文字大小一致,则修改自定义view中两处 itemTextSize * 4 / 5
为 itemTextSize 即可。