1.简单实现饼状图效果
1.1、创建一个实体类存储数据(数值、颜色、开始角度、结束角度)
public class MySector {
private float value;
private String colour;
private float startAngle;
private float endAngle;
public MySector(Builder builder){
this.value = builder.value > 0 ? builder.value : 1; //防止分母为零
this.colour = builder.colour;
this.startAngle = builder.startAngle;
this.endAngle = builder.endAngle;
}
public float getValue() {
return value;
}
public void setValue(float value) {
this.value = value;
}
public String getColour() {
return colour;
}
public void setColour(String colour) {
this.colour = colour;
}
public float getStartAngle() {
return startAngle;
}
public void setStartAngle(float startAngle) {
this.startAngle = startAngle;
}
public float getEndAngle() {
return endAngle;
}
public void setEndAngle(float endAngle) {
this.endAngle = endAngle;
}
static class Builder{
private float value;
private String colour;
private float startAngle;
private float endAngle;
public MySector.Builder value(float value){
this.value = value;
return this;
}
public MySector.Builder colour(String colour){
this.colour = colour;
return this;
}
public MySector.Builder startAngle(float startAngle){
this.startAngle = startAngle;
return this;
}
public MySector.Builder endAngle(float endAngle){
this.endAngle = endAngle;
return this;
}
public MySector build(){
return new MySector(this);
}
}
}
1.2、自定义View画饼状图扇形
(1)创建init方法初始化数据和画笔
(2)创建setRadius方法设置饼状图大小
(3)创建setMySector方法设置数据更新界面
(4)重写onDraw方法
public class MyChart extends View {
private List<MySector> mySectors;
private Paint paint;
private float centerX;
private float centerY;
private float Radius;
public MyChart(Context context) {
super(context);
init();
}
public MyChart(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public MyChart(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
mySectors = new ArrayList<>();
paint = new Paint();
paint.setTextSize(DensityUtil.px2dip(getContext(), 150));
paint.setAntiAlias(true);
}
public void setRadius(float radius) {
this.Radius = radius;
}
public void setPieEntries(List<MySector> mySectors) {
this.mySectors = mySectors;
invalidate();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//计算数值总值
int total = 0;
for (int i = 0; i < mySectors.size(); i++) {
total += mySectors.get(i).getValue();
}
//圆心和半径
centerX = getPivotX();
centerY = getPivotY();
if (Radius == 0) {
Radius = (getWidth() > getHeight() ? getHeight() / 2 : getWidth() / 2);
}
//开始角度设置为0
float startC = 0;
for (int i = 0; i < mySectors.size(); i++) {
//计算当前扇形角度
float sweep = 360 * (mySectors.get(i).getValue() / total);
//设置当前扇形颜色
int mDayColor = Color.parseColor(mySectors.get(i).getColour());
paint.setColor(mDayColor);
//画扇形的方法
RectF rectF = new RectF(centerX - Radius, centerY - Radius, centerX + Radius, centerY + Radius);
canvas.drawArc(rectF, startC, sweep, true, paint);
//将每个扇形的起始角度和结束角度放入对应的对象
mySectors.get(i).setStartAngle(startC);
mySectors.get(i).setEndAngle(startC + sweep);
//将当前扇形的结束角度作为下一个扇形的开始角度
startC += sweep;
}
}
}
1.3、xml文件代码
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.chx.piechartdemo.MyChart
android:id="@+id/myChart"
android:layout_width="match_parent"
android:layout_height="300dp"
android:background="@android:color/white" />
</RelativeLayout>
1.4、Activity中使用代码
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myChart = (MyChart) findViewById(R.id.myChart);
myChart.setRadius(DensityUtil.px2dip(this, 800));
List<MySector> mySectorList = new ArrayList<>();
MySector.Builder builder = new MySector.Builder();
mySectorList.add(builder.value(1).colour("#FFA500").build());
mySectorList.add(builder.value(2).colour("#23BF70").build());
mySectorList.add(builder.value(3).colour("#7155EC").build());
mySectorList.add(builder.value(4).colour("#B09EFF").build());
mySectorList.add(builder.value(5).colour("#00A2D0").build());
mySectorList.add(builder.value(6).colour("#41DBCB").build());
myChart.setMySector(mySectorList);
}
2.四周带百分比显示的饼状图
2.1、修改onDraw中的代码(代码中有详细注解)
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//计算数值总值
int total = 0;
for (int i = 0; i < mySectors.size(); i++) {
total += mySectors.get(i).getValue();
}
//圆心和半径
centerX = getPivotX();
centerY = getPivotY();
if (Radius == 0) {
Radius = (getWidth() > getHeight() ? getHeight() / 2 : getWidth() / 2);
}
//开始角度设置为0
float startC = 0;
for (int i = 0; i < mySectors.size(); i++) {
//计算当前扇形角度
float sweep = 360 * (mySectors.get(i).getValue() / total);
//设置当前扇形颜色
int mDayColor = Color.parseColor(mySectors.get(i).getColour());
paint.setColor(mDayColor);
//画扇形的方法
RectF rectF = new RectF(centerX - Radius, centerY - Radius, centerX + Radius, centerY + Radius);
canvas.drawArc(rectF, startC, sweep, true, paint);
//下面是画扇形外围的短线和百分数值。
float arcCenterC = startC + sweep / 2; //当前扇形弧线的中间点和圆心的连线与起始角度的夹角
float arcCenterX = 0; //当前扇形弧线的中间点的坐标x以此点作为短线的起点
float arcCenterY = 0; //当前扇形弧线的中间点的坐标y
float arcCenterX2 = 0; //这两个点作为短线的结束点
float arcCenterY2 = 0;
//百分百数字的格式
DecimalFormat numberFormat = new DecimalFormat("00.00");
paint.setColor(Color.BLACK);
//利用三角函数来求出每个短线的起始点和结束点,并画出短线和百分比。
//具体的计算方法看下面图示介绍
if (arcCenterC >= 0 && arcCenterC < 90) {
arcCenterX = (float) (centerX + Radius * Math.cos(arcCenterC * Math.PI / 180));
arcCenterY = (float) (centerY + Radius * Math.sin(arcCenterC * Math.PI / 180));
arcCenterX2 = (float) (arcCenterX + DensityUtils.dp2px(getContext(), 10) * Math.cos(arcCenterC * Math.PI / 180));
arcCenterY2 = (float) (arcCenterY + DensityUtils.dp2px(getContext(), 10) * Math.sin(arcCenterC * Math.PI / 180));
canvas.drawLine(arcCenterX, arcCenterY, arcCenterX2, arcCenterY2, paint);
canvas.drawText(numberFormat.format(mySectors.get(i).getValue() / total * 100) + "%", arcCenterX2, arcCenterY2 + paint.getTextSize() / 2, paint);
} else if (arcCenterC >= 90 && arcCenterC < 180) {
arcCenterC = 180 - arcCenterC;
arcCenterX = (float) (centerX - Radius * Math.cos(arcCenterC * Math.PI / 180));
arcCenterY = (float) (centerY + Radius * Math.sin(arcCenterC * Math.PI / 180));
arcCenterX2 = (float) (arcCenterX - DensityUtils.dp2px(getContext(), 10) * Math.cos(arcCenterC * Math.PI / 180));
arcCenterY2 = (float) (arcCenterY + DensityUtils.dp2px(getContext(), 10) * Math.sin(arcCenterC * Math.PI / 180));
canvas.drawLine(arcCenterX, arcCenterY, arcCenterX2, arcCenterY2, paint);
canvas.drawText(numberFormat.format(mySectors.get(i).getValue() / total * 100) + "%", (float) (arcCenterX2 - paint.getTextSize() * 3.5), arcCenterY2 + paint.getTextSize() / 2, paint);
} else if (arcCenterC >= 180 && arcCenterC < 270) {
arcCenterC = 270 - arcCenterC;
arcCenterX = (float) (centerX - Radius * Math.sin(arcCenterC * Math.PI / 180));
arcCenterY = (float) (centerY - Radius * Math.cos(arcCenterC * Math.PI / 180));
arcCenterX2 = (float) (arcCenterX - DensityUtils.dp2px(getContext(), 10) * Math.sin(arcCenterC * Math.PI / 180));
arcCenterY2 = (float) (arcCenterY - DensityUtils.dp2px(getContext(), 10) * Math.cos(arcCenterC * Math.PI / 180));
canvas.drawLine(arcCenterX, arcCenterY, arcCenterX2, arcCenterY2, paint);
canvas.drawText(numberFormat.format(mySectors.get(i).getValue() / total * 100) + "%", (float) (arcCenterX2 - paint.getTextSize() * 3.5), arcCenterY2, paint);
} else if (arcCenterC >= 270 && arcCenterC < 360) {
arcCenterC = 360 - arcCenterC;
arcCenterX = (float) (centerX + Radius * Math.cos(arcCenterC * Math.PI / 180));
arcCenterY = (float) (centerY - Radius * Math.sin(arcCenterC * Math.PI / 180));
arcCenterX2 = (float) (arcCenterX + DensityUtils.dp2px(getContext(), 10) * Math.cos(arcCenterC * Math.PI / 180));
arcCenterY2 = (float) (arcCenterY - DensityUtils.dp2px(getContext(), 10) * Math.sin(arcCenterC * Math.PI / 180));
canvas.drawLine(arcCenterX, arcCenterY, arcCenterX2, arcCenterY2, paint);
canvas.drawText(numberFormat.format(mySectors.get(i).getValue() / total * 100) + "%", arcCenterX2, arcCenterY2, paint);
}
//将每个扇形的起始角度和结束角度放入对应的对象
mySectors.get(i).setStartAngle(startC);
mySectors.get(i).setEndAngle(startC + sweep);
//将当前扇形的结束角度作为下一个扇形的开始角度
startC += sweep;
}
}
3.带点击效果的饼状图(更新中…)