前言: Android开发中,原生的控件只能满足大多时候的需求,对于一些特殊的样式需要自定义控件去实现,接下来实现一个环形的图标,需要用到的关键类
Paint
,RectF
,Matrix
,Path
1.圆环
可以看到圆环有不通过的扇形区域,我们可以理解画两个圆弧然后首尾连接就可以形成该形状
Path path = new Path();
path.addArc(mMaxRect, startAngle, allData.get(i).getAngle());
path.arcTo(mMinRect, startAngle + allData.get(i).getAngle(), -allData.get(i).getAngle());
path.close();
2.画线
调用drawLine()
方法
canvas.drawLine(startX, startY, endX, endY, mPaint);
3.画文字
换文字要注意基类线的位置
mPaint.setTextAlign(Paint.Align.LEFT);
canvas.drawText(tmpText, endX + defaultS, endY + mTmpRect.height() / 2, mPaint);
所有代码
/**
* Description:环形饼状图
* Created by Buuu on 2018/04/27.
*/
public class CirclePieChart extends View {
private List allData;
private Paint mPaint;
private RectF mMinRect;
private RectF mMaxRect;
private Matrix mMatrix;
private List mPathsList;
private int mWH;
private int mMaxWH;
private int defaultSpace = 250;//周围宽度
private float startAngle = 0f; //初始角度
private float lineLength = 50; //指示线的长度
public CirclePieChart(Context context) {
this(context, null);
}
public CirclePieChart(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public CirclePieChart(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initData();
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mMatrix.reset();
if (allData != null && !allData.isEmpty()) {
mMaxWH = Math.min(w, h);
mWH = Math.min(w, h) - defaultSpace;
//通过调整内圈或外圈的大小 调整宽度
mMaxRect = new RectF(-mWH / 2, -mWH / 2, mWH / 2, mWH / 2);
mMinRect = new RectF(-mWH / 4, -mWH / 4, mWH / 4, mWH / 4);
//画环形图形,如需点击事件,根据需求添加
for (int i = 0; i < allData.size(); i++) {
Path path = new Path();
path.addArc(mMaxRect, startAngle, allData.get(i).getAngle());
path.arcTo(mMinRect, startAngle + allData.get(i).getAngle(), -allData.get(i).getAngle());
path.close();
startAngle+=allData.get(i).getAngle();
mPathsList.add(path);
}
}
}
private void initData() {
mPathsList = new ArrayList<>();
mPaint = new Paint();
mPaint.setTextSize(28);
mPaint.setColor(Color.parseColor("#3300f0ff"));
mPaint.setTextAlign(Paint.Align.LEFT);
mPaint.setAntiAlias(true);
mPaint.setStrokeWidth(3);
mMatrix = new Matrix();
}
private Rect mTmpRect = new Rect();
private float defaultS = 10F;
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.translate(mMaxWH / 2, mMaxWH / 2);
if (mMatrix.isIdentity()) {
canvas.getMatrix().invert(mMatrix);
}
for (int i = 0; i < mPathsList.size(); i++) {
mPaint.setColor(allData.get(i).getColor());
canvas.drawPath(mPathsList.get(i), mPaint);
}
float childStartAngle = startAngle;
//画线和字
for (int i = 0; i < allData.size(); i++) {
double cos = Math.cos(Math.toRadians(childStartAngle + (allData.get(i).getAngle() ) / 2));
double sin = Math.sin(Math.toRadians(childStartAngle + (allData.get(i).getAngle() ) / 2));
float startX = (float) (cos * mWH) / 2;
float endX = (float) (lineLength * cos + startX);
float startY = (float) (sin * mWH) / 2;
float endY = (float) (lineLength * sin + startY);
mPaint.setColor(allData.get(i).getColor());
canvas.drawLine(startX, startY, endX, endY, mPaint);
String tmpText = (int) Math.ceil(allData.get(i).getAngle()*100/360) +"%" ;
mPaint.getTextBounds(tmpText, 0, tmpText.length(), mTmpRect);
if (endY == 0 && endX > 0) {// x轴 正方向
mPaint.setTextAlign(Paint.Align.LEFT);
canvas.drawText(tmpText, endX + defaultS, endY + mTmpRect.height() / 2, mPaint);
} else if (endX > 0 && endY > 0 && endX >= endY) {//x轴 正方向 0
mPaint.setTextAlign(Paint.Align.LEFT);
canvas.drawText(tmpText, endX + defaultS, endY + mTmpRect.height() / 2, mPaint);
} else if (endX > 0 && endY > 0 && endX < endY) {//x轴 正方向 45
mPaint.setTextAlign(Paint.Align.CENTER);
canvas.drawText(tmpText, endX, endY + mTmpRect.height() + defaultS, mPaint);
} else if (endX == 0 && endY > 0) { //y轴正方向
mPaint.setTextAlign(Paint.Align.CENTER);
canvas.drawText(tmpText, endX, endY + mTmpRect.height() + defaultS, mPaint);
} else if (endX < 0 && endY > 0 && Math.abs(endX) <= Math.abs(endY)) {//y轴正方向 90
mPaint.setTextAlign(Paint.Align.CENTER);
canvas.drawText(tmpText, endX, endY + mTmpRect.height() + defaultS, mPaint);
} else if (endX < 0 && endY > 0 && Math.abs(endX) > Math.abs(endY)) {//y轴正方向 135
mPaint.setTextAlign(Paint.Align.RIGHT);
canvas.drawText(tmpText, endX - defaultS, endY + mTmpRect.height() / 2, mPaint);
} else if (endX < 0 && endY == 0) {//x轴负反向
mPaint.setTextAlign(Paint.Align.RIGHT);
canvas.drawText(tmpText, endX - defaultS, endY + mTmpRect.height() / 2, mPaint);
} else if (endX < 0 && endY < 0 && Math.abs(endX) >= Math.abs(endY)) {//x轴负反向 180
mPaint.setTextAlign(Paint.Align.RIGHT);
canvas.drawText(tmpText, endX - defaultS, endY + mTmpRect.height() / 2, mPaint);
} else if (endX < 0 && endY < 0 && Math.abs(endX) < Math.abs(endY)) {//x轴负反向 225
mPaint.setTextAlign(Paint.Align.CENTER);
canvas.drawText(tmpText, endX, endY - defaultS, mPaint);
} else if (endX == 0 && endY < 0) {//y轴负方向
mPaint.setTextAlign(Paint.Align.CENTER);
canvas.drawText(tmpText, endX, endY - defaultS, mPaint);
} else if (endX > 0 && endY < 0 && Math.abs(endX) <= Math.abs(endY)) {//y轴负方向 270
mPaint.setTextAlign(Paint.Align.CENTER);
canvas.drawText(tmpText, endX, endY - defaultS, mPaint);
} else if (endX > 0 && endY < 0 && Math.abs(endX) > Math.abs(endY)) {//y轴负方向 315
mPaint.setTextAlign(Paint.Align.LEFT);
canvas.drawText(tmpText, endX + defaultS, endY + mTmpRect.height() / 2, mPaint);
} else {//其他
mPaint.setTextAlign(Paint.Align.CENTER);
canvas.drawText(tmpText, endX + defaultS, endY + defaultS, mPaint);
}
childStartAngle += allData.get(i).getAngle();
}
}
/**
* 设置数据
*
* @param allDatas PieChartPart
*/
public void setData(List allDatas) {
this.allData = allDatas;
int numericalSum = 0;
int tempAngle;
int standardAngle = 360;
for (int i = 0; i < allData.size(); i++) {
numericalSum+=allData.get(i).getNumerical();
}
for (int i = 0; i < allData.size(); i++) {
tempAngle = (int) (allData.get(i).getNumerical() * 360 / numericalSum);
if(i>1){
tempAngle +=1;
}
if (allData.size()-1 == i){
tempAngle = standardAngle;
}else {
standardAngle -= tempAngle;
}
allData.get(i).setAngle(tempAngle);
}
invalidate();
}
}
//PieChartPart
public class PieChartPart {
private int color;
private float angle;
private float numerical;
public PieChartPart() {
}
public PieChartPart(int color, float numerical) {
this.color = color;
this.numerical = numerical;
}
public PieChartPart(int color, float numerical,float angle) {
this.color = color;
this.numerical = numerical;
this.angle = angle;
}
public int getColor() {
return color;
}
public void setColor(int color) {
this.color = color;
}
public float getAngle() {
return angle;
}
public void setAngle(float angle) {
this.angle = angle;
}
public float getNumerical() {
return numerical;
}
public void setNumerical(int numerical) {
this.numerical = numerical;
}
}
主要用到Path类,为更好的理解可以着重看一下该类,哦,对了,如果涉及到点击事件的话可以采用region类