之前的文章已经介绍了怎么绘制饼图和曲线图,今天这里介绍另一种常用的统计图——柱状图。
依旧是看一下效果图:
可以看到,图中有两幅柱状图,这里简单说明一下:第一幅是单柱状图,是根据同一组数据绘制的,每个柱子间距相同,相邻柱子
用两种颜色区分,视觉效果好一点;第二幅是双柱状图,是根据两组数据绘制的,同一刻度两组数据之间间距相同,且用不同颜色
区分两组数据,刻度与刻度之间的间距是根据数据数量变化调整的。
下面是主要实现方法:
public class CustomBarChart extends View {
// 坐标单位
private String[] xLabel;
private String[] yLabel;
// 曲线数据
private List<int[]> dataList;
private List<Integer> colorList;
// 默认边距
private int margin = 20;
// 距离左边偏移量
private int marginX = 30;
// 原点坐标
private int xPoint;
private int yPoint;
// X,Y轴的单位长度
private int xScale;
private int yScale;
// 画笔
private Paint paintAxes;
private Paint paintCoordinate;
private Paint paintRectF;
private Paint paintValue;
public CustomBarChart(Context context, String[] xLabel, String[] yLabel,
List<int[]> dataList, List<Integer> colorList) {
super(context);
this.xLabel = xLabel;
this.yLabel = yLabel;
this.dataList = dataList;
this.colorList = colorList;
}
public CustomBarChart(Context context) {
super(context);
}
/**
* 初始化数据值和画笔
*/
public void init() {
xPoint = margin + marginX;
yPoint = this.getHeight() - margin;
xScale = (this.getWidth() - 2 * margin - marginX) / (xLabel.length - 1);
yScale = (this.getHeight() - 2 * margin) / (yLabel.length - 1);
paintAxes = new Paint();
paintAxes.setStyle(Paint.Style.STROKE);
paintAxes.setAntiAlias(true);
paintAxes.setDither(true);
paintAxes.setColor(ContextCompat.getColor(getContext(), R.color.color11));
paintAxes.setStrokeWidth(4);
paintCoordinate = new Paint();
paintCoordinate.setStyle(Paint.Style.STROKE);
paintCoordinate.setDither(true);
paintCoordinate.setAntiAlias(true);
paintCoordinate.setColor(ContextCompat.getColor(getContext(), R.color.color11));
paintCoordinate.setTextSize(15);
paintRectF = new Paint();
paintRectF.setStyle(Paint.Style.FILL);
paintRectF.setDither(true);
paintRectF.setAntiAlias(true);
paintRectF.setStrokeWidth(1);
paintValue = new Paint();
paintValue.setStyle(Paint.Style.STROKE);
paintValue.setAntiAlias(true);
paintValue.setDither(true);
paintValue.setTextAlign(Paint.Align.CENTER);
paintValue.setTextSize(10);
}
@Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(ContextCompat.getColor(getContext(), R.color.color1));
init();
drawAxesLine(canvas, paintAxes);
drawCoordinate(canvas, paintCoordinate);
if (dataList.size() == 1) {
drawBar(canvas, paintRectF, dataList.get(0), colorList);
drawValue(canvas, paintValue, dataList.get(0), colorList.get(2));
} else if (dataList.size() == 2) {
drawBars(canvas, paintRectF, dataList, colorList);
drawValues(canvas, paintValue, dataList, colorList.get(2));
}
}
/**
* 绘制坐标轴
*/
private void drawAxesLine(Canvas canvas, Paint paint) {
// X
canvas.drawLine(xPoint, yPoint, this.getWidth() - margin / 6, yPoint, paint);
canvas.drawLine(this.getWidth() - margin / 6, yPoint, this.getWidth() - margin / 2, yPoint - margin / 3, paint);
canvas.drawLine(this.getWidth() - margin / 6, yPoint, this.getWidth() - margin / 2, yPoint + margin / 3, paint);
// Y
canvas.drawLine(xPoint, yPoint, xPoint, margin / 6, paint);
canvas.drawLine(xPoint, margin / 6, xPoint - margin / 3, margin / 2, paint);
canvas.drawLine(xPoint, margin / 6, xPoint + margin / 3, margin / 2, paint);
}
/**
* 绘制刻度
*/
private void drawCoordinate(Canvas canvas, Paint paint) {
// X轴坐标
for (int i = 0; i <= (xLabel.length - 1); i++) {
paint.setTextAlign(Paint.Align.CENTER);
int startX = xPoint + i * xScale;
canvas.drawText(xLabel[i], startX, this.getHeight() - margin / 6, paint);
}
// Y轴坐标
for (int i = 0; i <= (yLabel.length - 1); i++) {
paint.setTextAlign(Paint.Align.LEFT);
int startY = yPoint - i * yScale;
int offsetX;
switch (yLabel[i].length()) {
case 1:
offsetX = 28;
break;
case 2:
offsetX = 20;
break;
case 3:
offsetX = 12;
break;
case 4:
offsetX = 5;
break;
default:
offsetX = 0;
break;
}
int offsetY;
if (i == 0) {
offsetY = 0;
} else {
offsetY = margin / 5;
}
canvas.drawText(yLabel[i], margin / 4 + offsetX, startY + offsetY, paint);
}
}
/**
* 绘制单柱形
*/
private void drawBar(Canvas canvas, Paint paint, int data[], List<Integer> colorList) {
for (int i = 1; i <= (xLabel.length - 1); i++) {
int startX = xPoint + i * xScale;
RectF rect = new RectF(startX - 5, toY(data[i - 1]), startX + 5, this.getHeight() - margin - 2);
if (i % 2 == 1) {
paint.setColor(ContextCompat.getColor(getContext(), colorList.get(0)));
} else {
paint.setColor(ContextCompat.getColor(getContext(), colorList.get(1)));
}
canvas.drawRect(rect, paint);
}
}
/**
* 绘制双柱形
*/
private void drawBars(Canvas canvas, Paint paint, List<int[]> dataList, List<Integer> colorList) {
for (int i = 1; i <= (xLabel.length - 1); i++) {
int startX = xPoint + i * xScale;
paint.setColor(ContextCompat.getColor(getContext(), colorList.get(0)));
RectF rect1 = new RectF(startX - 20, toY(dataList.get(0)[i - 1]), startX - 10,
this.getHeight() - margin - 2);
canvas.drawRect(rect1, paint);
paint.setColor(ContextCompat.getColor(getContext(), colorList.get(1)));
RectF rect2 = new RectF(startX - 5, toY(dataList.get(1)[i - 1]), startX + 5,
this.getHeight() - margin - 2);
canvas.drawRect(rect2, paint);
}
}
/**
* 绘制单数值
*/
private void drawValue(Canvas canvas, Paint paint, int data[], int color) {
paint.setColor(ContextCompat.getColor(getContext(), color));
for (int i = 1; i <= (xLabel.length - 1); i++) {
canvas.drawText(data[i - 1] + "w", xPoint + i * xScale, toY(data[i - 1]) - 5, paintValue);
}
}
/**
* 绘制双数值
*/
private void drawValues(Canvas canvas, Paint paint, List<int[]> dataList, int color) {
paint.setColor(ContextCompat.getColor(getContext(), color));
for (int i = 1; i <= (xLabel.length - 1); i++) {
int startX = xPoint + i * xScale;
int offsetY1 = 5;
int offsetY2 = 5;
if (dataList.get(0)[i - 1] == dataList.get(1)[i - 1]) {
offsetY2 += 10;
}
if (i > 1) {
if ((dataList.get(1)[i - 2] == dataList.get(0)[i - 1])) {
offsetY1 += 10;
}
}
canvas.drawText(dataList.get(0)[i - 1] + "w", startX - 18,
toY(dataList.get(0)[i - 1]) - offsetY1, paintValue);
canvas.drawText(dataList.get(1)[i - 1] + "w", startX + 3,
toY(dataList.get(1)[i - 1]) - offsetY2, paintValue);
}
}
/**
* 数据按比例转坐标
*/
private float toY(int num) {
float y;
try {
float a = (float) num / 100.0f;
y = yPoint - a * yScale;
} catch (Exception e) {
return 0;
}
return y;
}
}
这里对构造方法做一下简单说明:
public CustomBarChart(Context context, String[] xLabel, String[] yLabel,
List<int[]> dataList, List<Integer> colorList)
第一个参数不用多说了;
第二个参数是一个字符串数组,是x轴的坐标刻度名称,同时x轴也依据此数组长度划分刻度轴,所以如果觉得x轴太密集了,这
里可以间隔传入空字符串代替原值,这样坐标轴刻度会间隔绘制显示;
第三个参数是一个字符串数组,是y轴的坐标刻度名称,同时y轴也依据此数组长度划分刻度轴。
第四个参数是一个整型数组的集合,集合最大长度只能为2,否则会出错,博主这里没做容错处理,所以尤其要注意。这个集合
代表了要绘制的柱状图的数值,传入的集合包含一个整型数组代表绘制单柱状图,包含两个整型数组代表绘制双柱状图。还要强调
一下的是,这里的两个整型数组长度相同,且比x轴坐标刻度数组长度少1,因为坐标刻度从0开始计算。
第五个参数是一个整型值的集合,集合长度只能为3,否则会出错,这里也没有做容错处理所以请注意。这个集合代表了柱状图
里面要用到的颜色的数值,具体定义在 res\values\colors.xml 文件中。这个集合里面的三个整型数值代表的颜色分别为第一个柱子颜
色,第二个柱子颜色,要绘制的数值颜色。在单柱状图中,如果不希望柱图颜色间隔绘制,可以传入两个相同的颜色数值作为集合
前两个元素。这里没有对控制数值的是否显示封装一个方法另做处理,可以简单的设置集合第三个参数为透明色,已达到不显示数
值的效果。
最后来看一下具体调用:
/**
* 初始化柱状图1数据
*/
private void initBarChart1() {
String[] xLabel = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13",
"14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27",
"28", "29", "30", "31"};
String[] yLabel = {"0", "100", "200", "300", "400", "500", "600", "700", "800", "900"};
int[] data1 = {300, 500, 550, 500, 300, 700, 800, 750, 550, 600, 400, 300, 400, 600, 500,
700, 300, 500, 550, 500, 300, 700, 800, 750, 550, 600, 400, 300, 400, 600, 500};
List<int[]> data = new ArrayList<>();
data.add(data1);
List<Integer> color = new ArrayList<>();
color.add(R.color.color12);
color.add(R.color.color13);
color.add(R.color.color16);
customBarChart1.addView(new CustomBarChart(this, xLabel, yLabel, data, color));
}
/**
* 初始化柱状图2数据
*/
private void initBarChart2() {
String[] xLabel = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12"};
String[] yLabel = {"0", "100", "200", "300", "400", "500", "600", "700", "800", "900"};
int[] data1 = {300, 500, 550, 500, 300, 700, 800, 750, 550, 600, 400, 300};
int[] data2 = {400, 600, 500, 700, 300, 500, 550, 500, 300, 700, 800, 750};
List<int[]> data = new ArrayList<>();
data.add(data1);
data.add(data2);
List<Integer> color = new ArrayList<>();
color.add(R.color.color14);
color.add(R.color.color15);
color.add(R.color.color11);
customBarChart2.addView(new CustomBarChart(this, xLabel, yLabel, data, color));
}
博主这里写的方法还比较粗糙,请不要介意,仅给需要的人提供一个思路,可以在此基础上修改完善。