效果图:
1.attrs:
2.JAVA代码:
/**
* created by: Eroch
* time: 2020/3/15
* introduce: 自定义饼状图,xml最多可同时显示5个数据,如需直接java代码加数据,可自行修改代码
* 求圆形任意一点坐标:
* 圆心坐标(x0,y0)
* 半径:R
* 角度a0
* 圆上任意一点(x1,y1)
* 则:
* x1 = x0 + R * cos(a0 * π / 180 )
* y1 = y0 + R + sin(a0 * π / 180 )
*/
public class XPieView extends View {
private final static int LINE_LENGTH = 80;//直线线的长度
private final static int CORNER_LENGTH = 10;//折线的长度
private final static int TEXT_MARGIN = 5;//线的长度
private final static int TEXT_SIZE = 10;
//isPieWidth:android:layout_width决定的饼状图的大小还是饼状图+饼状图文字数据的大小
//true:layout_width决定饼状图+文字数据的大小
//false:layout_width决定饼状图的大小,文字数据不算在里面
private boolean isPieWidth;
private float[] progressS;//所有数据
private int[] colorS;//所有颜色
private Paint paint;//画笔
private float percent;//百分比
private float centreX,centreY;//饼状图的中心点(centreX,centreY)
private float r;//半径
public XPieView(Context context, AttributeSet attrs) {
super(context, attrs);
paint = new Paint();
paint.setDither(true);//抗抖动
paint.setAntiAlias(true);//抗锯齿
paint.setStrokeWidth(2);//画笔粗细
paint.setStyle(Paint.Style.FILL);//画笔样式为填充
paint.setStrokeCap(Paint.Cap.ROUND);//设置笔端样式为圆
paint.setTextSize(TEXT_SIZE);
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.XPieView);
if (array != null){
int color0 = array.getInt(R.styleable.XPieView_pie_color0, 0);
int color1 = array.getInt(R.styleable.XPieView_pie_color1, 0);
int color2 = array.getInt(R.styleable.XPieView_pie_color2, 0);
int color3 = array.getInt(R.styleable.XPieView_pie_color3, 0);
int color4 = array.getInt(R.styleable.XPieView_pie_color4, 0);
colorS = new int[]{color0, color1, color2, color3, color4};
float progress0 = array.getFloat(R.styleable.XPieView_pie_progress0, 0);
float progress1 = array.getFloat(R.styleable.XPieView_pie_progress1, 0);
float progress2 = array.getFloat(R.styleable.XPieView_pie_progress2, 0);
float progress3 = array.getFloat(R.styleable.XPieView_pie_progress3, 0);
float progress4 = array.getFloat(R.styleable.XPieView_pie_progress4, 0);
percent =360 / (progress0 + progress1 + progress2 + progress3 + progress4);
progressS = new float[]{progress0,progress1,progress2,progress3,progress4};
isPieWidth = array.getBoolean(R.styleable.XPieView_pie_layoutWidth_isPieWidth, false);
boolean isRank = array.getBoolean(R.styleable.XPieView_pie_isRank, false);//是否进行排序
if (isRank){
//冒泡(最大的在在前面)
for (int i = 0; i < progressS.length; i++) {
for (int j = 0; j < progressS.length - 1 - i; j++) {
if (progressS[j] < progressS[j + 1]){
float num = progressS[j+1];
int color = colorS[j + 1];
progressS[j + 1] = progressS[j];
colorS[j + 1] = colorS[j];
progressS[j] = num;
colorS[j] = color;
}
}
}
}
}
array.recycle();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//wrap_content:-2
//match_parent:-1
//android:layout_width="150dp":150*2
//android:layout_width="70dp":70*2
int width = getLayoutParams().width;
int height = getLayoutParams().height;
if (width >0 || height > 0 ){
int index = width > height ? width : height;
if (isPieWidth){
//width = 饼+文字数据宽度
centreX = centreY = index - (LINE_LENGTH*2)>>1;
r = index>>2;
setMeasuredDimension(index,index);
}else {
//width = 饼的宽度
centreX = centreY = (index>>1) + LINE_LENGTH;
r = index>>2;
setMeasuredDimension(index + LINE_LENGTH*2,index + LINE_LENGTH*2);
}
}else {
centreX = centreY = 200;
r = 100;
setMeasuredDimension(400,400);
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
paint.setColor(colorS[0]);
//计算最大文字的宽度
float textLength = (String.valueOf(progressS[0]).length() - 1 )/ 1.5f * TEXT_SIZE;
//为保证饼状图的美观,所以这统一X坐标
float referenceRightX = centreX + r + LINE_LENGTH - textLength;
float referenceLeftX = centreX - r - LINE_LENGTH + textLength;
float p = 0;
for (int i = 0; i < progressS.length; i++) {
if (progressS[i] == 0){
return;
}
if (i != 0) {
p += progressS[i - 1] * percent;
}
paint.setColor(colorS[i]);
canvas.drawArc(centreX-r,centreX-r,centreX+r,centreX+r,i == 0 ? 0 : p, progressS[i]*percent,true,paint);
float data = p + (percent * progressS[i])/2;
double a = Math.toRadians(data);//把数字90 转换成 90度
double sin = Math.sin(a);
double cos = Math.cos(a);
//饼的任意一点坐标
float v = (float) (centreX + r * cos);
float v1 = (float) (centreY + r * sin);
//折线坐标
float cornerX = (float) (centreX + (r+CORNER_LENGTH) * cos);
float cornerY = (float) (centreX + (r+CORNER_LENGTH) * sin);
canvas.drawLine(v,v1,cornerX,cornerY,paint);
//270°和90°分别是12点钟和6点钟,作为区别左右边的凭据
if (data <270 && data > 90){
//在左边
canvas.drawLine( cornerX,cornerY,referenceLeftX,cornerY,paint);
canvas.drawText(String.valueOf(progressS[i]),referenceLeftX -TEXT_MARGIN - textLength,cornerY+(TEXT_SIZE>>2),paint);
}else {
//在右边
canvas.drawLine(cornerX ,cornerY,referenceRightX,cornerY,paint);
canvas.drawText(String.valueOf(progressS[i]),referenceRightX + TEXT_MARGIN,cornerY+(TEXT_SIZE>>2),paint);
}
}
}
}
3.Xml里使用:
End...
这几天没任务,看了一下自定义View,哪里写法还可以改进的,欢迎留言!